From 36b65ab59e5e514ad06a11cde96b87c565d86ac8 Mon Sep 17 00:00:00 2001 From: Mathieu Suen Date: Tue, 20 Jan 2026 15:15:57 +0100 Subject: [PATCH] Air: add "unwrap" functions for loading extra data --- src/Air.zig | 281 +++++++++++++++++++++++++++++--- src/Air/Liveness.zig | 91 +++++------ src/Air/Liveness/Verify.zig | 70 +++----- src/Air/print.zig | 138 ++++++---------- src/Air/types_resolved.zig | 52 +++--- src/Sema.zig | 12 +- src/codegen/aarch64/Select.zig | 198 ++++++++++------------ src/codegen/c.zig | 157 +++++++----------- src/codegen/llvm.zig | 147 +++++++---------- src/codegen/riscv64/CodeGen.zig | 120 ++++++-------- src/codegen/sparc64/CodeGen.zig | 102 +++++------- src/codegen/spirv/CodeGen.zig | 126 ++++++-------- src/codegen/wasm/CodeGen.zig | 60 +++---- src/codegen/x86_64/CodeGen.zig | 203 ++++++++++------------- 14 files changed, 864 insertions(+), 893 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index b5cb950d493278244cb5cc9e859d6a69c5aaf338..28b7a27ba992f375fffa3a96e7045f5de5952d1e 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -281,16 +281,21 @@ pub const Inst = struct { /// also supports enums and pointers. /// Uses the `ty_op` field. bitcast, - /// Uses the `ty_pl` field with payload `Block`. A block runs its body which always ends - /// with a `noreturn` instruction, so the only way to proceed to the code after the `block` - /// is to encounter a `br` that targets this `block`. If the `block` type is `noreturn`, + /// A block runs its body which always ends with a `noreturn` instruction, + /// so the only way to proceed to the code after the `block` is to encounter a `br` + /// that targets this `block`. If the `block` type is `noreturn`, /// then there do not exist any `br` instructions targeting this `block`. + /// Uses the `ty_pl` field with payload `Block`. + /// + /// See `unwrapBlock` for a way to load this tag's data. block, /// A labeled block of code that loops forever. The body must be `noreturn`: loops /// occur through an explicit `repeat` instruction pointing back to this one. /// Result type is always `noreturn`; no instructions in a block follow this one. /// There is always at least one `repeat` instruction referencing the loop. /// Uses the `ty_pl` field. Payload is `Block`. + /// + /// See `unwrapBlock` for a way to load this tag's data. loop, /// Sends control flow back to the beginning of a parent `loop` body. /// Uses the `repeat` field. @@ -319,6 +324,8 @@ pub const Inst = struct { /// Result type is the return type of the function being called. /// Uses the `pl_op` field with the `Call` payload. operand is the callee. /// Triggers `resolveTypeLayout` on the return type of the callee. + /// + /// See `unwrapCall` for a way to load this tag's data. call, /// Same as `call` except with the `always_tail` attribute. call_always_tail, @@ -436,14 +443,20 @@ pub const Inst = struct { /// Conditional branch. /// Result type is always noreturn; no instructions in a block follow this one. /// Uses the `pl_op` field. Operand is the condition. Payload is `CondBr`. + /// + /// See `unwrapCondBr` for a way to load this tags's data. cond_br, /// Switch branch. /// Result type is always noreturn; no instructions in a block follow this one. /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`. + /// + /// See `unwrapSwitch` for a way to load this tags's data. switch_br, /// Switch branch which can dispatch back to itself with a different operand. /// Result type is always noreturn; no instructions in a block follow this one. /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`. + /// + /// See `unwrapSwitch` for a way to load this tags's data. loop_switch_br, /// Dispatches back to a branch of a parent `loop_switch_br`. /// Result type is always noreturn; no instructions in a block follow this one. @@ -458,6 +471,8 @@ pub const Inst = struct { /// payload value, as if `unwrap_errunion_payload` was executed on the operand. /// The error branch is considered to have a branch hint of `.unlikely`. /// Uses the `pl_op` field. Payload is `Try`. + /// + /// See `unwrapTry` for a way to load this tag's data. @"try", /// Same as `try` except the error branch hint is `.cold`. try_cold, @@ -465,6 +480,8 @@ pub const Inst = struct { /// result is a pointer to the payload. Result is as if `unwrap_errunion_payload_ptr` /// was executed on the operand. /// Uses the `ty_pl` field. Payload is `TryPtr`. + /// + /// See `unwrapTryPtr` for a way to load this tag's data. try_ptr, /// Same as `try_ptr` except the error branch hint is `.cold`. try_ptr_cold, @@ -476,6 +493,8 @@ pub const Inst = struct { dbg_empty_stmt, /// A block that represents an inlined function call. /// Uses the `ty_pl` field. Payload is `DbgInlineBlock`. + /// + /// See `unwrapBlock` for a way to load this tag's data. dbg_inline_block, /// Marks the beginning of a local variable. The operand is a pointer pointing /// to the storage for the variable. The local may be a const or a var. @@ -715,7 +734,7 @@ pub const Inst = struct { /// Uses the `ty_pl` field, where the payload index points to: /// 1. mask_elem: ShuffleOneMask // for each `mask_len`, which comes from `ty_pl.ty` /// 2. operand: Ref // guaranteed not to be an interned value - /// See `unwrapShuffleOne`. + /// See `unwrapShuffleOne` for a way to load this tag's data. shuffle_one, /// Constructs a vector by selecting elements from two vectors based on a mask. Each mask /// element is either an index into one of the vectors, or "undef". @@ -723,7 +742,7 @@ pub const Inst = struct { /// 1. mask_elem: ShuffleOneMask // for each `mask_len`, which comes from `ty_pl.ty` /// 2. operand_a: Ref // guaranteed not to be an interned value /// 3. operand_b: Ref // guaranteed not to be an interned value - /// See `unwrapShuffleTwo`. + /// See `unwrapShuffleTwo` for a way to load this tag's data.. shuffle_two, /// Constructs a vector element-wise from `a` or `b` based on `pred`. /// Uses the `pl_op` field with `pred` as operand, and payload `Bin`. @@ -944,6 +963,8 @@ pub const Inst = struct { /// The calling convention is given by `func.@"callconv"(target)`. /// The return type (and hence the result type of this instruction) is `func.returnType()`. /// The parameter types are the types of the arguments given in `Air.Call`. + /// + /// See `unwrapCompilerRtCall` for a way to load this tag's data. legalize_compiler_rt_call, pub fn fromCmpOp(op: std.math.CompareOperator, optimized: bool) Tag { @@ -1445,18 +1466,18 @@ pub const ShuffleTwoMask = enum(u32) { /// 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. -/// - name: 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. -/// - name: memory at this position is reinterpreted as a null -/// terminated string. pad to the next u32 after the null byte. -/// 4. A number of u32 elements follow according to the equation `(source_len + 3) / 4`. +/// 2. 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. +/// 3. for every outputs_len +/// - constraint: memory at this position is reinterpreted as a null +/// terminated string. +/// - name: memory at this position is reinterpreted as a null +/// terminated string. pad to the next u32 after the null byte. +/// 4. for every inputs_len +/// - constraint: memory at this position is reinterpreted as a null +/// terminated string. +/// - name: memory at this position is reinterpreted as a null +/// terminated string. pad to the next u32 after the null byte. pub const Asm = struct { /// Length of the assembly source in bytes. source_len: u32, @@ -2157,11 +2178,229 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch { }; } -pub fn unwrapShuffleOne(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index) struct { +pub const UnwrappedDbgInlineBlock = struct { + func: InternPool.Index, + body: []const Inst.Index, + ty: Type, +}; + +pub fn unwrapDbgBlock(air: *const Air, inst_index: Inst.Index) UnwrappedDbgInlineBlock { + const data = air.instructions.items(.data)[@intFromEnum(inst_index)]; + const tag = air.instructions.items(.tag)[@intFromEnum(inst_index)]; + assert(tag == .dbg_inline_block); + const payload = data.ty_pl.payload; + const extra = air.extraData(Air.DbgInlineBlock, payload); + return .{ + .func = extra.data.func, + .ty = data.ty_pl.ty.toType(), + .body = @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]), + }; +} + +pub const UnwrappedBlock = struct { + body: []const Inst.Index, + ty: Type, +}; + +pub fn unwrapBlock(air: *const Air, inst_index: Inst.Index) UnwrappedBlock { + const data = air.instructions.items(.data)[@intFromEnum(inst_index)]; + const tag = air.instructions.items(.tag)[@intFromEnum(inst_index)]; + const payload = switch (tag) { + .block, .loop => data.ty_pl.payload, + else => unreachable, + }; + const extra = air.extraData(Air.Block, payload); + return .{ + .ty = data.ty_pl.ty.toType(), + .body = @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]), + }; +} + +pub const UnwrappedCall = struct { + callee: Inst.Ref, + args: []const Air.Inst.Ref, +}; + +pub fn unwrapCall(air: *const Air, inst_index: Inst.Index) UnwrappedCall { + const data = air.instructions.items(.data)[@intFromEnum(inst_index)]; + const tag = air.instructions.items(.tag)[@intFromEnum(inst_index)]; + const payload = switch (tag) { + .call, .call_always_tail, .call_never_tail, .call_never_inline => data.pl_op.payload, + else => unreachable, + }; + const extra = air.extraData(Air.Call, payload); + return .{ + .callee = data.pl_op.operand, + .args = @ptrCast(air.extra.items[extra.end..][0..extra.data.args_len]), + }; +} + +pub const UnwrappedCompilerRtCall = struct { + func: CompilerRtFunc, + args: []const Air.Inst.Ref, +}; + +pub fn unwrapCompilerRtCall(air: *const Air, inst_index: Inst.Index) UnwrappedCompilerRtCall { + const data = air.instructions.items(.data)[@intFromEnum(inst_index)]; + const tag = air.instructions.items(.tag)[@intFromEnum(inst_index)]; + assert(tag == .legalize_compiler_rt_call); + const payload = data.legalize_compiler_rt_call.payload; + const extra = air.extraData(Air.Call, payload); + return .{ + .func = data.legalize_compiler_rt_call.func, + .args = @ptrCast(air.extra.items[extra.end..][0..extra.data.args_len]), + }; +} + +pub const UnwrappedCondBr = struct { + condition: Inst.Ref, + then_body: []const Inst.Index, + else_body: []const Inst.Index, + branch_hints: CondBr.BranchHints, +}; + +pub fn unwrapCondBr(air: *const Air, inst_index: Inst.Index) UnwrappedCondBr { + const data = air.instructions.items(.data)[@intFromEnum(inst_index)]; + const tag = air.instructions.items(.tag)[@intFromEnum(inst_index)]; + assert(tag == .cond_br); + const payload = data.pl_op.payload; + const extra = air.extraData(Air.CondBr, payload); + return .{ + .condition = data.pl_op.operand, + .then_body = @ptrCast(air.extra.items[extra.end..][0..extra.data.then_body_len]), + .else_body = @ptrCast(air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len]), + .branch_hints = extra.data.branch_hints, + }; +} + +pub const UnwrappedTry = struct { + error_union: Inst.Ref, + else_body: []const Inst.Index, +}; + +pub fn unwrapTry(air: *const Air, inst_index: Inst.Index) UnwrappedTry { + const data = air.instructions.items(.data)[@intFromEnum(inst_index)]; + const tag = air.instructions.items(.tag)[@intFromEnum(inst_index)]; + assert(tag == .@"try" or tag == .try_cold); + const payload = data.pl_op.payload; + const extra = air.extraData(Air.Try, payload); + return .{ + .error_union = data.pl_op.operand, + .else_body = @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]), + }; +} + +pub const UnwrappedTryPtr = struct { + error_union_payload_ptr_ty: Inst.Ref, + error_union_ptr: Inst.Ref, + else_body: []const Inst.Index, +}; + +pub fn unwrapTryPtr(air: *const Air, inst_index: Inst.Index) UnwrappedTryPtr { + const data = air.instructions.items(.data)[@intFromEnum(inst_index)]; + const tag = air.instructions.items(.tag)[@intFromEnum(inst_index)]; + assert(tag == .try_ptr or tag == .try_ptr_cold); + const payload = data.ty_pl.payload; + const extra = air.extraData(Air.TryPtr, payload); + return .{ + .error_union_ptr = extra.data.ptr, + .error_union_payload_ptr_ty = data.ty_pl.ty, + .else_body = @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]), + }; +} + +pub const UnwrappedAsm = struct { + outputs: []const Air.Inst.Ref, + inputs: []const Air.Inst.Ref, + source: [:0]u8, + input_constraint_names: []const u32, + output_constraint_names: []const u32, + clobbers: InternPool.Index, + is_volatile: bool, + + const AsmIterator = struct { + current: u32, + operands: []const Air.Inst.Ref, + constraint_names: []const u32, + + pub fn next(self: *AsmIterator) ?struct { constraint: []const u8, operand: Inst.Ref, name: []const u8, index: u32 } { + if (self.current >= self.operands.len) { + return null; + } + defer { + self.current += 1; + } + + const constraint_name = std.mem.sliceAsBytes(self.constraint_names); + const constraint = std.mem.sliceTo(constraint_name, 0); + const name = std.mem.sliceTo(constraint_name[constraint.len + 1 ..], 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. + const next_offset = std.math.divCeil(usize, constraint.len + 1 + name.len + 1, @sizeOf(u32)) catch unreachable; + self.constraint_names = self.constraint_names[next_offset..]; + + return .{ + .constraint = constraint, + .operand = self.operands[self.current], + .name = name, + .index = self.current, + }; + } + }; + + pub fn iterateInputs(self: *const UnwrappedAsm) AsmIterator { + return .{ + .current = 0, + .operands = self.inputs, + .constraint_names = self.input_constraint_names, + }; + } + + pub fn iterateOutputs(self: *const UnwrappedAsm) AsmIterator { + return .{ + .current = 0, + .operands = self.outputs, + .constraint_names = self.output_constraint_names, + }; + } +}; + +pub fn unwrapAsm(air: *const Air, inst_index: Inst.Index) UnwrappedAsm { + const data = air.instructions.items(.data)[@intFromEnum(inst_index)]; + const tag = air.instructions.items(.tag)[@intFromEnum(inst_index)]; + assert(tag == .assembly); + const payload = data.ty_pl.payload; + const extra = air.extraData(Air.Asm, payload); + const source_start = extra.end + extra.data.flags.outputs_len + extra.data.inputs_len; + const output_constraint_name_start = source_start + (extra.data.source_len / 4) + 1; + const output_constraint_name = air.extra.items[output_constraint_name_start..]; + const outputs: []Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..extra.data.flags.outputs_len]); + // Get the input names and constraints offset place after the output. + var it = UnwrappedAsm.AsmIterator{ + .current = 0, + .constraint_names = output_constraint_name, + .operands = outputs, + }; + while (it.next()) |_| {} + + return .{ + .clobbers = extra.data.clobbers, + .is_volatile = extra.data.flags.is_volatile, + .inputs = @ptrCast(air.extra.items[extra.end + extra.data.flags.outputs_len ..][0..extra.data.inputs_len]), + .outputs = outputs, + .source = std.mem.sliceAsBytes(air.extra.items[source_start..])[0..extra.data.source_len :0], + .output_constraint_names = output_constraint_name, + .input_constraint_names = it.constraint_names, + }; +} + +pub const UnwrappedShuffleOne = struct { result_ty: Type, operand: Inst.Ref, mask: []const ShuffleOneMask, -} { +}; + +pub fn unwrapShuffleOne(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index) UnwrappedShuffleOne { const inst = air.instructions.get(@intFromEnum(inst_index)); switch (inst.tag) { .shuffle_one => {}, @@ -2177,12 +2416,14 @@ pub fn unwrapShuffleOne(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index }; } -pub fn unwrapShuffleTwo(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index) struct { +pub const UnwrappedShuffleTwo = struct { result_ty: Type, operand_a: Inst.Ref, operand_b: Inst.Ref, mask: []const ShuffleTwoMask, -} { +}; + +pub fn unwrapShuffleTwo(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index) UnwrappedShuffleTwo { const inst = air.instructions.get(@intFromEnum(inst_index)); switch (inst.tag) { .shuffle_two => {}, diff --git a/src/Air/Liveness.zig b/src/Air/Liveness.zig index 9b722973a66672d5b4f3d071f6dda99ee76ab084..a85944c4678455126d04403e1c48486db9d6e860 100644 --- a/src/Air/Liveness.zig +++ b/src/Air/Liveness.zig @@ -17,6 +17,7 @@ const trace = @import("../tracy.zig").trace; const Air = @import("../Air.zig"); const InternPool = @import("../InternPool.zig"); const Zcu = @import("../Zcu.zig"); +const Type = @import("../Type.zig"); pub const Verify = @import("Liveness/Verify.zig"); @@ -609,13 +610,11 @@ fn analyzeInst( }, .call, .call_always_tail, .call_never_tail, .call_never_inline => { - const inst_data = inst_datas[@intFromEnum(inst)].pl_op; - const callee = inst_data.operand; - const extra = a.air.extraData(Air.Call, inst_data.payload); - const args = @as([]const Air.Inst.Ref, @ptrCast(a.air.extra.items[extra.end..][0..extra.data.args_len])); + const call = a.air.unwrapCall(inst); + const args = call.args; if (args.len + 1 <= bpi - 1) { var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); - buf[0] = callee; + buf[0] = call.callee; @memcpy(buf[1..][0..args.len], args); return analyzeOperands(a, pass, data, inst, buf); } @@ -627,7 +626,7 @@ fn analyzeInst( i -= 1; try big.feed(args[i]); } - try big.feed(callee); + try big.feed(call.callee); return big.finish(); }, .select => { @@ -708,18 +707,15 @@ fn analyzeInst( .switch_dispatch => return analyzeInstSwitchDispatch(a, pass, data, inst), .assembly => { - const extra = a.air.extraData(Air.Asm, inst_datas[@intFromEnum(inst)].ty_pl.payload); - const outputs_len = extra.data.flags.outputs_len; - var extra_i: usize = extra.end; - const outputs = @as([]const Air.Inst.Ref, @ptrCast(a.air.extra.items[extra_i..][0..outputs_len])); - extra_i += outputs.len; - const inputs = @as([]const Air.Inst.Ref, @ptrCast(a.air.extra.items[extra_i..][0..extra.data.inputs_len])); - extra_i += inputs.len; + const unwrapped_asm = a.air.unwrapAsm(inst); + + const outputs = unwrapped_asm.outputs; + const inputs = unwrapped_asm.inputs; const num_operands = simple: { var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); var buf_index: usize = 0; - for (outputs) |output| { + for (unwrapped_asm.outputs) |output| { if (output != .none) { if (buf_index < buf.len) buf[buf_index] = output; buf_index += 1; @@ -748,15 +744,13 @@ fn analyzeInst( } return big.finish(); }, - - inline .block, .dbg_inline_block => |comptime_tag| { - const ty_pl = inst_datas[@intFromEnum(inst)].ty_pl; - const extra = a.air.extraData(switch (comptime_tag) { - .block => Air.Block, - .dbg_inline_block => Air.DbgInlineBlock, - else => unreachable, - }, ty_pl.payload); - return analyzeInstBlock(a, pass, data, inst, ty_pl.ty, @ptrCast(a.air.extra.items[extra.end..][0..extra.data.body_len])); + .dbg_inline_block => { + const block = a.air.unwrapDbgBlock(inst); + return analyzeInstBlock(a, pass, data, inst, block.ty, block.body); + }, + .block => { + const block = a.air.unwrapBlock(inst); + return analyzeInstBlock(a, pass, data, inst, block.ty, block.body); }, .loop => return analyzeInstLoop(a, pass, data, inst), @@ -778,8 +772,8 @@ fn analyzeInst( }, .legalize_compiler_rt_call => { - const extra = a.air.extraData(Air.Call, inst_datas[@intFromEnum(inst)].legalize_compiler_rt_call.payload); - const args: []const Air.Inst.Ref = @ptrCast(a.air.extra.items[extra.end..][0..extra.data.args_len]); + const rt_call = a.air.unwrapCompilerRtCall(inst); + const args = rt_call.args; if (args.len <= bpi - 1) { var buf: [bpi - 1]Air.Inst.Ref = @splat(.none); @memcpy(buf[0..args.len], args); @@ -972,7 +966,7 @@ fn analyzeInstBlock( comptime pass: LivenessPass, data: *LivenessPassData(pass), inst: Air.Inst.Index, - ty: Air.Inst.Ref, + ty: Type, body: []const Air.Inst.Index, ) !void { const gpa = a.gpa; @@ -1005,7 +999,7 @@ fn analyzeInstBlock( // If the block is noreturn, block deaths not only aren't useful, they're impossible to // find: there could be more stuff alive after the block than before it! - if (!a.intern_pool.isNoReturn(ty.toType().toIntern())) { + if (!a.intern_pool.isNoReturn(ty.toIntern())) { // The block kills the difference in the live sets const block_scope = data.block_scopes.get(inst).?; const num_deaths = data.live_set.count() - block_scope.live_set.count(); @@ -1139,9 +1133,8 @@ fn analyzeInstLoop( data: *LivenessPassData(pass), inst: Air.Inst.Index, ) !void { - const inst_datas = a.air.instructions.items(.data); - const extra = a.air.extraData(Air.Block, inst_datas[@intFromEnum(inst)].ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(a.air.extra.items[extra.end..][0..extra.data.body_len]); + const block = a.air.unwrapBlock(inst); + const body = block.body; const gpa = a.gpa; try analyzeOperands(a, pass, data, inst, .{ .none, .none, .none }); @@ -1187,44 +1180,38 @@ fn analyzeInstCondBr( inst: Air.Inst.Index, comptime inst_type: enum { cond_br, @"try", try_ptr }, ) !void { - const inst_datas = a.air.instructions.items(.data); const gpa = a.gpa; - const extra = switch (inst_type) { - .cond_br => a.air.extraData(Air.CondBr, inst_datas[@intFromEnum(inst)].pl_op.payload), - .@"try" => a.air.extraData(Air.Try, inst_datas[@intFromEnum(inst)].pl_op.payload), - .try_ptr => a.air.extraData(Air.TryPtr, inst_datas[@intFromEnum(inst)].ty_pl.payload), + const unwrapped_cond = switch (inst_type) { + .cond_br => a.air.unwrapCondBr(inst), + .@"try" => a.air.unwrapTry(inst), + .try_ptr => a.air.unwrapTryPtr(inst), }; const condition = switch (inst_type) { - .cond_br, .@"try" => inst_datas[@intFromEnum(inst)].pl_op.operand, - .try_ptr => extra.data.ptr, + .cond_br => unwrapped_cond.condition, + .@"try" => unwrapped_cond.error_union, + .try_ptr => unwrapped_cond.error_union_ptr, }; - const then_body: []const Air.Inst.Index = switch (inst_type) { - .cond_br => @ptrCast(a.air.extra.items[extra.end..][0..extra.data.then_body_len]), - else => &.{}, // we won't use this + const then_body = switch (inst_type) { + .cond_br => unwrapped_cond.then_body, + // The "then body" is just the remainder of this block + else => &.{}, }; - const else_body: []const Air.Inst.Index = @ptrCast(switch (inst_type) { - .cond_br => a.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len], - .@"try", .try_ptr => a.air.extra.items[extra.end..][0..extra.data.body_len], - }); + const else_body = switch (inst_type) { + .cond_br, .@"try", .try_ptr => unwrapped_cond.else_body, + }; switch (pass) { .loop_analysis => { - switch (inst_type) { - .cond_br => try analyzeBody(a, pass, data, then_body), - .@"try", .try_ptr => {}, - } + try analyzeBody(a, pass, data, then_body); try analyzeBody(a, pass, data, else_body); }, .main_analysis => { - switch (inst_type) { - .cond_br => try analyzeBody(a, pass, data, then_body), - .@"try", .try_ptr => {}, // The "then body" is just the remainder of this block - } + try analyzeBody(a, pass, data, then_body); var then_live = data.live_set.move(); defer then_live.deinit(gpa); diff --git a/src/Air/Liveness/Verify.zig b/src/Air/Liveness/Verify.zig index cdb5786921bb9fe005daa93479db0f9f2ce2c152..fc83574d070c018fc54ed095d4c67f0bf7698fb2 100644 --- a/src/Air/Liveness/Verify.zig +++ b/src/Air/Liveness/Verify.zig @@ -345,37 +345,26 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { try self.verifyInst(inst); }, .call, .call_always_tail, .call_never_tail, .call_never_inline => { - const pl_op = data[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @as( - []const Air.Inst.Ref, - @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]), - ); + const call = self.air.unwrapCall(inst); + const args = call.args; var bt = self.liveness.iterateBigTomb(inst); - try self.verifyOperand(inst, pl_op.operand, bt.feed()); + try self.verifyOperand(inst, call.callee, bt.feed()); for (args) |arg| { try self.verifyOperand(inst, arg, bt.feed()); } try self.verifyInst(inst); }, .assembly => { - const ty_pl = data[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const outputs_len = extra.data.flags.outputs_len; - var extra_i = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..outputs_len]); - extra_i += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); - extra_i += inputs.len; + const unwrapped_asm = self.air.unwrapAsm(inst); var bt = self.liveness.iterateBigTomb(inst); - for (outputs) |output| { + for (unwrapped_asm.outputs) |output| { if (output != .none) { try self.verifyOperand(inst, output, bt.feed()); } } - for (inputs) |input| { + for (unwrapped_asm.inputs) |input| { try self.verifyOperand(inst, input, bt.feed()); } try self.verifyInst(inst); @@ -383,13 +372,12 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { // control flow .@"try", .try_cold => { - const pl_op = data[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Try, pl_op.payload); - const try_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); + const unwrapped_try = self.air.unwrapTry(inst); + const try_body = unwrapped_try.else_body; const cond_br_liveness = self.liveness.getCondBr(inst); - try self.verifyOperand(inst, pl_op.operand, self.liveness.operandDies(inst, 0)); + try self.verifyOperand(inst, unwrapped_try.error_union, self.liveness.operandDies(inst, 0)); var live = try self.live.clone(self.gpa); defer live.deinit(self.gpa); @@ -405,13 +393,12 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { try self.verifyInst(inst); }, .try_ptr, .try_ptr_cold => { - const ty_pl = data[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); - const try_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); + const unwrapped_try = self.air.unwrapTryPtr(inst); + const try_body = unwrapped_try.else_body; const cond_br_liveness = self.liveness.getCondBr(inst); - try self.verifyOperand(inst, extra.data.ptr, self.liveness.operandDies(inst, 0)); + try self.verifyOperand(inst, unwrapped_try.error_union_ptr, self.liveness.operandDies(inst, 0)); var live = try self.live.clone(self.gpa); defer live.deinit(self.gpa); @@ -458,17 +445,11 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .block, .dbg_inline_block => |tag| { const ty_pl = data[@intFromEnum(inst)].ty_pl; const block_ty = ty_pl.ty.toType(); - const block_body: []const Air.Inst.Index = @ptrCast(switch (tag) { - inline .block, .dbg_inline_block => |comptime_tag| body: { - const extra = self.air.extraData(switch (comptime_tag) { - .block => Air.Block, - .dbg_inline_block => Air.DbgInlineBlock, - else => unreachable, - }, ty_pl.payload); - break :body self.air.extra.items[extra.end..][0..extra.data.body_len]; - }, + const block_body = switch (tag) { + .block => self.air.unwrapBlock(inst).body, + .dbg_inline_block => self.air.unwrapDbgBlock(inst).body, else => unreachable, - }); + }; const block_liveness = self.liveness.getBlock(inst); var orig_live = try self.live.clone(self.gpa); @@ -501,9 +482,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { try self.verifyInstOperands(inst, .{ .none, .none, .none }); }, .loop => { - const ty_pl = data[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Block, ty_pl.payload); - const loop_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); + const block = self.air.unwrapBlock(inst); // The same stuff should be alive after the loop as before it. const gop = try self.loops.getOrPut(self.gpa, inst); @@ -514,18 +493,17 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { } gop.value_ptr.* = try self.live.clone(self.gpa); - try self.verifyBody(loop_body); + try self.verifyBody(block.body); try self.verifyInstOperands(inst, .{ .none, .none, .none }); }, .cond_br => { - const pl_op = data[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const cond_br = self.air.unwrapCondBr(inst); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; const cond_br_liveness = self.liveness.getCondBr(inst); - try self.verifyOperand(inst, pl_op.operand, self.liveness.operandDies(inst, 0)); + try self.verifyOperand(inst, cond_br.condition, self.liveness.operandDies(inst, 0)); var live = try self.live.clone(self.gpa); defer live.deinit(self.gpa); @@ -589,8 +567,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { try self.verifyInstOperands(inst, .{ pl_op.operand, bin.lhs, bin.rhs }); }, .legalize_compiler_rt_call => { - const extra = self.air.extraData(Air.Call, data[@intFromEnum(inst)].legalize_compiler_rt_call.payload); - const args: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]); + const rt_call = self.air.unwrapCompilerRtCall(inst); + const args = rt_call.args; var bt = self.liveness.iterateBigTomb(inst); for (args) |arg| { try self.verifyOperand(inst, arg, bt.feed()); diff --git a/src/Air/print.zig b/src/Air/print.zig index 98b0a0b242ab7b7c41976c35f76756a5efbd7dec..0c126fab22d364b97c49ed6d896801fe4c66451e 100644 --- a/src/Air/print.zig +++ b/src/Air/print.zig @@ -395,25 +395,17 @@ const Writer = struct { fn writeBlock(w: *Writer, s: *std.Io.Writer, tag: Air.Inst.Tag, inst: Air.Inst.Index) Error!void { const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; try w.writeType(s, ty_pl.ty.toType()); - const body: []const Air.Inst.Index = @ptrCast(switch (tag) { - inline .block, .dbg_inline_block => |comptime_tag| body: { - const extra = w.air.extraData(switch (comptime_tag) { - .block => Air.Block, - .dbg_inline_block => Air.DbgInlineBlock, - else => unreachable, - }, ty_pl.payload); - switch (comptime_tag) { - .block => {}, - .dbg_inline_block => { - try s.writeAll(", "); - try w.writeInstRef(s, Air.internedToRef(extra.data.func), false); - }, - else => unreachable, - } - break :body w.air.extra.items[extra.end..][0..extra.data.body_len]; + + const body = switch (tag) { + .block => w.air.unwrapBlock(inst).body, + .dbg_inline_block => body: { + const dbg_block = w.air.unwrapDbgBlock(inst); + try s.writeAll(", "); + try w.writeInstRef(s, Air.internedToRef(dbg_block.func), false); + break :body dbg_block.body; }, else => unreachable, - }); + }; if (w.skip_body) return s.writeAll(", ..."); const liveness_block: Air.Liveness.BlockSlices = if (w.liveness) |liveness| liveness.getBlock(inst) @@ -434,16 +426,14 @@ const Writer = struct { } fn writeLoop(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = w.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]); + const block = w.air.unwrapBlock(inst); - try w.writeType(s, ty_pl.ty.toType()); + try w.writeType(s, block.ty); if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; - try w.writeBody(s, body); + try w.writeBody(s, block.body); w.indent = old_indent; try s.splatByteAll(' ', w.indent); try s.writeAll("}"); @@ -532,11 +522,10 @@ const Writer = struct { } fn writeLegalizeCompilerRtCall(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const inst_data = w.air.instructions.items(.data)[@intFromEnum(inst)].legalize_compiler_rt_call; - const extra = w.air.extraData(Air.Call, inst_data.payload); - const args: []const Air.Inst.Ref = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.args_len]); + const rt_call = w.air.unwrapCompilerRtCall(inst); + const args = rt_call.args; - try s.print("{t}, [", .{inst_data.func}); + try s.print("{t}, [", .{rt_call.func}); for (args, 0..) |arg, i| { if (i != 0) try s.writeAll(", "); try w.writeOperand(s, inst, i, arg); @@ -666,11 +655,8 @@ const Writer = struct { } fn writeAssembly(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = w.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = extra.data.flags.is_volatile; - const outputs_len = extra.data.flags.outputs_len; - var extra_i: usize = extra.end; + const unwrapped_asm = w.air.unwrapAsm(inst); + const is_volatile = unwrapped_asm.is_volatile; var op_index: usize = 0; const ret_ty = w.typeOfIndex(inst); @@ -680,49 +666,33 @@ const Writer = struct { try s.writeAll(", volatile"); } - const outputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..outputs_len])); - extra_i += outputs.len; - const inputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.inputs_len])); - extra_i += inputs.len; - - for (outputs) |output| { - const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the strings and their null terminators, we still use the next u32 - // for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (output == .none) { + var it = unwrapped_asm.iterateOutputs(); + while (it.next()) |out| { + const name = out.name; + const constraint = out.constraint; + if (out.operand == .none) { try s.print(", [{s}] -> {s}", .{ name, constraint }); } else { try s.print(", [{s}] out {s} = (", .{ name, constraint }); - try w.writeOperand(s, inst, op_index, output); + try w.writeOperand(s, inst, op_index, out.operand); op_index += 1; try s.writeByte(')'); } } - for (inputs) |input| { - const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the strings and their null terminators, we still use the next u32 - // for the null terminator. - extra_i += (constraint.len + name.len + 1) / 4 + 1; - + it = unwrapped_asm.iterateInputs(); + while (it.next()) |in| { + const name = in.name; + const constraint = in.constraint; try s.print(", [{s}] in {s} = (", .{ name, constraint }); - try w.writeOperand(s, inst, op_index, input); + try w.writeOperand(s, inst, op_index, in.operand); op_index += 1; try s.writeByte(')'); } const zcu = w.pt.zcu; const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; const struct_type: Type = .fromInterned(aggregate.ty); switch (aggregate.storage) { .elems => |elems| for (elems, 0..) |elem, i| { @@ -750,7 +720,7 @@ const Writer = struct { try s.print(", {x}", .{bytes}); }, } - const asm_source = std.mem.sliceAsBytes(w.air.extra.items[extra_i..])[0..extra.data.source_len]; + const asm_source = unwrapped_asm.source; try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)}); } @@ -767,10 +737,9 @@ const Writer = struct { } fn writeCall(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = w.air.extraData(Air.Call, pl_op.payload); - const args = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra.end..][0..extra.data.args_len])); - try w.writeOperand(s, inst, 0, pl_op.operand); + const call = w.air.unwrapCall(inst); + const args = call.args; + try w.writeOperand(s, inst, 0, call.callee); try s.writeAll(", ["); for (args, 0..) |arg, i| { if (i != 0) try s.writeAll(", "); @@ -792,15 +761,14 @@ const Writer = struct { } fn writeTry(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = w.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]); + const unwrapped_try = w.air.unwrapTry(inst); + const body = unwrapped_try.else_body; const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| liveness.getCondBr(inst) else .{ .then_deaths = &.{}, .else_deaths = &.{} }; - try w.writeOperand(s, inst, 0, pl_op.operand); + try w.writeOperand(s, inst, 0, unwrapped_try.error_union); if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; @@ -826,18 +794,17 @@ const Writer = struct { } fn writeTryPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = w.air.extraData(Air.TryPtr, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]); + const unwrapped_try = w.air.unwrapTryPtr(inst); + const body = unwrapped_try.else_body; const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| liveness.getCondBr(inst) else .{ .then_deaths = &.{}, .else_deaths = &.{} }; - try w.writeOperand(s, inst, 0, extra.data.ptr); + try w.writeOperand(s, inst, 0, unwrapped_try.error_union_ptr); try s.writeAll(", "); - try w.writeType(s, ty_pl.ty.toType()); + try w.writeType(s, unwrapped_try.error_union_payload_ptr_ty.toType()); if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; @@ -863,23 +830,22 @@ const Writer = struct { } fn writeCondBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = w.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const cond_br = w.air.unwrapCondBr(inst); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| liveness.getCondBr(inst) else .{ .then_deaths = &.{}, .else_deaths = &.{} }; - try w.writeOperand(s, inst, 0, pl_op.operand); + try w.writeOperand(s, inst, 0, cond_br.condition); if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(","); - if (extra.data.branch_hints.true != .none) { - try s.print(" {s}", .{@tagName(extra.data.branch_hints.true)}); + if (cond_br.branch_hints.true != .none) { + try s.print(" {s}", .{@tagName(cond_br.branch_hints.true)}); } - if (extra.data.branch_hints.then_cov != .none) { - try s.print(" {s}", .{@tagName(extra.data.branch_hints.then_cov)}); + if (cond_br.branch_hints.then_cov != .none) { + try s.print(" {s}", .{@tagName(cond_br.branch_hints.then_cov)}); } try s.writeAll(" {\n"); const old_indent = w.indent; @@ -897,11 +863,11 @@ const Writer = struct { try w.writeBody(s, then_body); try s.splatByteAll(' ', old_indent); try s.writeAll("},"); - if (extra.data.branch_hints.false != .none) { - try s.print(" {s}", .{@tagName(extra.data.branch_hints.false)}); + if (cond_br.branch_hints.false != .none) { + try s.print(" {s}", .{@tagName(cond_br.branch_hints.false)}); } - if (extra.data.branch_hints.else_cov != .none) { - try s.print(" {s}", .{@tagName(extra.data.branch_hints.else_cov)}); + if (cond_br.branch_hints.else_cov != .none) { + try s.print(" {s}", .{@tagName(cond_br.branch_hints.else_cov)}); } try s.writeAll(" {\n"); diff --git a/src/Air/types_resolved.zig b/src/Air/types_resolved.zig index 752b4eccc325fd4123d088da8a3e3ec7cf24be6e..216f690414abdf2daea6993536b303812a05866e 100644 --- a/src/Air/types_resolved.zig +++ b/src/Air/types_resolved.zig @@ -170,21 +170,21 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { .block, .loop, => { - const extra = air.extraData(Air.Block, data.ty_pl.payload); - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; + const block = air.unwrapBlock(inst); + if (!checkType(block.ty, zcu)) return false; if (!checkBody( air, - @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]), + block.body, zcu, )) return false; }, .dbg_inline_block => { - const extra = air.extraData(Air.DbgInlineBlock, data.ty_pl.payload); - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; + const block = air.unwrapDbgBlock(inst); + if (!checkType(block.ty, zcu)) return false; if (!checkBody( air, - @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]), + block.body, zcu, )) return false; }, @@ -342,9 +342,9 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { .call_never_tail, .call_never_inline, => { - const extra = air.extraData(Air.Call, data.pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..extra.data.args_len]); - if (!checkRef(data.pl_op.operand, zcu)) return false; + const call = air.unwrapCall(inst); + const args = call.args; + if (!checkRef(call.callee, zcu)) return false; for (args) |arg| if (!checkRef(arg, zcu)) return false; }, @@ -356,37 +356,37 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { }, .@"try", .try_cold => { - const extra = air.extraData(Air.Try, data.pl_op.payload); - if (!checkRef(data.pl_op.operand, zcu)) return false; + const unwrapped_try = air.unwrapTry(inst); + if (!checkRef(unwrapped_try.error_union, zcu)) return false; if (!checkBody( air, - @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]), + unwrapped_try.else_body, zcu, )) return false; }, .try_ptr, .try_ptr_cold => { - const extra = air.extraData(Air.TryPtr, data.ty_pl.payload); - if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; - if (!checkRef(extra.data.ptr, zcu)) return false; + const unwrapped_try = air.unwrapTryPtr(inst); + if (!checkType(unwrapped_try.error_union_payload_ptr_ty.toType(), zcu)) return false; + if (!checkRef(unwrapped_try.error_union_ptr, zcu)) return false; if (!checkBody( air, - @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]), + unwrapped_try.else_body, zcu, )) return false; }, .cond_br => { - const extra = air.extraData(Air.CondBr, data.pl_op.payload); - if (!checkRef(data.pl_op.operand, zcu)) return false; + const cond_br = air.unwrapCondBr(inst); + if (!checkRef(cond_br.condition, zcu)) return false; if (!checkBody( air, - @ptrCast(air.extra.items[extra.end..][0..extra.data.then_body_len]), + cond_br.then_body, zcu, )) return false; if (!checkBody( air, - @ptrCast(air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len]), + cond_br.else_body, zcu, )) return false; }, @@ -407,20 +407,20 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { }, .assembly => { - const extra = air.extraData(Air.Asm, data.ty_pl.payload); + const unwrapped_asm = air.unwrapAsm(inst); if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; // Luckily, we only care about the inputs and outputs, so we don't have to do // the whole null-terminated string dance. - const outputs_len = extra.data.flags.outputs_len; - const outputs: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..outputs_len]); - const inputs: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end + outputs_len ..][0..extra.data.inputs_len]); + const outputs = unwrapped_asm.outputs; + const inputs = unwrapped_asm.inputs; + for (outputs) |output| if (output != .none and !checkRef(output, zcu)) return false; for (inputs) |input| if (input != .none and !checkRef(input, zcu)) return false; }, .legalize_compiler_rt_call => { - const extra = air.extraData(Air.Call, data.legalize_compiler_rt_call.payload); - const args: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..extra.data.args_len]); + const rt_call = air.unwrapCompilerRtCall(inst); + const args = rt_call.args; for (args) |arg| if (!checkRef(arg, zcu)) return false; }, diff --git a/src/Sema.zig b/src/Sema.zig index fc71a58f776518edf6d0dd745469bb1c1f77e1ef..87dd6a83bbc9fed4e1533a20499793f10024cdcf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16226,6 +16226,12 @@ fn zirAsm( }); sema.appendRefsAssumeCapacity(out_args); sema.appendRefsAssumeCapacity(args); + { + const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); + @memcpy(buffer[0..asm_source.len], asm_source); + buffer[asm_source.len] = 0; + sema.air_extra.items.len += asm_source.len / 4 + 1; + } for (outputs) |o| { const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); @memcpy(buffer[0..o.c.len], o.c); @@ -16242,12 +16248,6 @@ fn zirAsm( buffer[input.c.len + 1 + input.n.len] = 0; sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4; } - { - const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); - @memcpy(buffer[0..asm_source.len], asm_source); - buffer[asm_source.len] = 0; - sema.air_extra.items.len += asm_source.len / 4 + 1; - } return asm_air; } diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index 49de055b47b6b55a3119ce52065888af61d8b0f8..55f0d7fcc0e37b35198f01c2ff88c96cfd72aca4 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -274,10 +274,9 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { }, .assembly => { const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; - const extra = isel.air.extraData(Air.Asm, ty_pl.payload); - const operands: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0 .. extra.data.flags.outputs_len + extra.data.inputs_len]); + const unwrapped_asm = isel.air.unwrapAsm(air_inst_index); - for (operands) |operand| if (operand != .none) try isel.analyzeUse(operand); + for (unwrapped_asm.outputs) |operand| if (operand != .none) try isel.analyzeUse(operand); if (ty_pl.ty != .void_type) try isel.def_order.putNoClobber(gpa, air_inst_index, {}); air_body_index += 1; @@ -355,23 +354,23 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { continue :air_tag air_tags[@intFromEnum(air_inst_index)]; }, inline .block, .dbg_inline_block => |air_tag| { - const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; - const extra = isel.air.extraData(switch (air_tag) { + const air_body_block = switch (air_tag) { else => comptime unreachable, - .block => Air.Block, - .dbg_inline_block => Air.DbgInlineBlock, - }, ty_pl.payload); - const result_ty = ty_pl.ty.toInterned().?; + .block => isel.air.unwrapBlock(air_inst_index), + .dbg_inline_block => isel.air.unwrapDbgBlock(air_inst_index), + }; + + const result_ty = air_body_block.ty.toIntern(); if (result_ty == .noreturn_type) { - try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + try isel.analyze(air_body_block.body); air_body_index += 1; break :air_tag; } assert(!(try isel.blocks.getOrPut(gpa, air_inst_index)).found_existing); - try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + try isel.analyze(air_body_block.body); const block_entry = isel.blocks.pop().?; assert(block_entry.key == air_inst_index); @@ -382,8 +381,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { continue :air_tag air_tags[@intFromEnum(air_inst_index)]; }, .loop => { - const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; - const extra = isel.air.extraData(Air.Block, ty_pl.payload); + const air_body_block = isel.air.unwrapBlock(air_inst_index); const initial_dom_start = isel.dom_start; const initial_dom_len = isel.dom_len; @@ -399,7 +397,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { .repeat_list = undefined, }); try isel.dom.appendNTimes(gpa, 0, std.math.divCeil(usize, isel.dom_len, @bitSizeOf(DomInt)) catch unreachable); - try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + try isel.analyze(air_body_block.body); for ( isel.dom.items[initial_dom_start..].ptr, isel.dom.items[isel.dom_start..][0 .. std.math.divCeil(usize, initial_dom_len, @bitSizeOf(DomInt)) catch unreachable], @@ -429,18 +427,17 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { .call_never_tail, .call_never_inline, => { - const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op; - const extra = isel.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0..extra.data.args_len]); + const air_call = isel.air.unwrapCall(air_inst_index); + const args = air_call.args; isel.saved_registers.insert(.lr); - const callee_ty = isel.air.typeOf(pl_op.operand, ip); + const callee_ty = isel.air.typeOf(air_call.callee, ip); const func_info = switch (ip.indexToKey(callee_ty.toIntern())) { else => unreachable, .func_type => |func_type| func_type, .ptr_type => |ptr_type| ip.indexToKey(ptr_type.child).func_type, }; - try isel.analyzeUse(pl_op.operand); + try isel.analyzeUse(air_call.callee); var param_it: CallAbiIterator = .init; for (args, 0..) |arg, arg_index| { const restore_values_len = isel.values.items.len; @@ -549,13 +546,12 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { continue :air_tag air_tags[@intFromEnum(air_inst_index)]; }, .cond_br => { - const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op; - const extra = isel.air.extraData(Air.CondBr, pl_op.payload); + const cond_br = isel.air.unwrapCondBr(air_inst_index); - try isel.analyzeUse(pl_op.operand); + try isel.analyzeUse(cond_br.condition); - try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.then_body_len])); - try isel.analyze(@ptrCast(isel.air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len])); + try isel.analyze(cond_br.then_body); + try isel.analyze(cond_br.else_body); air_body_index += 1; }, @@ -610,11 +606,10 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { air_body_index += 1; }, .@"try", .try_cold => { - const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op; - const extra = isel.air.extraData(Air.Try, pl_op.payload); + const unwrapped_try = isel.air.unwrapTry(air_inst_index); - try isel.analyzeUse(pl_op.operand); - try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + try isel.analyzeUse(unwrapped_try.error_union); + try isel.analyze(unwrapped_try.else_body); try isel.def_order.putNoClobber(gpa, air_inst_index, {}); air_body_index += 1; @@ -622,11 +617,10 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { continue :air_tag air_tags[@intFromEnum(air_inst_index)]; }, .try_ptr, .try_ptr_cold => { - const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl; - const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload); + const unwrapped_try = isel.air.unwrapTryPtr(air_inst_index); - try isel.analyzeUse(extra.data.ptr); - try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + try isel.analyzeUse(unwrapped_try.error_union_ptr); + try isel.analyze(unwrapped_try.else_body); try isel.def_order.putNoClobber(gpa, air_inst_index, {}); air_body_index += 1; @@ -2698,12 +2692,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, .inferred_alloc, .inferred_alloc_comptime => unreachable, .assembly => { const ty_pl = air.data(air.inst_index).ty_pl; - const extra = isel.air.extraData(Air.Asm, ty_pl.payload); - var extra_index = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra_index..][0..extra.data.flags.outputs_len]); - extra_index += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra_index..][0..extra.data.inputs_len]); - extra_index += inputs.len; + const unwrapped_asm = isel.air.unwrapAsm(air.inst_index); + const inputs = unwrapped_asm.inputs; var as: codegen.aarch64.Assemble = .{ .source = undefined, @@ -2711,15 +2701,12 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, }; defer as.operands.deinit(gpa); - for (outputs) |output| { - const extra_bytes = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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_index += (constraint.len + name.len + (2 + 3)) / 4; + var it = unwrapped_asm.iterateOutputs(); + while (it.next()) |output| { + const constraint = output.constraint; + const name = output.name; - switch (output) { + switch (output.operand) { else => return isel.fail("invalid constraint: '{s}'", .{constraint}), .none => if (std.mem.startsWith(u8, constraint, "={") and std.mem.endsWith(u8, constraint, "}")) { const output_reg = Register.parse(constraint["={".len .. constraint.len - "}".len]) orelse @@ -2760,54 +2747,51 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } const input_mats = try gpa.alloc(Value.Materialize, inputs.len); + var index: u32 = 0; defer gpa.free(input_mats); - const inputs_extra_index = extra_index; - for (inputs, input_mats) |input, *input_mat| { - const extra_bytes = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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_index += (constraint.len + name.len + (2 + 3)) / 4; + it = unwrapped_asm.iterateInputs(); + while (it.next()) |input| : (index += 1) { + const constraint = input.constraint; + const name = input.name; if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) { const input_reg = Register.parse(constraint["{".len .. constraint.len - "}".len]) orelse return isel.fail("invalid constraint: '{s}'", .{constraint}); - input_mat.* = .{ .vi = try isel.use(input), .ra = input_reg.alias }; + input_mats[index] = .{ .vi = try isel.use(input.operand), .ra = input_reg.alias }; if (!std.mem.eql(u8, name, "_")) { const operand_gop = try as.operands.getOrPut(gpa, name); if (operand_gop.found_existing) return isel.fail("duplicate input name: '{s}'", .{name}); - const input_ty = isel.air.typeOf(input, ip); + const input_ty = isel.air.typeOf(input.operand, ip); operand_gop.value_ptr.* = .{ .register = switch (input_ty.abiSize(zcu)) { 0 => unreachable, 1...4 => input_reg.alias.w(), 5...8 => input_reg.alias.x(), else => return isel.fail("too big input type: '{f}'", .{ - isel.fmtType(isel.air.typeOf(input, ip)), + isel.fmtType(isel.air.typeOf(input.operand, ip)), }), } }; } } else if (std.mem.eql(u8, constraint, "r")) { - const input_vi = try isel.use(input); - input_mat.* = try input_vi.matReg(isel); + const input_vi = try isel.use(input.operand); + input_mats[index] = try input_vi.matReg(isel); if (!std.mem.eql(u8, name, "_")) { const operand_gop = try as.operands.getOrPut(gpa, name); if (operand_gop.found_existing) return isel.fail("duplicate input name: '{s}'", .{name}); operand_gop.value_ptr.* = .{ .register = switch (input_vi.size(isel)) { 0 => unreachable, - 1...4 => input_mat.ra.w(), - 5...8 => input_mat.ra.x(), + 1...4 => input_mats[index].ra.w(), + 5...8 => input_mats[index].ra.x(), else => return isel.fail("too big input type: '{f}'", .{ - isel.fmtType(isel.air.typeOf(input, ip)), + isel.fmtType(isel.air.typeOf(input.operand, ip)), }), } }; } } else if (std.mem.eql(u8, name, "_")) { - input_mat.vi = try isel.use(input); + input_mats[index].vi = try isel.use(input.operand); } else return isel.fail("invalid constraint: '{s}'", .{constraint}); } - const clobbers = ip.indexToKey(extra.data.clobbers).aggregate; + const clobbers = ip.indexToKey(unwrapped_asm.clobbers).aggregate; const clobbers_ty: ZigType = .fromInterned(clobbers.ty); for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { switch (switch (clobbers.storage) { @@ -2858,7 +2842,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, } } - as.source = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..])[0..extra.data.source_len :0]; + as.source = unwrapped_asm.source; const asm_start = isel.instructions.items.len; while (as.nextInstruction() catch |err| switch (err) { error.InvalidSyntax => { @@ -2872,21 +2856,18 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, }) |instruction| try isel.emit(instruction); std.mem.reverse(codegen.aarch64.encoding.Instruction, isel.instructions.items[asm_start..]); - extra_index = inputs_extra_index; - for (input_mats) |input_mat| { - const extra_bytes = std.mem.sliceAsBytes(isel.air.extra.items[extra_index..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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_index += (constraint.len + name.len + (2 + 3)) / 4; + it = unwrapped_asm.iterateInputs(); + index = 0; + while (it.next()) |input| : (index += 1) { + const constraint = input.constraint; + const name = input.name; if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) { - try input_mat.vi.liveOut(isel, input_mat.ra); + try input_mats[index].vi.liveOut(isel, input_mats[index].ra); } else if (std.mem.eql(u8, constraint, "r")) { - try input_mat.finish(isel); + try input_mats[index].finish(isel); } else if (std.mem.eql(u8, name, "_")) { - try input_mat.vi.mat(isel); + try input_mats[index].vi.mat(isel); } else unreachable; } @@ -3515,16 +3496,16 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, .block => { - const ty_pl = air.data(air.inst_index).ty_pl; - const extra = isel.air.extraData(Air.Block, ty_pl.payload); - try isel.block(air.inst_index, ty_pl.ty.toType(), @ptrCast( - isel.air.extra.items[extra.end..][0..extra.data.body_len], - )); + const unwrapped_block = isel.air.unwrapBlock(air.inst_index); + try isel.block( + air.inst_index, + unwrapped_block.ty, + unwrapped_block.body, + ); if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, .loop => { - const ty_pl = air.data(air.inst_index).ty_pl; - const extra = isel.air.extraData(Air.Block, ty_pl.payload); + const unwrapped_block = isel.air.unwrapBlock(air.inst_index); const loops = isel.loops.values(); const loop_index = isel.loops.getIndex(air.inst_index).?; const loop = &loops[loop_index]; @@ -3558,7 +3539,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, loop.live_registers = isel.live_registers; loop.repeat_list = Loop.empty_list; - try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + try isel.body(unwrapped_block.body); try isel.merge(&loop.live_registers, .{ .fill_extra = true }); var repeat_label = loop.repeat_list; @@ -3608,10 +3589,9 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, .call => { - const pl_op = air.data(air.inst_index).pl_op; - const extra = isel.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(isel.air.extra.items[extra.end..][0..extra.data.args_len]); - const callee_ty = isel.air.typeOf(pl_op.operand, ip); + const air_call = isel.air.unwrapCall(air.inst_index); + const args = air_call.args; + const callee_ty = isel.air.typeOf(air_call.callee, ip); const func_info = switch (ip.indexToKey(callee_ty.toIntern())) { else => unreachable, .func_type => |func_type| func_type, @@ -3649,7 +3629,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, try call.finishReturn(isel); try call.prepareCallee(isel); - if (pl_op.operand.toInterned()) |ct_callee| { + if (air_call.callee.toInterned()) |ct_callee| { try isel.nav_relocs.append(gpa, switch (ip.indexToKey(ct_callee)) { else => unreachable, inline .@"extern", .func => |func| .{ @@ -3666,7 +3646,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, }); try isel.emit(.bl(0)); } else { - const callee_vi = try isel.use(pl_op.operand); + const callee_vi = try isel.use(air_call.callee); const callee_mat = try callee_vi.matReg(isel); try isel.emit(.blr(callee_mat.ra.x())); try callee_mat.finish(isel); @@ -4523,16 +4503,15 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, .cond_br => { - const pl_op = air.data(air.inst_index).pl_op; - const extra = isel.air.extraData(Air.CondBr, pl_op.payload); + const cond_br = isel.air.unwrapCondBr(air.inst_index); - try isel.body(@ptrCast(isel.air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len])); + try isel.body(cond_br.then_body); const else_label = isel.instructions.items.len; const else_live_registers = isel.live_registers; - try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.then_body_len])); + try isel.body(cond_br.else_body); try isel.merge(&else_live_registers, .{}); - const cond_vi = try isel.use(pl_op.operand); + const cond_vi = try isel.use(cond_br.condition); const cond_mat = try cond_vi.matReg(isel); try isel.emit(.tbz( cond_mat.ra.x(), @@ -4819,13 +4798,12 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, .@"try", .try_cold => { - const pl_op = air.data(air.inst_index).pl_op; - const extra = isel.air.extraData(Air.Try, pl_op.payload); - const error_union_ty = isel.air.typeOf(pl_op.operand, ip); + const unwrapped_try = isel.air.unwrapTry(air.inst_index); + const error_union_ty = isel.air.typeOf(unwrapped_try.error_union, &zcu.intern_pool); const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type; const payload_ty: ZigType = .fromInterned(error_union_info.payload_type); - const error_union_vi = try isel.use(pl_op.operand); + const error_union_vi = try isel.use(unwrapped_try.error_union); if (isel.live_values.fetchRemove(air.inst_index)) |payload_vi| { defer payload_vi.value.deref(isel); @@ -4840,7 +4818,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const cont_label = isel.instructions.items.len; const cont_live_registers = isel.live_registers; - try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + try isel.body(unwrapped_try.else_body); try isel.merge(&cont_live_registers, .{}); var error_set_part_it = error_union_vi.field( @@ -4859,18 +4837,17 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, .try_ptr, .try_ptr_cold => { - const ty_pl = air.data(air.inst_index).ty_pl; - const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload); - const error_union_ty = isel.air.typeOf(extra.data.ptr, ip).childType(zcu); + const unwrapped_try = isel.air.unwrapTryPtr(air.inst_index); + const error_union_ty = isel.air.typeOf(unwrapped_try.error_union_ptr, ip).childType(zcu); const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type; const payload_ty: ZigType = .fromInterned(error_union_info.payload_type); - const error_union_ptr_vi = try isel.use(extra.data.ptr); + const error_union_ptr_vi = try isel.use(unwrapped_try.error_union_ptr); const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel); if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: { defer payload_ptr_vi.value.deref(isel); - switch (codegen.errUnionPayloadOffset(ty_pl.ty.toType().childType(zcu), zcu)) { - 0 => try payload_ptr_vi.value.move(isel, extra.data.ptr), + switch (codegen.errUnionPayloadOffset(unwrapped_try.error_union_payload_ptr_ty.toType().childType(zcu), zcu)) { + 0 => try payload_ptr_vi.value.move(isel, unwrapped_try.error_union_ptr), else => |payload_offset| { const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused; const lo12: u12 = @truncate(payload_offset >> 0); @@ -4887,7 +4864,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, const cont_label = isel.instructions.items.len; const cont_live_registers = isel.live_registers; - try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len])); + try isel.body(unwrapped_try.else_body); try isel.merge(&cont_live_registers, .{}); const error_set_ra = try isel.allocIntReg(); @@ -4913,11 +4890,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, .dbg_inline_block => { - const ty_pl = air.data(air.inst_index).ty_pl; - const extra = isel.air.extraData(Air.DbgInlineBlock, ty_pl.payload); - try isel.block(air.inst_index, ty_pl.ty.toType(), @ptrCast( - isel.air.extra.items[extra.end..][0..extra.data.body_len], - )); + const dbg_block = isel.air.unwrapDbgBlock(air.inst_index); + try isel.block(air.inst_index, dbg_block.ty, dbg_block.body); if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f55dd408b52460bad630bf4efd1a2f251a5b8d91..106737a8331c523f9ecddad0e0138f31a10ea06f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -4622,9 +4622,8 @@ fn airCall( const gpa = f.object.dg.gpa; const w = &f.object.code.writer; - const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = f.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.args_len]); + const call = f.air.unwrapCall(inst); + const args = call.args; const resolved_args = try gpa.alloc(CValue, args.len); defer gpa.free(resolved_args); @@ -4653,15 +4652,15 @@ fn airCall( } } - const callee = try f.resolveInst(pl_op.operand); + const callee = try f.resolveInst(call.callee); { var bt = iterateBigTomb(f, inst); - try bt.feed(pl_op.operand); + try bt.feed(call.callee); for (args) |arg| try bt.feed(arg); } - const callee_ty = f.typeOf(pl_op.operand); + const callee_ty = f.typeOf(call.callee); const callee_is_ptr = switch (callee_ty.zigTypeTag(zcu)) { .@"fn" => false, .pointer => true, @@ -4698,7 +4697,7 @@ fn airCall( callee: { known: { - const callee_val = (try f.air.value(pl_op.operand, pt)) orelse break :known; + const callee_val = (try f.air.value(call.callee, pt)) orelse break :known; const fn_nav, const need_cast = switch (ip.indexToKey(callee_val.toIntern())) { .@"extern" => |@"extern"| .{ @"extern".owner_nav, false }, .func => |func| .{ func.owner_nav, Type.fromInterned(func.ty).fnCallingConvention(zcu) != .naked and @@ -4796,13 +4795,12 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue { const pt = f.object.dg.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload); - const owner_nav = ip.getNav(zcu.funcInfo(extra.data.func).owner_nav); + const block = f.air.unwrapDbgBlock(inst); + const owner_nav = ip.getNav(zcu.funcInfo(block.func).owner_nav); const w = &f.object.code.writer; try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)}); try f.object.newline(); - return lowerBlock(f, inst, @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len])); + return lowerBlock(f, inst, block.body); } fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4822,9 +4820,8 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { } fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { - const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = f.air.extraData(Air.Block, ty_pl.payload); - return lowerBlock(f, inst, @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len])); + const block = f.air.unwrapBlock(inst); + return lowerBlock(f, inst, block.body); } fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) !CValue { @@ -4873,21 +4870,19 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) } fn airTry(f: *Function, inst: Air.Inst.Index) !CValue { - const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = f.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len]); - const err_union_ty = f.typeOf(pl_op.operand); - return lowerTry(f, inst, pl_op.operand, body, err_union_ty, false); + const pt = f.object.dg.pt; + const unwrapped_try = f.air.unwrapTry(inst); + const body = unwrapped_try.else_body; + const err_union_ty = f.air.typeOf(unwrapped_try.error_union, &pt.zcu.intern_pool); + return lowerTry(f, inst, unwrapped_try.error_union, body, err_union_ty, false); } fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue { const pt = f.object.dg.pt; - const zcu = pt.zcu; - const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = f.air.extraData(Air.TryPtr, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len]); - const err_union_ty = f.typeOf(extra.data.ptr).childType(zcu); - return lowerTry(f, inst, extra.data.ptr, body, err_union_ty, true); + const unwrapped_try = f.air.unwrapTryPtr(inst); + const body = unwrapped_try.else_body; + const err_union_ty = f.air.typeOf(unwrapped_try.error_union_ptr, &pt.zcu.intern_pool).childType(pt.zcu); + return lowerTry(f, inst, unwrapped_try.error_union_ptr, body, err_union_ty, true); } fn lowerTry( @@ -5216,9 +5211,7 @@ fn airUnreach(o: *Object) !void { } fn airLoop(f: *Function, inst: Air.Inst.Index) !void { - const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const loop = f.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[loop.end..][0..loop.data.body_len]); + const block = f.air.unwrapBlock(inst); const w = &f.object.code.writer; // `repeat` instructions matching this loop will branch to @@ -5227,16 +5220,15 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !void { // construct at all! try w.print("zig_loop_{d}:", .{@intFromEnum(inst)}); try f.object.newline(); - try genBodyInner(f, body); // no need to restore state, we're noreturn + try genBodyInner(f, block.body); // no need to restore state, we're noreturn } fn airCondBr(f: *Function, inst: Air.Inst.Index) !void { - const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const cond = try f.resolveInst(pl_op.operand); - try reap(f, inst, &.{pl_op.operand}); - const extra = f.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const cond_br = f.air.unwrapCondBr(inst); + const cond = try f.resolveInst(cond_br.condition); + try reap(f, inst, &.{cond_br.condition}); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; const liveness_condbr = f.liveness.getCondBr(inst); const w = &f.object.code.writer; @@ -5439,16 +5431,11 @@ fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const pt = f.object.dg.pt; const zcu = pt.zcu; - const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = f.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = extra.data.flags.is_volatile; - const outputs_len = extra.data.flags.outputs_len; + const unwrapped_asm = f.air.unwrapAsm(inst); + const is_volatile = unwrapped_asm.is_volatile; const gpa = f.object.dg.gpa; - var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..outputs_len]); - extra_i += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..extra.data.inputs_len]); - extra_i += inputs.len; + const outputs = unwrapped_asm.outputs; + const inputs = unwrapped_asm.inputs; const result = result: { const w = &f.object.code.writer; @@ -5469,14 +5456,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } else .none; const locals_begin: LocalIndex = @intCast(f.locals.items.len); - const constraints_extra_begin = extra_i; - for (outputs) |output| { - const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]); - const constraint = mem.sliceTo(extra_bytes, 0); - const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; + var it = unwrapped_asm.iterateOutputs(); + while (it.next()) |output| { + const constraint = output.constraint; if (constraint.len < 2 or constraint[0] != '=' or (constraint[1] == '{' and constraint[constraint.len - 1] != '}')) @@ -5486,7 +5468,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_reg = constraint[1] == '{'; if (is_reg) { - const output_ty = if (output == .none) inst_ty else f.typeOf(output).childType(zcu); + const output_ty = if (output.operand == .none) inst_ty else f.typeOf(output.operand).childType(zcu); try w.writeAll("register "); const output_local = try f.allocLocalValue(.{ .ctype = try f.ctypeFromType(output_ty, .complete), @@ -5505,13 +5487,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try f.object.newline(); } } - for (inputs) |input| { - const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]); - const constraint = mem.sliceTo(extra_bytes, 0); - const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; + + it = unwrapped_asm.iterateInputs(); + while (it.next()) |input| { + const constraint = input.constraint; if (constraint.len < 1 or mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or (constraint[0] == '{' and constraint[constraint.len - 1] != '}')) @@ -5520,9 +5499,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } const is_reg = constraint[0] == '{'; - const input_val = try f.resolveInst(input); + const input_val = try f.resolveInst(input.operand); if (asmInputNeedsLocal(f, constraint, input_val)) { - const input_ty = f.typeOf(input); + const input_ty = f.typeOf(input.operand); if (is_reg) try w.writeAll("register "); const input_local = try f.allocLocalValue(.{ .ctype = try f.ctypeFromType(input_ty, .complete), @@ -5545,7 +5524,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } { - const asm_source = mem.sliceAsBytes(f.air.extra.items[extra_i..])[0..extra.data.source_len]; + const asm_source = unwrapped_asm.source; var stack = std.heap.stackFallback(256, f.object.dg.gpa); const allocator = stack.get(); @@ -5599,18 +5578,15 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try w.print("({f}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)}); } - extra_i = constraints_extra_begin; var locals_index = locals_begin; try w.writeByte(':'); - for (outputs, 0..) |output, index| { - const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]); - const constraint = mem.sliceTo(extra_bytes, 0); - const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; - if (index > 0) try w.writeByte(','); + it = unwrapped_asm.iterateOutputs(); + while (it.next()) |output| { + const constraint = output.constraint; + const name = output.name; + + if (output.index > 0) try w.writeByte(','); try w.writeByte(' '); if (!mem.eql(u8, name, "_")) try w.print("[{s}]", .{name}); const is_reg = constraint[1] == '{'; @@ -5618,28 +5594,26 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (is_reg) { try f.writeCValue(w, .{ .local = locals_index }, .Other); locals_index += 1; - } else if (output == .none) { + } else if (output.operand == .none) { try f.writeCValue(w, inst_local, .FunctionArgument); } else { - try f.writeCValueDeref(w, try f.resolveInst(output)); + try f.writeCValueDeref(w, try f.resolveInst(output.operand)); } try w.writeByte(')'); } try w.writeByte(':'); - for (inputs, 0..) |input, index| { - const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]); - const constraint = mem.sliceTo(extra_bytes, 0); - const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; - if (index > 0) try w.writeByte(','); + it = unwrapped_asm.iterateInputs(); + while (it.next()) |input| { + const constraint = input.constraint; + const name = input.name; + + if (input.index > 0) try w.writeByte(','); try w.writeByte(' '); if (!mem.eql(u8, name, "_")) try w.print("[{s}]", .{name}); const is_reg = constraint[0] == '{'; - const input_val = try f.resolveInst(input); + const input_val = try f.resolveInst(input.operand); try w.print("{f}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)}); try f.writeCValue(w, if (asmInputNeedsLocal(f, constraint, input_val)) local: { const input_local_idx = locals_index; @@ -5650,7 +5624,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } try w.writeByte(':'); const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; const struct_type: Type = .fromInterned(aggregate.ty); switch (aggregate.storage) { .elems => |elems| for (elems, 0..) |elem, i| switch (elem) { @@ -5697,22 +5671,17 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try w.writeAll(");"); try f.object.newline(); - extra_i = constraints_extra_begin; locals_index = locals_begin; - for (outputs) |output| { - const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]); - const constraint = mem.sliceTo(extra_bytes, 0); - const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; + it = unwrapped_asm.iterateOutputs(); + while (it.next()) |output| { + const constraint = output.constraint; const is_reg = constraint[1] == '{'; if (is_reg) { - try f.writeCValueDeref(w, if (output == .none) + try f.writeCValueDeref(w, if (output.operand == .none) .{ .local_ref = inst_local.new_local } else - try f.resolveInst(output)); + try f.resolveInst(output.operand)); try w.writeAll(" = "); try f.writeCValue(w, .{ .local = locals_index }, .Other); locals_index += 1; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a178604a6874b68dc30e69f00b7d50bc0e0bfa86..b2a7d230ce7b695f715c2421e9ef2c5f1588f998 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5258,14 +5258,13 @@ pub const FuncGen = struct { }; fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !Builder.Value { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]); + const air_call = self.air.unwrapCall(inst); + const args = air_call.args; const o = self.ng.object; const pt = self.ng.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - const callee_ty = self.typeOf(pl_op.operand); + const callee_ty = self.typeOf(air_call.callee); const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) { .@"fn" => callee_ty, .pointer => callee_ty.childType(zcu), @@ -5273,7 +5272,7 @@ pub const FuncGen = struct { }; const fn_info = zcu.typeToFunc(zig_fn_ty).?; const return_type = Type.fromInterned(fn_info.return_type); - const llvm_fn = try self.resolveInst(pl_op.operand); + const llvm_fn = try self.resolveInst(air_call.callee); const target = zcu.getTarget(); const sret = firstParamSRet(fn_info, zcu, target); @@ -5934,9 +5933,8 @@ pub const FuncGen = struct { } fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Block, ty_pl.payload); - return self.lowerBlock(inst, null, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len])); + const block = self.air.unwrapBlock(inst); + return self.lowerBlock(inst, null, block.body); } fn lowerBlock( @@ -6216,11 +6214,10 @@ pub const FuncGen = struct { } fn airCondBr(self: *FuncGen, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const cond = try self.resolveInst(pl_op.operand); - const extra = self.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const cond_br = self.air.unwrapCondBr(inst); + const cond = try self.resolveInst(cond_br.condition); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; const Hint = enum { none, @@ -6230,22 +6227,22 @@ pub const FuncGen = struct { then_cold, else_cold, }; - const hint: Hint = switch (extra.data.branch_hints.true) { - .none => switch (extra.data.branch_hints.false) { + const hint: Hint = switch (cond_br.branch_hints.true) { + .none => switch (cond_br.branch_hints.false) { .none => .none, .likely => .else_likely, .unlikely => .then_likely, .cold => .else_cold, .unpredictable => .unpredictable, }, - .likely => switch (extra.data.branch_hints.false) { + .likely => switch (cond_br.branch_hints.false) { .none => .then_likely, .likely => .unpredictable, .unlikely => .then_likely, .cold => .else_cold, .unpredictable => .unpredictable, }, - .unlikely => switch (extra.data.branch_hints.false) { + .unlikely => switch (cond_br.branch_hints.false) { .none => .else_likely, .likely => .else_likely, .unlikely => .unpredictable, @@ -6267,35 +6264,33 @@ pub const FuncGen = struct { self.wip.cursor = .{ .block = then_block }; if (hint == .then_cold) _ = try self.wip.callIntrinsicAssumeCold(); - try self.genBodyDebugScope(null, then_body, extra.data.branch_hints.then_cov); + try self.genBodyDebugScope(null, then_body, cond_br.branch_hints.then_cov); self.wip.cursor = .{ .block = else_block }; if (hint == .else_cold) _ = try self.wip.callIntrinsicAssumeCold(); - try self.genBodyDebugScope(null, else_body, extra.data.branch_hints.else_cov); + try self.genBodyDebugScope(null, else_body, cond_br.branch_hints.else_cov); // No need to reset the insert cursor since this instruction is noreturn. } fn airTry(self: *FuncGen, inst: Air.Inst.Index, err_cold: bool) !Builder.Value { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const err_union = try self.resolveInst(pl_op.operand); - const extra = self.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); - const err_union_ty = self.typeOf(pl_op.operand); + const unwrapped_try = self.air.unwrapTry(inst); + const err_union = try self.resolveInst(unwrapped_try.error_union); + const body = unwrapped_try.else_body; + const err_union_ty = self.typeOf(unwrapped_try.error_union); const is_unused = self.liveness.isUnused(inst); return lowerTry(self, err_union, body, err_union_ty, false, false, is_unused, err_cold); } fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index, err_cold: bool) !Builder.Value { const zcu = self.ng.pt.zcu; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); - const err_union_ptr = try self.resolveInst(extra.data.ptr); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); - const err_union_ty = self.typeOf(extra.data.ptr).childType(zcu); + const unwrapped_try = self.air.unwrapTryPtr(inst); + const err_union_ptr = try self.resolveInst(unwrapped_try.error_union_ptr); + const body = unwrapped_try.else_body; + const err_union_ty = self.typeOf(unwrapped_try.error_union_ptr).childType(zcu); const is_unused = self.liveness.isUnused(inst); - self.maybeMarkAllowZeroAccess(self.typeOf(extra.data.ptr).ptrInfo(zcu)); + self.maybeMarkAllowZeroAccess(self.typeOf(unwrapped_try.error_union_ptr).ptrInfo(zcu)); return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused, err_cold); } @@ -6627,9 +6622,8 @@ pub const FuncGen = struct { } fn airLoop(self: *FuncGen, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const loop = self.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[loop.end..][0..loop.data.body_len]); + const block = self.air.unwrapBlock(inst); + const body = block.body; const loop_block = try self.wip.block(1, "Loop"); // `airRepeat` will increment incoming each time _ = try self.wip.br(loop_block); @@ -7137,10 +7131,9 @@ pub const FuncGen = struct { } fn airDbgInlineBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + const block = self.air.unwrapDbgBlock(inst); self.arg_inline_index = 0; - return self.lowerBlock(inst, extra.data.func, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len])); + return self.lowerBlock(inst, block.func, block.body); } fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { @@ -7262,17 +7255,12 @@ pub const FuncGen = struct { // this implementation feeds the inline assembly code directly to LLVM. const o = self.ng.object; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = extra.data.flags.is_volatile; - const outputs_len = extra.data.flags.outputs_len; + const unwrapped_asm = self.air.unwrapAsm(inst); + const is_volatile = unwrapped_asm.is_volatile; const gpa = self.gpa; - var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..outputs_len]); - extra_i += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); - extra_i += inputs.len; + const outputs = unwrapped_asm.outputs; + const inputs = unwrapped_asm.inputs; var llvm_constraints: std.ArrayList(u8) = .empty; defer llvm_constraints.deinit(gpa); @@ -7305,14 +7293,10 @@ pub const FuncGen = struct { var name_map: std.StringArrayHashMapUnmanaged(u16) = .empty; try name_map.ensureUnusedCapacity(arena, max_param_count); - var rw_extra_i = extra_i; - for (outputs, llvm_ret_indirect, llvm_rw_vals) |output, *is_indirect, *llvm_rw_val| { - const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; + var it = unwrapped_asm.iterateOutputs(); + while (it.next()) |output| { + const constraint = output.constraint; + const name = output.name; try llvm_constraints.ensureUnusedCapacity(gpa, constraint.len + 3); if (total_i != 0) { @@ -7320,15 +7304,15 @@ pub const FuncGen = struct { } llvm_constraints.appendAssumeCapacity('='); - if (output != .none) { - const output_inst = try self.resolveInst(output); - const output_ty = self.typeOf(output); + if (output.operand != .none) { + const output_inst = try self.resolveInst(output.operand); + const output_ty = self.typeOf(output.operand); assert(output_ty.zigTypeTag(zcu) == .pointer); const elem_llvm_ty = try o.lowerPtrElemTy(pt, output_ty.childType(zcu)); switch (constraint[0]) { '=' => {}, - '+' => llvm_rw_val.* = output_inst, + '+' => llvm_rw_vals[output.index] = output_inst, else => return self.todo("unsupported output constraint on output type '{c}'", .{ constraint[0], }), @@ -7337,8 +7321,8 @@ pub const FuncGen = struct { self.maybeMarkAllowZeroAccess(output_ty.ptrInfo(zcu)); // Pass any non-return outputs indirectly, if the constraint accepts a memory location - is_indirect.* = constraintAllowsMemory(constraint); - if (is_indirect.*) { + llvm_ret_indirect[output.index] = constraintAllowsMemory(constraint); + if (llvm_ret_indirect[output.index]) { // Pass the result by reference as an indirect output (e.g. "=*m") llvm_constraints.appendAssumeCapacity('*'); @@ -7359,7 +7343,7 @@ pub const FuncGen = struct { }), } - is_indirect.* = false; + llvm_ret_indirect[output.index] = false; const ret_ty = self.typeOfIndex(inst); llvm_ret_types[llvm_ret_i] = try o.lowerType(pt, ret_ty); @@ -7387,16 +7371,13 @@ pub const FuncGen = struct { total_i += 1; } - for (inputs) |input| { - const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; + it = unwrapped_asm.iterateInputs(); + while (it.next()) |input| { + const constraint = input.constraint; + const name = input.name; - const arg_llvm_value = try self.resolveInst(input); - const arg_ty = self.typeOf(input); + const arg_llvm_value = try self.resolveInst(input.operand); + const arg_ty = self.typeOf(input.operand); const is_by_ref = isByRef(arg_ty, zcu); if (is_by_ref) { if (constraintAllowsMemory(constraint)) { @@ -7452,27 +7433,23 @@ pub const FuncGen = struct { total_i += 1; } - for (outputs, llvm_ret_indirect, llvm_rw_vals, 0..) |output, is_indirect, llvm_rw_val, output_index| { - const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[rw_extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[rw_extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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. - rw_extra_i += (constraint.len + name.len + (2 + 3)) / 4; + it = unwrapped_asm.iterateOutputs(); + while (it.next()) |output| { + const constraint = output.constraint; if (constraint[0] != '+') continue; - const rw_ty = self.typeOf(output); + const rw_ty = self.typeOf(output.operand); const llvm_elem_ty = try o.lowerPtrElemTy(pt, rw_ty.childType(zcu)); - if (is_indirect) { - llvm_param_values[llvm_param_i] = llvm_rw_val; - llvm_param_types[llvm_param_i] = llvm_rw_val.typeOfWip(&self.wip); + if (llvm_ret_indirect[output.index]) { + llvm_param_values[llvm_param_i] = llvm_rw_vals[output.index]; + llvm_param_types[llvm_param_i] = llvm_rw_vals[output.index].typeOfWip(&self.wip); } else { const alignment = rw_ty.abiAlignment(zcu).toLlvm(); const loaded = try self.wip.load( if (rw_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, llvm_elem_ty, - llvm_rw_val, + llvm_rw_vals[output.index], alignment, "", ); @@ -7480,18 +7457,18 @@ pub const FuncGen = struct { llvm_param_types[llvm_param_i] = llvm_elem_ty; } - try llvm_constraints.print(gpa, ",{d}", .{output_index}); + try llvm_constraints.print(gpa, ",{d}", .{output.index}); // In the case of indirect inputs, LLVM requires the callsite to have // an elementtype() attribute. - llvm_param_attrs[llvm_param_i] = if (is_indirect) llvm_elem_ty else .none; + llvm_param_attrs[llvm_param_i] = if (llvm_ret_indirect[output.index]) llvm_elem_ty else .none; llvm_param_i += 1; total_i += 1; } const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; const struct_type: Type = .fromInterned(aggregate.ty); if (total_i != 0) try llvm_constraints.append(gpa, ','); switch (aggregate.storage) { @@ -7539,7 +7516,7 @@ pub const FuncGen = struct { if (std.mem.endsWith(u8, llvm_constraints.items, ",")) llvm_constraints.items.len -= 1; - const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; + const asm_source = unwrapped_asm.source; // hackety hacks until stage2 has proper inline asm in the frontend. var rendered_template = std.array_list.Managed(u8).init(gpa); diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig index 5dc89d9af916d5c6f6cc47dfd32982622b3e9412..1f70f5f4de379ad0e01f75be35f0d13695f5b766 100644 --- a/src/codegen/riscv64/CodeGen.zig +++ b/src/codegen/riscv64/CodeGen.zig @@ -3627,11 +3627,11 @@ fn airRuntimeNavPtr(func: *Func, inst: Air.Inst.Index) !void { } fn airTry(func: *Func, inst: Air.Inst.Index) !void { - const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = func.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(func.air.extra.items[extra.end..][0..extra.data.body_len]); - const operand_ty = func.typeOf(pl_op.operand); - const result = try func.genTry(inst, pl_op.operand, body, operand_ty, false); + const zcu = func.pt.zcu; + const unwrapped_try = func.air.unwrapTry(inst); + const body = unwrapped_try.else_body; + const operand_ty = func.air.typeOf(unwrapped_try.error_union, &zcu.intern_pool); + const result = try func.genTry(inst, unwrapped_try.error_union, body, operand_ty, false); return func.finishAir(inst, result, .{ .none, .none, .none }); } @@ -4801,10 +4801,8 @@ fn airFrameAddress(func: *Func, inst: Air.Inst.Index) !void { fn airCall(func: *Func, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { if (modifier == .always_tail) return func.fail("TODO implement tail calls for riscv64", .{}); - const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const callee = pl_op.operand; - const extra = func.air.extraData(Air.Call, pl_op.payload); - const arg_refs: []const Air.Inst.Ref = @ptrCast(func.air.extra.items[extra.end..][0..extra.data.args_len]); + const call = func.air.unwrapCall(inst); + const arg_refs = call.args; const expected_num_args = 8; const ExpectedContents = extern struct { @@ -4822,10 +4820,10 @@ fn airCall(func: *Func, inst: Air.Inst.Index, modifier: std.builtin.CallModifier defer allocator.free(arg_vals); for (arg_vals, arg_refs) |*arg_val, arg_ref| arg_val.* = .{ .air_ref = arg_ref }; - const call_ret = try func.genCall(.{ .air = callee }, arg_tys, arg_vals); + const call_ret = try func.genCall(.{ .air = call.callee }, arg_tys, arg_vals); var bt = func.liveness.iterateBigTomb(inst); - try func.feed(&bt, pl_op.operand); + try func.feed(&bt, call.callee); for (arg_refs) |arg_ref| try func.feed(&bt, arg_ref); const result = if (func.liveness.isUnused(inst)) .unreach else call_ret; @@ -5218,9 +5216,8 @@ fn airDbgStmt(func: *Func, inst: Air.Inst.Index) !void { } fn airDbgInlineBlock(func: *Func, inst: Air.Inst.Index) !void { - const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = func.air.extraData(Air.DbgInlineBlock, ty_pl.payload); - try func.lowerBlock(inst, @ptrCast(func.air.extra.items[extra.end..][0..extra.data.body_len])); + const block = func.air.unwrapDbgBlock(inst); + try func.lowerBlock(inst, block.body); } fn airDbgVar(func: *Func, inst: Air.Inst.Index) InnerError!void { @@ -5271,19 +5268,18 @@ fn genVarDbgInfo( } fn airCondBr(func: *Func, inst: Air.Inst.Index) !void { - const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const cond = try func.resolveInst(pl_op.operand); - const cond_ty = func.typeOf(pl_op.operand); - const extra = func.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(func.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(func.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const cond_br = func.air.unwrapCondBr(inst); + const cond = try func.resolveInst(cond_br.condition); + const cond_ty = func.typeOf(cond_br.condition); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; const liveness_cond_br = func.liveness.getCondBr(inst); // If the condition dies here in this condbr instruction, process // that death now instead of later as this has an effect on // whether it needs to be spilled in the branches if (func.liveness.operandDies(inst, 0)) { - if (pl_op.operand.toIndex()) |op_inst| try func.processDeath(op_inst); + if (cond_br.condition.toIndex()) |op_inst| try func.processDeath(op_inst); } func.scope_generation += 1; @@ -5633,10 +5629,7 @@ fn airIsNonErrPtr(func: *Func, inst: Air.Inst.Index) !void { fn airLoop(func: *Func, inst: Air.Inst.Index) !void { // A loop is a setup to be able to jump back to the beginning. - const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const loop = func.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(func.air.extra.items[loop.end..][0..loop.data.body_len]); - + const body = func.air.unwrapBlock(inst); func.scope_generation += 1; const state = try func.saveState(); @@ -5646,7 +5639,7 @@ fn airLoop(func: *Func, inst: Air.Inst.Index) !void { }); defer assert(func.loops.remove(inst)); - try func.genBody(body); + try func.genBody(body.body); func.finishAirBookkeeping(); } @@ -5663,9 +5656,8 @@ fn jump(func: *Func, index: Mir.Inst.Index) !Mir.Inst.Index { } fn airBlock(func: *Func, inst: Air.Inst.Index) !void { - const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = func.air.extraData(Air.Block, ty_pl.payload); - try func.lowerBlock(inst, @ptrCast(func.air.extra.items[extra.end..][0..extra.data.body_len])); + const block = func.air.unwrapBlock(inst); + try func.lowerBlock(inst, block.body); } fn lowerBlock(func: *Func, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { @@ -6053,15 +6045,9 @@ fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void { } fn airAsm(func: *Func, inst: Air.Inst.Index) !void { - const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = func.air.extraData(Air.Asm, ty_pl.payload); - const outputs_len = extra.data.flags.outputs_len; - var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = - @ptrCast(func.air.extra.items[extra_i..][0..outputs_len]); - extra_i += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(func.air.extra.items[extra_i..][0..extra.data.inputs_len]); - extra_i += inputs.len; + const unwrapped_asm = func.air.unwrapAsm(inst); + const outputs = unwrapped_asm.outputs; + const inputs = unwrapped_asm.inputs; var result: MCValue = .none; var args = std.array_list.Managed(MCValue).init(func.gpa); @@ -6076,19 +6062,15 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { try arg_map.ensureTotalCapacity(@intCast(outputs.len + inputs.len)); defer arg_map.deinit(); - var outputs_extra_i = extra_i; - for (outputs) |output| { - const extra_bytes = mem.sliceAsBytes(func.air.extra.items[extra_i..]); - const constraint = mem.sliceTo(mem.sliceAsBytes(func.air.extra.items[extra_i..]), 0); - const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; + var it = unwrapped_asm.iterateOutputs(); + while (it.next()) |output| { + const constraint = output.constraint; + const name = output.name; const is_read = switch (constraint[0]) { '=' => false, '+' => read: { - if (output == .none) return func.fail( + if (output.operand == .none) return func.fail( "read-write constraint unsupported for asm result: '{s}'", .{constraint}, ); @@ -6100,7 +6082,7 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { const rest = constraint[@as(usize, 1) + @intFromBool(is_early_clobber) ..]; const arg_mcv: MCValue = arg_mcv: { const arg_maybe_reg: ?Register = if (mem.eql(u8, rest, "m")) - if (output != .none) null else return func.fail( + if (output.operand != .none) null else return func.fail( "memory constraint unsupported for asm result: '{s}'", .{constraint}, ) @@ -6115,7 +6097,7 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { break :arg_mcv args.items[index]; } else return func.fail("invalid constraint: '{s}'", .{constraint}); break :arg_mcv if (arg_maybe_reg) |reg| .{ .register = reg } else arg: { - const ptr_mcv = try func.resolveInst(output); + const ptr_mcv = try func.resolveInst(output.operand); switch (ptr_mcv) { .immediate => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_| break :arg ptr_mcv.deref(), @@ -6131,20 +6113,17 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { if (!mem.eql(u8, name, "_")) arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); args.appendAssumeCapacity(arg_mcv); - if (output == .none) result = arg_mcv; - if (is_read) try func.load(arg_mcv, .{ .air_ref = output }, func.typeOf(output)); + if (output.operand == .none) result = arg_mcv; + if (is_read) try func.load(arg_mcv, .{ .air_ref = output.operand }, func.typeOf(output.operand)); } - for (inputs) |input| { - const input_bytes = mem.sliceAsBytes(func.air.extra.items[extra_i..]); - const constraint = mem.sliceTo(input_bytes, 0); - const name = mem.sliceTo(input_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; + it = unwrapped_asm.iterateInputs(); + while (it.next()) |input| { + const constraint = input.constraint; + const name = input.name; - const ty = func.typeOf(input); - const input_mcv = try func.resolveInst(input); + const ty = func.typeOf(input.operand); + const input_mcv = try func.resolveInst(input.operand); const arg_mcv: MCValue = if (mem.eql(u8, constraint, "X")) input_mcv else if (mem.startsWith(u8, constraint, "{") and mem.endsWith(u8, constraint, "}")) arg: { @@ -6171,7 +6150,7 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { const zcu = func.pt.zcu; const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; const struct_type: Type = .fromInterned(aggregate.ty); switch (aggregate.storage) { .elems => |elems| for (elems, 0..) |elem, i| { @@ -6231,7 +6210,7 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { labels.deinit(func.gpa); } - const asm_source = std.mem.sliceAsBytes(func.air.extra.items[extra_i..])[0..extra.data.source_len]; + const asm_source = unwrapped_asm.source; var line_it = mem.tokenizeAny(u8, asm_source, "\n\r;"); next_line: while (line_it.next()) |line| { var mnem_it = mem.tokenizeAny(u8, line, " \t"); @@ -6499,19 +6478,14 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { while (label_it.next()) |label| if (label.value_ptr.pending_relocs.items.len > 0) return func.fail("undefined label: '{s}'", .{label.key_ptr.*}); - for (outputs, args.items[0..outputs.len]) |output, arg_mcv| { - const extra_bytes = mem.sliceAsBytes(func.air.extra.items[outputs_extra_i..]); - const constraint = - mem.sliceTo(mem.sliceAsBytes(func.air.extra.items[outputs_extra_i..]), 0); - const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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. - outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4; + it = unwrapped_asm.iterateOutputs(); + while (it.next()) |output| { + const constraint = output.constraint; - if (output == .none) continue; - if (arg_mcv != .register) continue; + if (output.operand == .none) continue; + if (args.items[output.index] != .register) continue; if (constraint.len == 2 and std.ascii.isDigit(constraint[1])) continue; - try func.store(.{ .air_ref = output }, arg_mcv, func.typeOf(output)); + try func.store(.{ .air_ref = output.operand }, args.items[output.index], func.typeOf(output.operand)); } simple: { diff --git a/src/codegen/sparc64/CodeGen.zig b/src/codegen/sparc64/CodeGen.zig index b3416dcedfd7a3f0b9f1c9cb5a80e5e26f387d69..a246df12c59f3277001ba28fd671b6c42fe769e7 100644 --- a/src/codegen/sparc64/CodeGen.zig +++ b/src/codegen/sparc64/CodeGen.zig @@ -877,15 +877,10 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { } fn airAsm(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = extra.data.flags.is_volatile; - const outputs_len = extra.data.flags.outputs_len; - var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i .. extra_i + outputs_len]); - extra_i += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i .. extra_i + extra.data.inputs_len]); - extra_i += inputs.len; + const unwrapped_asm = self.air.unwrapAsm(inst); + const is_volatile = unwrapped_asm.is_volatile; + const outputs = unwrapped_asm.outputs; + const inputs = unwrapped_asm.inputs; const dead = !is_volatile and self.liveness.isUnused(inst); const result: MCValue = if (dead) .dead else result: { @@ -893,27 +888,18 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement codegen for asm with more than 1 output", .{}); } - const output_constraint: ?[]const u8 = for (outputs) |output| { - if (output != .none) { + var it = unwrapped_asm.iterateOutputs(); + const output_constraint: ?[]const u8 = while (it.next()) |output| { + if (output.operand != .none) { return self.fail("TODO implement codegen for non-expr asm", .{}); } - const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; - break constraint; + break output.constraint; } else null; - for (inputs) |input| { - const input_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(input_bytes, 0); - const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; + it = unwrapped_asm.iterateInputs(); + while (it.next()) |input| { + const constraint = input.constraint; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); @@ -922,15 +908,15 @@ 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(input); + const arg_mcv = try self.resolveInst(input.operand); try self.register_manager.getReg(reg, null); - try self.genSetReg(self.typeOf(input), reg, arg_mcv); + try self.genSetReg(self.typeOf(input.operand), reg, arg_mcv); } // TODO honor the clobbers - _ = extra.data.clobbers; + _ = unwrapped_asm.clobbers; - const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; + const asm_source = unwrapped_asm.source; if (mem.eql(u8, asm_source, "ta 0x6d")) { _ = try self.addInst(.{ @@ -1109,9 +1095,8 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { } fn airBlock(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Block, ty_pl.payload); - try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len])); + const block = self.air.unwrapBlock(inst); + try self.lowerBlock(inst, block.body); } fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { @@ -1276,11 +1261,9 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch}); - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const callee = pl_op.operand; - const extra = self.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra.end .. extra.end + extra.data.args_len]); - const ty = self.typeOf(callee); + const call = self.air.unwrapCall(inst); + const args = call.args; + const ty = self.typeOf(call.callee); const pt = self.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -1327,7 +1310,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier // Due to incremental compilation, how function calls are generated depends // on linking. - if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { + if (try self.air.value(call.callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { .func => { return self.fail("TODO implement calling functions", .{}); }, @@ -1339,7 +1322,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier }, } else { assert(ty.zigTypeTag(zcu) == .pointer); - const mcv = try self.resolveInst(callee); + const mcv = try self.resolveInst(call.callee); try self.genSetReg(ty, .o7, mcv); _ = try self.addInst(.{ @@ -1365,13 +1348,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (args.len + 1 <= Air.Liveness.bpi - 1) { var buf = [1]Air.Inst.Ref{.none} ** (Air.Liveness.bpi - 1); - buf[0] = callee; + buf[0] = call.callee; @memcpy(buf[1..][0..args.len], args); return self.finishAir(inst, result, buf); } var bt = try self.iterateBigTomb(inst, 1 + args.len); - bt.feed(callee); + bt.feed(call.callee); for (args) |arg| { bt.feed(arg); } @@ -1451,9 +1434,7 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { } fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Block, ty_pl.payload); - _ = extra; + _ = inst; return self.fail("TODO implement airCmpxchg for {}", .{ self.target.cpu.arch, @@ -1461,11 +1442,10 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { } fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const condition = try self.resolveInst(pl_op.operand); - const extra = self.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const cond_br = self.air.unwrapCondBr(inst); + const condition = try self.resolveInst(cond_br.condition); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; const liveness_condbr = self.liveness.getCondBr(inst); // Here we emit a branch to the false section. @@ -1475,7 +1455,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // that death now instead of later as this has an effect on // whether it needs to be spilled in the branches if (self.liveness.operandDies(inst, 0)) { - if (pl_op.operand.toIndex()) |op_index| { + if (cond_br.condition.toIndex()) |op_index| { self.processDeath(op_index); } } @@ -1613,10 +1593,9 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void { } fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + const block = self.air.unwrapDbgBlock(inst); // TODO emit debug info for function change - try self.lowerBlock(inst, @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len])); + try self.lowerBlock(inst, block.body); } fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { @@ -1780,12 +1759,10 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { fn airLoop(self: *Self, inst: Air.Inst.Index) !void { // A loop is a setup to be able to jump back to the beginning. - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const loop = self.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[loop.end .. loop.end + loop.data.body_len]); + const block = self.air.unwrapBlock(inst); const start: u32 = @intCast(self.mir_instructions.len); - try self.genBody(body); + try self.genBody(block.body); try self.jump(start); return self.finishAirBookkeeping(); @@ -2606,12 +2583,11 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { } fn airTry(self: *Self, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); + const unwrapped_try = self.air.unwrapTry(inst); + const body = unwrapped_try.else_body; const result: MCValue = result: { - const error_union_ty = self.typeOf(pl_op.operand); - const error_union = try self.resolveInst(pl_op.operand); + const error_union_ty = self.air.typeOf(unwrapped_try.error_union, &self.pt.zcu.intern_pool); + const error_union = try self.resolveInst(unwrapped_try.error_union); const is_err_result = try self.isErr(error_union_ty, error_union); const reloc = try self.condBr(is_err_result); @@ -2620,7 +2596,7 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void { try self.performReloc(reloc); break :result try self.errUnionPayload(error_union, error_union_ty); }; - return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); + return self.finishAir(inst, result, .{ unwrapped_try.error_union, .none, .none }); } fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index d4227ddff6acb6acd3fa72bdfd22f7bacba07d64..f7212b6e1b1f449368872d27aa3688f12b891aa9 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -5002,9 +5002,8 @@ fn genStructuredBody( } fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) !?Id { - const inst_datas = cg.air.instructions.items(.data); - const extra = cg.air.extraData(Air.Block, inst_datas[@intFromEnum(inst)].ty_pl.payload); - return cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len])); + const block = cg.air.unwrapBlock(inst); + return cg.lowerBlock(inst, block.body); } fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) !?Id { @@ -5188,11 +5187,10 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) !void { fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) !void { const gpa = cg.module.gpa; - const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const cond_br = cg.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[cond_br.end..][0..cond_br.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[cond_br.end + then_body.len ..][0..cond_br.data.else_body_len]); - const condition_id = try cg.resolve(pl_op.operand); + const cond_br = cg.air.unwrapCondBr(inst); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; + const condition_id = try cg.resolve(cond_br.condition); const then_label = cg.module.allocId(); const else_label = cg.module.allocId(); @@ -5251,9 +5249,7 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) !void { fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) !void { const gpa = cg.module.gpa; - const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const loop = cg.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[loop.end..][0..loop.data.body_len]); + const block = cg.air.unwrapBlock(inst); const body_label = cg.module.allocId(); @@ -5284,7 +5280,7 @@ fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) !void { const next_block = try cg.genStructuredBody(.{ .loop = .{ .merge_label = merge_label, .continue_label = continue_label, - } }, body); + } }, block.body); try cg.structuredBreak(next_block); try cg.beginSpvBlock(continue_label); @@ -5294,7 +5290,7 @@ fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) !void { .unstructured => { try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label }); try cg.beginSpvBlock(body_label); - try cg.genBody(body); + try cg.genBody(block.body); try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label }); }, @@ -5375,12 +5371,11 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) !void { fn airTry(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const gpa = cg.module.gpa; const zcu = cg.module.zcu; - const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const err_union_id = try cg.resolve(pl_op.operand); - const extra = cg.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len]); + const unwrapped_try = cg.air.unwrapTry(inst); + const body = unwrapped_try.else_body; - const err_union_ty = cg.typeOf(pl_op.operand); + const err_union_id = try cg.resolve(unwrapped_try.error_union); + const err_union_ty = cg.air.typeOf(unwrapped_try.error_union, &zcu.intern_pool); const payload_ty = cg.typeOfIndex(inst); const bool_ty_id = try cg.resolveType(.bool, .direct); @@ -5882,12 +5877,11 @@ fn airDbgStmt(cg: *CodeGen, inst: Air.Inst.Index) !void { fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const zcu = cg.module.zcu; - const inst_datas = cg.air.instructions.items(.data); - const extra = cg.air.extraData(Air.DbgInlineBlock, inst_datas[@intFromEnum(inst)].ty_pl.payload); + const block = cg.air.unwrapDbgBlock(inst); const old_base_line = cg.base_line; defer cg.base_line = old_base_line; - cg.base_line = zcu.navSrcLine(zcu.funcInfo(extra.data.func).owner_nav); - return cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len])); + cg.base_line = zcu.navSrcLine(zcu.funcInfo(block.func).owner_nav); + return cg.lowerBlock(inst, block.body); } fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void { @@ -5900,52 +5894,34 @@ fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void { fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const gpa = cg.module.gpa; const zcu = cg.module.zcu; - const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = cg.air.extraData(Air.Asm, ty_pl.payload); + const unwrapped_asm = cg.air.unwrapAsm(inst); - const is_volatile = extra.data.flags.is_volatile; - const outputs_len = extra.data.flags.outputs_len; + const is_volatile = unwrapped_asm.is_volatile; + const outputs_len = unwrapped_asm.outputs.len; if (!is_volatile and cg.liveness.isUnused(inst)) return null; - var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(cg.air.extra.items[extra_i..][0..outputs_len]); - extra_i += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(cg.air.extra.items[extra_i..][0..extra.data.inputs_len]); - extra_i += inputs.len; - - if (outputs.len > 1) { + if (outputs_len > 1) { return cg.todo("implement inline asm with more than 1 output", .{}); } var ass: Assembler = .{ .cg = cg }; defer ass.deinit(); - var output_extra_i = extra_i; - for (outputs) |output| { - if (output != .none) { + var it = unwrapped_asm.iterateOutputs(); + while (it.next()) |out| { + if (out.operand != .none) { return cg.todo("implement inline asm with non-returned output", .{}); } - const extra_bytes = std.mem.sliceAsBytes(cg.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(cg.air.extra.items[extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - // TODO: Record output and use it somewhere. } - for (inputs) |input| { - const extra_bytes = std.mem.sliceAsBytes(cg.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; + it = unwrapped_asm.iterateInputs(); + while (it.next()) |in| { + const input_ty = cg.typeOf(in.operand); - const input_ty = cg.typeOf(input); - - if (std.mem.eql(u8, constraint, "c")) { + if (std.mem.eql(u8, in.constraint, "c")) { // constant - const val = (try cg.air.value(input, cg.pt)) orelse { + const val = (try cg.air.value(in.operand, cg.pt)) orelse { return cg.fail("assembly inputs with 'c' constraint have to be compile-time known", .{}); }; @@ -5971,37 +5947,36 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { .undef => return cg.fail("assembly input with 'c' constraint cannot be undefined", .{}), - .int => try ass.value_map.put(gpa, name, .{ .constant = @intCast(val.toUnsignedInt(zcu)) }), - .enum_literal => |str| try ass.value_map.put(gpa, name, .{ .string = str.toSlice(ip) }), + .int => try ass.value_map.put(gpa, in.name, .{ .constant = @intCast(val.toUnsignedInt(zcu)) }), + .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }), else => unreachable, // TODO } - } else if (std.mem.eql(u8, constraint, "t")) { + } else if (std.mem.eql(u8, in.constraint, "t")) { // type if (input_ty.zigTypeTag(zcu) == .type) { // This assembly input is a type instead of a value. // That's fine for now, just make sure to resolve it as such. - const val = (try cg.air.value(input, cg.pt)).?; + const val = (try cg.air.value(in.operand, cg.pt)).?; const ty_id = try cg.resolveType(val.toType(), .direct); - try ass.value_map.put(gpa, name, .{ .ty = ty_id }); + try ass.value_map.put(gpa, in.name, .{ .ty = ty_id }); } else { const ty_id = try cg.resolveType(input_ty, .direct); - try ass.value_map.put(gpa, name, .{ .ty = ty_id }); + try ass.value_map.put(gpa, in.name, .{ .ty = ty_id }); } } else { if (input_ty.zigTypeTag(zcu) == .type) { return cg.fail("use the 't' constraint to supply types to SPIR-V inline assembly", .{}); } - const val_id = try cg.resolve(input); - try ass.value_map.put(gpa, name, .{ .value = val_id }); + const val_id = try cg.resolve(in.operand); + try ass.value_map.put(gpa, in.name, .{ .value = val_id }); } } - // TODO: do something with clobbers - _ = extra.data.clobbers; + _ = unwrapped_asm.clobbers; - const asm_source = std.mem.sliceAsBytes(cg.air.extra.items[extra_i..])[0..extra.data.source_len]; + const asm_source = unwrapped_asm.source; ass.assemble(asm_source) catch |err| switch (err) { error.AssembleFail => { @@ -6033,26 +6008,20 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { else => |others| return others, }; - for (outputs) |output| { - _ = output; - const extra_bytes = std.mem.sliceAsBytes(cg.air.extra.items[output_extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(cg.air.extra.items[output_extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - output_extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - const result = ass.value_map.get(name) orelse return { - return cg.fail("invalid asm output '{s}'", .{name}); + it = unwrapped_asm.iterateOutputs(); + while (it.next()) |out| { + const result = ass.value_map.get(out.name) orelse return { + return cg.fail("invalid asm output '{s}'", .{out.name}); }; - switch (result) { .just_declared, .unresolved_forward_reference => unreachable, .ty => return cg.fail("cannot return spir-v type as value from assembly", .{}), .value => |ref| return ref, .constant, .string => return cg.fail("cannot return constant from assembly", .{}), } - // TODO: Multiple results // TODO: Check that the output type from assembly is the same as the type actually expected by Zig. + } return null; @@ -6063,10 +6032,9 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie const gpa = cg.module.gpa; const zcu = cg.module.zcu; - const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = cg.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.args_len]); - const callee_ty = cg.typeOf(pl_op.operand); + const air_call = cg.air.unwrapCall(inst); + const args = air_call.args; + const callee_ty = cg.typeOf(air_call.callee); const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) { .@"fn" => callee_ty, .pointer => return cg.fail("cannot call function pointers", .{}), @@ -6077,7 +6045,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie const result_type_id = try cg.resolveFnReturnType(.fromInterned(return_type)); const result_id = cg.module.allocId(); - const callee_id = try cg.resolve(pl_op.operand); + const callee_id = try cg.resolve(air_call.callee); comptime assert(zig_call_abi_ver == 3); diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig index b6f8145875b6abe0f63dee3eecf0d791295ba3fc..90de3461cacbfdb90b07a3f2898598ed27be9ac7 100644 --- a/src/codegen/wasm/CodeGen.zig +++ b/src/codegen/wasm/CodeGen.zig @@ -2137,10 +2137,9 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{}); - const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = cg.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.args_len]); - const ty = cg.typeOf(pl_op.operand); + const call = cg.air.unwrapCall(inst); + const args = call.args; + const ty = cg.typeOf(call.callee); const pt = cg.pt; const zcu = pt.zcu; @@ -2155,7 +2154,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie const first_param_sret = firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target); const callee: ?InternPool.Nav.Index = blk: { - const func_val = (try cg.air.value(pl_op.operand, pt)) orelse break :blk null; + const func_val = (try cg.air.value(call.callee, pt)) orelse break :blk null; switch (ip.indexToKey(func_val.toIntern())) { inline .func, .@"extern" => |x| break :blk x.owner_nav, @@ -2189,7 +2188,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie // in this case we call a function pointer // so load its value onto the stack assert(ty.zigTypeTag(zcu) == .pointer); - const operand = try cg.resolveInst(pl_op.operand); + const operand = try cg.resolveInst(call.callee); try cg.emitWValue(operand); try cg.mir_func_tys.put(cg.gpa, fn_ty.toIntern(), {}); @@ -2233,7 +2232,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie }; var bt = try cg.iterateBigTomb(inst, 1 + args.len); - bt.feed(pl_op.operand); + bt.feed(call.callee); for (args) |arg| bt.feed(arg); return bt.finishAir(result_value); } @@ -3335,9 +3334,8 @@ fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { } fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = cg.air.extraData(Air.Block, ty_pl.payload); - try cg.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len])); + const block = cg.air.unwrapBlock(inst); + try cg.lowerBlock(inst, block.ty, block.body); } fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void { @@ -3381,9 +3379,7 @@ fn endBlock(cg: *CodeGen) !void { } fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const loop = cg.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[loop.end..][0..loop.data.body_len]); + const block = cg.air.unwrapBlock(inst); // result type of loop is always 'noreturn', meaning we can always // emit the wasm type 'block_empty'. @@ -3392,18 +3388,17 @@ fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { try cg.loops.putNoClobber(cg.gpa, inst, cg.block_depth); defer assert(cg.loops.remove(inst)); - try cg.genBody(body); + try cg.genBody(block.body); try cg.endBlock(); return cg.finishAir(inst, .none, &.{}); } fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const condition = try cg.resolveInst(pl_op.operand); - const extra = cg.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const cond_br = cg.air.unwrapCondBr(inst); + const condition = try cg.resolveInst(cond_br.condition); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; const liveness_condbr = cg.liveness.getCondBr(inst); // result type is always noreturn, so use `block_empty` as type. @@ -6423,10 +6418,9 @@ fn airDbgStmt(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = cg.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + const block = cg.air.unwrapDbgBlock(inst); // TODO - try cg.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len])); + try cg.lowerBlock(inst, block.ty, block.body); } fn airDbgVar( @@ -6441,24 +6435,22 @@ fn airDbgVar( } fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const err_union = try cg.resolveInst(pl_op.operand); - const extra = cg.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len]); - const err_union_ty = cg.typeOf(pl_op.operand); + const unwrapped_try = cg.air.unwrapTry(inst); + const body = unwrapped_try.else_body; + const err_union = try cg.resolveInst(unwrapped_try.error_union); + const err_union_ty = cg.typeOf(unwrapped_try.error_union); const result = try lowerTry(cg, inst, err_union, body, err_union_ty, false); - return cg.finishAir(inst, result, &.{pl_op.operand}); + return cg.finishAir(inst, result, &.{unwrapped_try.error_union}); } fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const zcu = cg.pt.zcu; - const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = cg.air.extraData(Air.TryPtr, ty_pl.payload); - const err_union_ptr = try cg.resolveInst(extra.data.ptr); - const body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len]); - const err_union_ty = cg.typeOf(extra.data.ptr).childType(zcu); + const unwrapped_try = cg.air.unwrapTryPtr(inst); + const err_union_ptr = try cg.resolveInst(unwrapped_try.error_union_ptr); + const body = unwrapped_try.else_body; + const err_union_ty = cg.typeOf(unwrapped_try.error_union_ptr).childType(zcu); const result = try lowerTry(cg, inst, err_union_ptr, body, err_union_ty, true); - return cg.finishAir(inst, result, &.{extra.data.ptr}); + return cg.finishAir(inst, result, &.{unwrapped_try.error_union_ptr}); } fn lowerTry( diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index 382c9e9c3cb1ff24aa64abe5572cd12779dcca00..7da423f4299b1d791ed32d39eca8b28ab2874d7f 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -67348,21 +67348,19 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }, .bitcast => try cg.airBitCast(inst), .block => { - const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; - const block = cg.air.extraData(Air.Block, ty_pl.payload); + const block = cg.air.unwrapBlock(inst); if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_enter_block_none); - try cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[block.end..][0..block.data.body_len])); + try cg.lowerBlock(inst, block.body); if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_leave_block_none); }, .loop => { - const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; - const block = cg.air.extraData(Air.Block, ty_pl.payload); + const block = cg.air.unwrapBlock(inst); try cg.loops.putNoClobber(cg.gpa, inst, .{ .state = try cg.saveState(), .target = @intCast(cg.mir_instructions.len), }); defer assert(cg.loops.remove(inst)); - try cg.genBodyBlock(@ptrCast(cg.air.extra.items[block.end..][0..block.data.body_len])); + try cg.genBodyBlock(block.body); }, .repeat => { const repeat = air_datas[@intFromEnum(inst)].repeat; @@ -89048,17 +89046,16 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { try cg.asmOpOnly(.{ ._, .nop }); }, .dbg_inline_block => { - const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; - const dbg_inline_block = cg.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + const dbg_inline_block = cg.air.unwrapDbgBlock(inst); const old_inline_func = cg.inline_func; defer cg.inline_func = old_inline_func; - cg.inline_func = dbg_inline_block.data.func; + cg.inline_func = dbg_inline_block.func; if (!cg.mod.strip) _ = try cg.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_enter_inline_func, - .data = .{ .ip_index = dbg_inline_block.data.func }, + .data = .{ .ip_index = dbg_inline_block.func }, }); - try cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[dbg_inline_block.end..][0..dbg_inline_block.data.body_len])); + try cg.lowerBlock(inst, dbg_inline_block.body); if (!cg.mod.strip) _ = try cg.addInst(.{ .tag = .pseudo, .ops = .pseudo_dbg_leave_inline_func, @@ -175916,10 +175913,8 @@ fn genLocalDebugInfo(cg: *CodeGen, air_tag: Air.Inst.Tag, ty: Type, mcv: MCValue fn airCall(self: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier, opts: CopyOptions) !void { if (modifier == .always_tail) return self.fail("TODO implement tail calls for x86_64", .{}); - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Call, pl_op.payload); - const arg_refs: []const Air.Inst.Ref = - @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]); + const call = self.air.unwrapCall(inst); + const arg_refs = call.args; const ExpectedContents = extern struct { tys: [32][@sizeOf(Type)]u8 align(@alignOf(Type)), @@ -175937,10 +175932,10 @@ fn airCall(self: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif defer allocator.free(arg_vals); for (arg_vals, arg_refs) |*arg_val, arg_ref| arg_val.* = .{ .air_ref = arg_ref }; - const ret = try self.genCall(.{ .air = pl_op.operand }, arg_tys, arg_vals, opts); + const ret = try self.genCall(.{ .air = call.callee }, arg_tys, arg_vals, opts); var bt = self.liveness.iterateBigTomb(inst); - try self.feed(&bt, pl_op.operand); + try self.feed(&bt, call.callee); for (arg_refs) |arg_ref| try self.feed(&bt, arg_ref); const result = if (self.liveness.isUnused(inst)) .unreach else ret; @@ -176300,20 +176295,18 @@ fn airRetLoad(self: *CodeGen, inst: Air.Inst.Index) !void { } fn airTry(self: *CodeGen, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = self.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); - const operand_ty = self.typeOf(pl_op.operand); - const result = try self.genTry(inst, pl_op.operand, body, operand_ty, false); + const unwrapped_try = self.air.unwrapTry(inst); + const body = unwrapped_try.else_body; + const operand_ty = self.typeOf(unwrapped_try.error_union); + const result = try self.genTry(inst, unwrapped_try.error_union, body, operand_ty, false); return self.finishAir(inst, result, .{ .none, .none, .none }); } fn airTryPtr(self: *CodeGen, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(self.air.extra.items[extra.end..][0..extra.data.body_len]); - const operand_ty = self.typeOf(extra.data.ptr); - const result = try self.genTry(inst, extra.data.ptr, body, operand_ty, true); + const unwrapped_try = self.air.unwrapTryPtr(inst); + const body = unwrapped_try.else_body; + const operand_ty = self.typeOf(unwrapped_try.error_union_ptr); + const result = try self.genTry(inst, unwrapped_try.error_union_ptr, body, operand_ty, true); return self.finishAir(inst, result, .{ .none, .none, .none }); } @@ -176391,21 +176384,20 @@ fn genCondBrMir(self: *CodeGen, ty: Type, mcv: MCValue) !Mir.Inst.Index { } fn airCondBr(self: *CodeGen, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const cond = try self.resolveInst(pl_op.operand); - const cond_ty = self.typeOf(pl_op.operand); - const extra = self.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = - @ptrCast(self.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = - @ptrCast(self.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const cond_br = self.air.unwrapCondBr(inst); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; + + const cond = try self.resolveInst(cond_br.condition); + const cond_ty = self.typeOf(cond_br.condition); + const liveness_cond_br = self.liveness.getCondBr(inst); // If the condition dies here in this condbr instruction, process // that death now instead of later as this has an effect on // whether it needs to be spilled in the branches if (self.liveness.operandDies(inst, 0)) { - if (pl_op.operand.toIndex()) |op_inst| try self.processDeath(op_inst, .{}); + if (cond_br.condition.toIndex()) |op_inst| try self.processDeath(op_inst, .{}); } const state = try self.saveState(); @@ -177124,14 +177116,10 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { @setEvalBranchQuota(1_100); const pt = self.pt; const zcu = pt.zcu; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const outputs_len = extra.data.flags.outputs_len; - var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..outputs_len]); - extra_i += outputs.len; - const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); - extra_i += inputs.len; + const unwrapped_asm = self.air.unwrapAsm(inst); + + const outputs = unwrapped_asm.outputs; + const inputs = unwrapped_asm.inputs; var result: MCValue = .none; var args: std.array_list.Managed(MCValue) = .init(self.gpa); @@ -177146,36 +177134,29 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { try arg_map.ensureTotalCapacity(@intCast(outputs.len + inputs.len)); defer arg_map.deinit(); - var outputs_extra_i = extra_i; - for (outputs) |output| { - const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; - - const maybe_inst = switch (output) { + var it = unwrapped_asm.iterateOutputs(); + while (it.next()) |out| { + const maybe_inst = switch (out.operand) { .none => inst, else => null, }; - const ty = switch (output) { + const ty = switch (out.operand) { .none => self.typeOfIndex(inst), - else => self.typeOf(output).childType(zcu), + else => self.typeOf(out.operand).childType(zcu), }; - const is_read = switch (constraint[0]) { + const is_read = switch (out.constraint[0]) { '=' => false, '+' => read: { - if (output == .none) return self.fail( + if (out.operand == .none) return self.fail( "read-write constraint unsupported for asm result: '{s}'", - .{constraint}, + .{out.constraint}, ); break :read true; }, - else => return self.fail("invalid constraint: '{s}'", .{constraint}), + else => return self.fail("invalid constraint: '{s}'", .{out.constraint}), }; - const is_early_clobber = constraint[1] == '&'; - const rest = constraint[@as(usize, 1) + @intFromBool(is_early_clobber) ..]; + const is_early_clobber = out.constraint[1] == '&'; + const rest = out.constraint[@as(usize, 1) + @intFromBool(is_early_clobber) ..]; const arg_mcv: MCValue = arg_mcv: { const arg_maybe_reg: ?Register = if (std.mem.eql(u8, rest, "r") or std.mem.eql(u8, rest, "f") or std.mem.eql(u8, rest, "x")) @@ -177189,30 +177170,30 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { @intCast(ty.abiSize(zcu)), ) else if (std.mem.eql(u8, rest, "m")) - if (output != .none) null else return self.fail( + if (out.operand != .none) null else return self.fail( "memory constraint unsupported for asm result: '{s}'", - .{constraint}, + .{out.constraint}, ) else if (std.mem.eql(u8, rest, "g") or std.mem.eql(u8, rest, "rm") or std.mem.eql(u8, rest, "mr") or std.mem.eql(u8, rest, "r,m") or std.mem.eql(u8, rest, "m,r")) self.register_manager.tryAllocReg(maybe_inst, abi.RegisterClass.gp) orelse - if (output != .none) + if (out.operand != .none) null else return self.fail("ran out of registers lowering inline asm", .{}) else if (std.mem.startsWith(u8, rest, "{") and std.mem.endsWith(u8, rest, "}")) parseRegName(rest["{".len .. rest.len - "}".len]) orelse - return self.fail("invalid register constraint: '{s}'", .{constraint}) + return self.fail("invalid register constraint: '{s}'", .{out.constraint}) else if (rest.len == 1 and std.ascii.isDigit(rest[0])) { const index = std.fmt.charToDigit(rest[0], 10) catch unreachable; if (index >= args.items.len) return self.fail("constraint out of bounds: '{s}'", .{ - constraint, + out.constraint, }); break :arg_mcv args.items[index]; - } else return self.fail("invalid constraint: '{s}'", .{constraint}); + } else return self.fail("invalid constraint: '{s}'", .{out.constraint}); break :arg_mcv if (arg_maybe_reg) |reg| .{ .register = reg } else arg: { - const ptr_mcv = try self.resolveInst(output); + const ptr_mcv = try self.resolveInst(out.operand); switch (ptr_mcv) { .immediate => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |_| break :arg ptr_mcv.deref(), @@ -177223,30 +177204,24 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { }; }; if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |tracked_index| { - try self.register_manager.getRegIndex(tracked_index, if (output == .none) inst else null); + try self.register_manager.getRegIndex(tracked_index, if (out.operand == .none) inst else null); _ = self.register_manager.lockRegIndexAssumeUnused(tracked_index); }; - if (!std.mem.eql(u8, name, "_")) - arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); + if (!std.mem.eql(u8, out.name, "_")) + arg_map.putAssumeCapacityNoClobber(out.name, @intCast(args.items.len)); args.appendAssumeCapacity(arg_mcv); - if (output == .none) result = arg_mcv; - if (is_read) try self.load(arg_mcv, self.typeOf(output), .{ .air_ref = output }); + if (out.operand == .none) result = arg_mcv; + if (is_read) try self.load(arg_mcv, self.typeOf(out.operand), .{ .air_ref = out.operand }); } - for (inputs) |input| { - const input_bytes = std.mem.sliceAsBytes(self.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(input_bytes, 0); - const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 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 + name.len + (2 + 3)) / 4; - - const ty = self.typeOf(input); - const input_mcv = try self.resolveInst(input); - const arg_mcv: MCValue = if (std.mem.eql(u8, constraint, "r") or - std.mem.eql(u8, constraint, "f") or std.mem.eql(u8, constraint, "x")) + it = unwrapped_asm.iterateInputs(); + while (it.next()) |in| { + const ty = self.typeOf(in.operand); + const input_mcv = try self.resolveInst(in.operand); + const arg_mcv: MCValue = if (std.mem.eql(u8, in.constraint, "r") or + std.mem.eql(u8, in.constraint, "f") or std.mem.eql(u8, in.constraint, "x")) arg: { - const rc = switch (constraint[0]) { + const rc = switch (in.constraint[0]) { 'r' => abi.RegisterClass.gp, 'f' => abi.RegisterClass.x87, 'x' => abi.RegisterClass.sse, @@ -177258,14 +177233,14 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { const reg = try self.register_manager.allocReg(null, rc); try self.genSetReg(reg, ty, input_mcv, .{}); break :arg .{ .register = registerAlias(reg, @intCast(ty.abiSize(zcu))) }; - } else if (std.mem.eql(u8, constraint, "i") or std.mem.eql(u8, constraint, "n")) + } else if (std.mem.eql(u8, in.constraint, "i") or std.mem.eql(u8, in.constraint, "n")) switch (input_mcv) { .immediate => |imm| .{ .immediate = imm }, else => return self.fail("immediate operand requires comptime value: '{s}'", .{ - constraint, + in.constraint, }), } - else if (std.mem.eql(u8, constraint, "m")) arg: { + else if (std.mem.eql(u8, in.constraint, "m")) arg: { switch (input_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |_| break :arg input_mcv, @@ -177284,9 +177259,9 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { }; try self.genSetReg(addr_reg, .usize, input_mcv.address(), .{}); break :arg .{ .indirect = .{ .reg = addr_reg } }; - } else if (std.mem.eql(u8, constraint, "g") or - std.mem.eql(u8, constraint, "rm") or std.mem.eql(u8, constraint, "mr") or - std.mem.eql(u8, constraint, "r,m") or std.mem.eql(u8, constraint, "m,r")) + } else if (std.mem.eql(u8, in.constraint, "g") or + std.mem.eql(u8, in.constraint, "rm") or std.mem.eql(u8, in.constraint, "mr") or + std.mem.eql(u8, in.constraint, "r,m") or std.mem.eql(u8, in.constraint, "m,r")) arg: { switch (input_mcv) { .register, .indirect, .load_frame => break :arg input_mcv, @@ -177297,30 +177272,30 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { const temp_mcv = try self.allocTempRegOrMem(ty, true); try self.genCopy(ty, temp_mcv, input_mcv, .{}); break :arg temp_mcv; - } else if (std.mem.eql(u8, constraint, "X")) + } else if (std.mem.eql(u8, in.constraint, "X")) input_mcv - else if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) arg: { - const reg = parseRegName(constraint["{".len .. constraint.len - "}".len]) orelse - return self.fail("invalid register constraint: '{s}'", .{constraint}); + else if (std.mem.startsWith(u8, in.constraint, "{") and std.mem.endsWith(u8, in.constraint, "}")) arg: { + const reg = parseRegName(in.constraint["{".len .. in.constraint.len - "}".len]) orelse + return self.fail("invalid register constraint: '{s}'", .{in.constraint}); try self.register_manager.getReg(reg, null); try self.genSetReg(reg, ty, input_mcv, .{}); break :arg .{ .register = reg }; - } else if (constraint.len == 1 and std.ascii.isDigit(constraint[0])) arg: { - const index = std.fmt.charToDigit(constraint[0], 10) catch unreachable; - if (index >= args.items.len) return self.fail("constraint out of bounds: '{s}'", .{constraint}); + } else if (in.constraint.len == 1 and std.ascii.isDigit(in.constraint[0])) arg: { + const index = std.fmt.charToDigit(in.constraint[0], 10) catch unreachable; + if (index >= args.items.len) return self.fail("constraint out of bounds: '{s}'", .{in.constraint}); try self.genCopy(ty, args.items[index], input_mcv, .{}); break :arg args.items[index]; - } else return self.fail("invalid constraint: '{s}'", .{constraint}); + } else return self.fail("invalid constraint: '{s}'", .{in.constraint}); if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { _ = self.register_manager.lockReg(reg); }; - if (!std.mem.eql(u8, name, "_")) - arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); + if (!std.mem.eql(u8, in.name, "_")) + arg_map.putAssumeCapacityNoClobber(in.name, @intCast(args.items.len)); args.appendAssumeCapacity(arg_mcv); } const ip = &zcu.intern_pool; - const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate; const struct_type: Type = .fromInterned(aggregate.ty); switch (aggregate.storage) { .elems => |elems| for (elems, 0..) |elem, i| switch (elem) { @@ -177390,7 +177365,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { labels.deinit(self.gpa); } - const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; + const asm_source = unwrapped_asm.source; var line_it = std.mem.tokenizeAny(u8, asm_source, "\n\r;"); next_line: while (line_it.next()) |line| { var mnem_it = std.mem.tokenizeAny(u8, line, " \t"); @@ -177821,19 +177796,13 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { while (label_it.next()) |label| if (label.value_ptr.pending_relocs.items.len > 0) return self.fail("undefined label: '{s}'", .{label.key_ptr.*}); - for (outputs, args.items[0..outputs.len]) |output, arg_mcv| { - const extra_bytes = std.mem.sliceAsBytes(self.air.extra.items[outputs_extra_i..]); - const constraint = - std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[outputs_extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 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. - outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (output == .none) continue; + it = unwrapped_asm.iterateOutputs(); + while (it.next()) |out| { + const arg_mcv = args.items[it.current - 1]; + if (out.operand == .none) continue; if (arg_mcv != .register) continue; - if (constraint.len == 2 and std.ascii.isDigit(constraint[1])) continue; - try self.store(self.typeOf(output), .{ .air_ref = output }, arg_mcv, .{}); + if (out.constraint.len == 2 and std.ascii.isDigit(out.constraint[1])) continue; + try self.store(self.typeOf(out.operand), .{ .air_ref = out.operand }, arg_mcv, .{}); } simple: { -- 2.54.0