aboutsummaryrefslogtreecommitdiff
path: root/src/arch
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/aarch64/bits.zig2063
-rw-r--r--src/arch/riscv64/CodeGen.zig10
-rw-r--r--src/arch/sparc64/CodeGen.zig4
-rw-r--r--src/arch/wasm/CodeGen.zig31
-rw-r--r--src/arch/x86_64/CodeGen.zig108
-rw-r--r--src/arch/x86_64/Emit.zig11
6 files changed, 80 insertions, 2147 deletions
diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig
deleted file mode 100644
index 9c4227a712..0000000000
--- a/src/arch/aarch64/bits.zig
+++ /dev/null
@@ -1,2063 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-const assert = std.debug.assert;
-const testing = std.testing;
-
-/// Disjoint sets of registers. Every register must belong to
-/// exactly one register class.
-pub const RegisterClass = enum {
- general_purpose,
- stack_pointer,
- floating_point,
-};
-
-/// Registers in the AArch64 instruction set
-pub const Register = enum(u8) {
- // zig fmt: off
- // 64-bit general-purpose registers
- x0, x1, x2, x3, x4, x5, x6, x7,
- x8, x9, x10, x11, x12, x13, x14, x15,
- x16, x17, x18, x19, x20, x21, x22, x23,
- x24, x25, x26, x27, x28, x29, x30, xzr,
-
- // 32-bit general-purpose registers
- w0, w1, w2, w3, w4, w5, w6, w7,
- w8, w9, w10, w11, w12, w13, w14, w15,
- w16, w17, w18, w19, w20, w21, w22, w23,
- w24, w25, w26, w27, w28, w29, w30, wzr,
-
- // Stack pointer
- sp, wsp,
-
- // 128-bit floating-point registers
- q0, q1, q2, q3, q4, q5, q6, q7,
- q8, q9, q10, q11, q12, q13, q14, q15,
- q16, q17, q18, q19, q20, q21, q22, q23,
- q24, q25, q26, q27, q28, q29, q30, q31,
-
- // 64-bit floating-point registers
- d0, d1, d2, d3, d4, d5, d6, d7,
- d8, d9, d10, d11, d12, d13, d14, d15,
- d16, d17, d18, d19, d20, d21, d22, d23,
- d24, d25, d26, d27, d28, d29, d30, d31,
-
- // 32-bit floating-point registers
- s0, s1, s2, s3, s4, s5, s6, s7,
- s8, s9, s10, s11, s12, s13, s14, s15,
- s16, s17, s18, s19, s20, s21, s22, s23,
- s24, s25, s26, s27, s28, s29, s30, s31,
-
- // 16-bit floating-point registers
- h0, h1, h2, h3, h4, h5, h6, h7,
- h8, h9, h10, h11, h12, h13, h14, h15,
- h16, h17, h18, h19, h20, h21, h22, h23,
- h24, h25, h26, h27, h28, h29, h30, h31,
-
- // 8-bit floating-point registers
- b0, b1, b2, b3, b4, b5, b6, b7,
- b8, b9, b10, b11, b12, b13, b14, b15,
- b16, b17, b18, b19, b20, b21, b22, b23,
- b24, b25, b26, b27, b28, b29, b30, b31,
- // zig fmt: on
-
- pub fn class(self: Register) RegisterClass {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => .general_purpose,
- @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => .general_purpose,
-
- @intFromEnum(Register.sp) => .stack_pointer,
- @intFromEnum(Register.wsp) => .stack_pointer,
-
- @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => .floating_point,
- @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => .floating_point,
- @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => .floating_point,
- @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => .floating_point,
- @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => .floating_point,
- else => unreachable,
- };
- }
-
- pub fn id(self: Register) u6 {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.x0))),
- @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.w0))),
-
- @intFromEnum(Register.sp) => 32,
- @intFromEnum(Register.wsp) => 32,
-
- @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.q0) + 33)),
- @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.d0) + 33)),
- @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.s0) + 33)),
- @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.h0) + 33)),
- @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as(u6, @intCast(@intFromEnum(self) - @intFromEnum(Register.b0) + 33)),
- else => unreachable,
- };
- }
-
- pub fn enc(self: Register) u5 {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.x0))),
- @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.w0))),
-
- @intFromEnum(Register.sp) => 31,
- @intFromEnum(Register.wsp) => 31,
-
- @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.q0))),
- @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.d0))),
- @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.s0))),
- @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.h0))),
- @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as(u5, @intCast(@intFromEnum(self) - @intFromEnum(Register.b0))),
- else => unreachable,
- };
- }
-
- /// Returns the bit-width of the register.
- pub fn size(self: Register) u8 {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => 64,
- @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => 32,
-
- @intFromEnum(Register.sp) => 64,
- @intFromEnum(Register.wsp) => 32,
-
- @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => 128,
- @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => 64,
- @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => 32,
- @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => 16,
- @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => 8,
- else => unreachable,
- };
- }
-
- /// Convert from a general-purpose register to its 64 bit alias.
- pub fn toX(self: Register) Register {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.x0) + @intFromEnum(Register.x0)),
- ),
- @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.w0) + @intFromEnum(Register.x0)),
- ),
- else => unreachable,
- };
- }
-
- /// Convert from a general-purpose register to its 32 bit alias.
- pub fn toW(self: Register) Register {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.x0)...@intFromEnum(Register.xzr) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.x0) + @intFromEnum(Register.w0)),
- ),
- @intFromEnum(Register.w0)...@intFromEnum(Register.wzr) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.w0) + @intFromEnum(Register.w0)),
- ),
- else => unreachable,
- };
- }
-
- /// Convert from a floating-point register to its 128 bit alias.
- pub fn toQ(self: Register) Register {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.q0)),
- ),
- @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.q0)),
- ),
- @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.q0)),
- ),
- @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.q0)),
- ),
- @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.q0)),
- ),
- else => unreachable,
- };
- }
-
- /// Convert from a floating-point register to its 64 bit alias.
- pub fn toD(self: Register) Register {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.d0)),
- ),
- @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.d0)),
- ),
- @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.d0)),
- ),
- @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.d0)),
- ),
- @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.d0)),
- ),
- else => unreachable,
- };
- }
-
- /// Convert from a floating-point register to its 32 bit alias.
- pub fn toS(self: Register) Register {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.s0)),
- ),
- @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.s0)),
- ),
- @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.s0)),
- ),
- @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.s0)),
- ),
- @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.s0)),
- ),
- else => unreachable,
- };
- }
-
- /// Convert from a floating-point register to its 16 bit alias.
- pub fn toH(self: Register) Register {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.h0)),
- ),
- @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.h0)),
- ),
- @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.h0)),
- ),
- @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.h0)),
- ),
- @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.h0)),
- ),
- else => unreachable,
- };
- }
-
- /// Convert from a floating-point register to its 8 bit alias.
- pub fn toB(self: Register) Register {
- return switch (@intFromEnum(self)) {
- @intFromEnum(Register.q0)...@intFromEnum(Register.q31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.q0) + @intFromEnum(Register.b0)),
- ),
- @intFromEnum(Register.d0)...@intFromEnum(Register.d31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.d0) + @intFromEnum(Register.b0)),
- ),
- @intFromEnum(Register.s0)...@intFromEnum(Register.s31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.s0) + @intFromEnum(Register.b0)),
- ),
- @intFromEnum(Register.h0)...@intFromEnum(Register.h31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.h0) + @intFromEnum(Register.b0)),
- ),
- @intFromEnum(Register.b0)...@intFromEnum(Register.b31) => @as(
- Register,
- @enumFromInt(@intFromEnum(self) - @intFromEnum(Register.b0) + @intFromEnum(Register.b0)),
- ),
- else => unreachable,
- };
- }
-
- pub fn dwarfNum(self: Register) u5 {
- return self.enc();
- }
-};
-
-test "Register.enc" {
- try testing.expectEqual(@as(u5, 0), Register.x0.enc());
- try testing.expectEqual(@as(u5, 0), Register.w0.enc());
-
- try testing.expectEqual(@as(u5, 31), Register.xzr.enc());
- try testing.expectEqual(@as(u5, 31), Register.wzr.enc());
-
- try testing.expectEqual(@as(u5, 31), Register.sp.enc());
- try testing.expectEqual(@as(u5, 31), Register.sp.enc());
-}
-
-test "Register.size" {
- try testing.expectEqual(@as(u8, 64), Register.x19.size());
- try testing.expectEqual(@as(u8, 32), Register.w3.size());
-}
-
-test "Register.toX/toW" {
- try testing.expectEqual(Register.x0, Register.w0.toX());
- try testing.expectEqual(Register.x0, Register.x0.toX());
-
- try testing.expectEqual(Register.w3, Register.w3.toW());
- try testing.expectEqual(Register.w3, Register.x3.toW());
-}
-
-/// Represents an instruction in the AArch64 instruction set
-pub const Instruction = union(enum) {
- move_wide_immediate: packed struct {
- rd: u5,
- imm16: u16,
- hw: u2,
- fixed: u6 = 0b100101,
- opc: u2,
- sf: u1,
- },
- pc_relative_address: packed struct {
- rd: u5,
- immhi: u19,
- fixed: u5 = 0b10000,
- immlo: u2,
- op: u1,
- },
- load_store_register: packed struct {
- rt: u5,
- rn: u5,
- offset: u12,
- opc: u2,
- op1: u2,
- v: u1,
- fixed: u3 = 0b111,
- size: u2,
- },
- load_store_register_pair: packed struct {
- rt1: u5,
- rn: u5,
- rt2: u5,
- imm7: u7,
- load: u1,
- encoding: u2,
- fixed: u5 = 0b101_0_0,
- opc: u2,
- },
- load_literal: packed struct {
- rt: u5,
- imm19: u19,
- fixed: u6 = 0b011_0_00,
- opc: u2,
- },
- exception_generation: packed struct {
- ll: u2,
- op2: u3,
- imm16: u16,
- opc: u3,
- fixed: u8 = 0b1101_0100,
- },
- unconditional_branch_register: packed struct {
- op4: u5,
- rn: u5,
- op3: u6,
- op2: u5,
- opc: u4,
- fixed: u7 = 0b1101_011,
- },
- unconditional_branch_immediate: packed struct {
- imm26: u26,
- fixed: u5 = 0b00101,
- op: u1,
- },
- no_operation: packed struct {
- fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111,
- },
- logical_shifted_register: packed struct {
- rd: u5,
- rn: u5,
- imm6: u6,
- rm: u5,
- n: u1,
- shift: u2,
- fixed: u5 = 0b01010,
- opc: u2,
- sf: u1,
- },
- add_subtract_immediate: packed struct {
- rd: u5,
- rn: u5,
- imm12: u12,
- sh: u1,
- fixed: u6 = 0b100010,
- s: u1,
- op: u1,
- sf: u1,
- },
- logical_immediate: packed struct {
- rd: u5,
- rn: u5,
- imms: u6,
- immr: u6,
- n: u1,
- fixed: u6 = 0b100100,
- opc: u2,
- sf: u1,
- },
- bitfield: packed struct {
- rd: u5,
- rn: u5,
- imms: u6,
- immr: u6,
- n: u1,
- fixed: u6 = 0b100110,
- opc: u2,
- sf: u1,
- },
- add_subtract_shifted_register: packed struct {
- rd: u5,
- rn: u5,
- imm6: u6,
- rm: u5,
- fixed_1: u1 = 0b0,
- shift: u2,
- fixed_2: u5 = 0b01011,
- s: u1,
- op: u1,
- sf: u1,
- },
- add_subtract_extended_register: packed struct {
- rd: u5,
- rn: u5,
- imm3: u3,
- option: u3,
- rm: u5,
- fixed: u8 = 0b01011_00_1,
- s: u1,
- op: u1,
- sf: u1,
- },
- conditional_branch: struct {
- cond: u4,
- o0: u1,
- imm19: u19,
- o1: u1,
- fixed: u7 = 0b0101010,
- },
- compare_and_branch: struct {
- rt: u5,
- imm19: u19,
- op: u1,
- fixed: u6 = 0b011010,
- sf: u1,
- },
- conditional_select: struct {
- rd: u5,
- rn: u5,
- op2: u2,
- cond: u4,
- rm: u5,
- fixed: u8 = 0b11010100,
- s: u1,
- op: u1,
- sf: u1,
- },
- data_processing_3_source: packed struct {
- rd: u5,
- rn: u5,
- ra: u5,
- o0: u1,
- rm: u5,
- op31: u3,
- fixed: u5 = 0b11011,
- op54: u2,
- sf: u1,
- },
- data_processing_2_source: packed struct {
- rd: u5,
- rn: u5,
- opcode: u6,
- rm: u5,
- fixed_1: u8 = 0b11010110,
- s: u1,
- fixed_2: u1 = 0b0,
- sf: u1,
- },
-
- pub const Condition = enum(u4) {
- /// Integer: Equal
- /// Floating point: Equal
- eq,
- /// Integer: Not equal
- /// Floating point: Not equal or unordered
- ne,
- /// Integer: Carry set
- /// Floating point: Greater than, equal, or unordered
- cs,
- /// Integer: Carry clear
- /// Floating point: Less than
- cc,
- /// Integer: Minus, negative
- /// Floating point: Less than
- mi,
- /// Integer: Plus, positive or zero
- /// Floating point: Greater than, equal, or unordered
- pl,
- /// Integer: Overflow
- /// Floating point: Unordered
- vs,
- /// Integer: No overflow
- /// Floating point: Ordered
- vc,
- /// Integer: Unsigned higher
- /// Floating point: Greater than, or unordered
- hi,
- /// Integer: Unsigned lower or same
- /// Floating point: Less than or equal
- ls,
- /// Integer: Signed greater than or equal
- /// Floating point: Greater than or equal
- ge,
- /// Integer: Signed less than
- /// Floating point: Less than, or unordered
- lt,
- /// Integer: Signed greater than
- /// Floating point: Greater than
- gt,
- /// Integer: Signed less than or equal
- /// Floating point: Less than, equal, or unordered
- le,
- /// Integer: Always
- /// Floating point: Always
- al,
- /// Integer: Always
- /// Floating point: Always
- nv,
-
- /// Converts a std.math.CompareOperator into a condition flag,
- /// i.e. returns the condition that is true iff the result of the
- /// comparison is true. Assumes signed comparison
- pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
- return switch (op) {
- .gte => .ge,
- .gt => .gt,
- .neq => .ne,
- .lt => .lt,
- .lte => .le,
- .eq => .eq,
- };
- }
-
- /// Converts a std.math.CompareOperator into a condition flag,
- /// i.e. returns the condition that is true iff the result of the
- /// comparison is true. Assumes unsigned comparison
- pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
- return switch (op) {
- .gte => .cs,
- .gt => .hi,
- .neq => .ne,
- .lt => .cc,
- .lte => .ls,
- .eq => .eq,
- };
- }
-
- /// Returns the condition which is true iff the given condition is
- /// false (if such a condition exists)
- pub fn negate(cond: Condition) Condition {
- return switch (cond) {
- .eq => .ne,
- .ne => .eq,
- .cs => .cc,
- .cc => .cs,
- .mi => .pl,
- .pl => .mi,
- .vs => .vc,
- .vc => .vs,
- .hi => .ls,
- .ls => .hi,
- .ge => .lt,
- .lt => .ge,
- .gt => .le,
- .le => .gt,
- .al => unreachable,
- .nv => unreachable,
- };
- }
- };
-
- pub fn toU32(self: Instruction) u32 {
- return switch (self) {
- .move_wide_immediate => |v| @as(u32, @bitCast(v)),
- .pc_relative_address => |v| @as(u32, @bitCast(v)),
- .load_store_register => |v| @as(u32, @bitCast(v)),
- .load_store_register_pair => |v| @as(u32, @bitCast(v)),
- .load_literal => |v| @as(u32, @bitCast(v)),
- .exception_generation => |v| @as(u32, @bitCast(v)),
- .unconditional_branch_register => |v| @as(u32, @bitCast(v)),
- .unconditional_branch_immediate => |v| @as(u32, @bitCast(v)),
- .no_operation => |v| @as(u32, @bitCast(v)),
- .logical_shifted_register => |v| @as(u32, @bitCast(v)),
- .add_subtract_immediate => |v| @as(u32, @bitCast(v)),
- .logical_immediate => |v| @as(u32, @bitCast(v)),
- .bitfield => |v| @as(u32, @bitCast(v)),
- .add_subtract_shifted_register => |v| @as(u32, @bitCast(v)),
- .add_subtract_extended_register => |v| @as(u32, @bitCast(v)),
- // TODO once packed structs work, this can be refactored
- .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25),
- .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31),
- .conditional_select => |v| @as(u32, v.rd) | @as(u32, v.rn) << 5 | @as(u32, v.op2) << 10 | @as(u32, v.cond) << 12 | @as(u32, v.rm) << 16 | @as(u32, v.fixed) << 21 | @as(u32, v.s) << 29 | @as(u32, v.op) << 30 | @as(u32, v.sf) << 31,
- .data_processing_3_source => |v| @as(u32, @bitCast(v)),
- .data_processing_2_source => |v| @as(u32, @bitCast(v)),
- };
- }
-
- fn moveWideImmediate(
- opc: u2,
- rd: Register,
- imm16: u16,
- shift: u6,
- ) Instruction {
- assert(shift % 16 == 0);
- assert(!(rd.size() == 32 and shift > 16));
- assert(!(rd.size() == 64 and shift > 48));
-
- return Instruction{
- .move_wide_immediate = .{
- .rd = rd.enc(),
- .imm16 = imm16,
- .hw = @as(u2, @intCast(shift / 16)),
- .opc = opc,
- .sf = switch (rd.size()) {
- 32 => 0,
- 64 => 1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction {
- assert(rd.size() == 64);
- const imm21_u = @as(u21, @bitCast(imm21));
- return Instruction{
- .pc_relative_address = .{
- .rd = rd.enc(),
- .immlo = @as(u2, @truncate(imm21_u)),
- .immhi = @as(u19, @truncate(imm21_u >> 2)),
- .op = op,
- },
- };
- }
-
- pub const LoadStoreOffsetImmediate = union(enum) {
- post_index: i9,
- pre_index: i9,
- unsigned: u12,
- };
-
- pub const LoadStoreOffsetRegister = struct {
- rm: u5,
- shift: union(enum) {
- uxtw: u2,
- lsl: u2,
- sxtw: u2,
- sxtx: u2,
- },
- };
-
- /// Represents the offset operand of a load or store instruction.
- /// Data can be loaded from memory with either an immediate offset
- /// or an offset that is stored in some register.
- pub const LoadStoreOffset = union(enum) {
- immediate: LoadStoreOffsetImmediate,
- register: LoadStoreOffsetRegister,
-
- pub const none = LoadStoreOffset{
- .immediate = .{ .unsigned = 0 },
- };
-
- pub fn toU12(self: LoadStoreOffset) u12 {
- return switch (self) {
- .immediate => |imm_type| switch (imm_type) {
- .post_index => |v| (@as(u12, @intCast(@as(u9, @bitCast(v)))) << 2) + 1,
- .pre_index => |v| (@as(u12, @intCast(@as(u9, @bitCast(v)))) << 2) + 3,
- .unsigned => |v| v,
- },
- .register => |r| switch (r.shift) {
- .uxtw => |v| (@as(u12, @intCast(r.rm)) << 6) + (@as(u12, @intCast(v)) << 2) + 16 + 2050,
- .lsl => |v| (@as(u12, @intCast(r.rm)) << 6) + (@as(u12, @intCast(v)) << 2) + 24 + 2050,
- .sxtw => |v| (@as(u12, @intCast(r.rm)) << 6) + (@as(u12, @intCast(v)) << 2) + 48 + 2050,
- .sxtx => |v| (@as(u12, @intCast(r.rm)) << 6) + (@as(u12, @intCast(v)) << 2) + 56 + 2050,
- },
- };
- }
-
- pub fn imm(offset: u12) LoadStoreOffset {
- return .{
- .immediate = .{ .unsigned = offset },
- };
- }
-
- pub fn imm_post_index(offset: i9) LoadStoreOffset {
- return .{
- .immediate = .{ .post_index = offset },
- };
- }
-
- pub fn imm_pre_index(offset: i9) LoadStoreOffset {
- return .{
- .immediate = .{ .pre_index = offset },
- };
- }
-
- pub fn reg(rm: Register) LoadStoreOffset {
- return .{
- .register = .{
- .rm = rm.enc(),
- .shift = .{
- .lsl = 0,
- },
- },
- };
- }
-
- pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset {
- assert(rm.size() == 32 and (shift == 0 or shift == 2));
- return .{
- .register = .{
- .rm = rm.enc(),
- .shift = .{
- .uxtw = shift,
- },
- },
- };
- }
-
- pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset {
- assert(rm.size() == 64 and (shift == 0 or shift == 3));
- return .{
- .register = .{
- .rm = rm.enc(),
- .shift = .{
- .lsl = shift,
- },
- },
- };
- }
-
- pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset {
- assert(rm.size() == 32 and (shift == 0 or shift == 2));
- return .{
- .register = .{
- .rm = rm.enc(),
- .shift = .{
- .sxtw = shift,
- },
- },
- };
- }
-
- pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset {
- assert(rm.size() == 64 and (shift == 0 or shift == 3));
- return .{
- .register = .{
- .rm = rm.enc(),
- .shift = .{
- .sxtx = shift,
- },
- },
- };
- }
- };
-
- /// Which kind of load/store to perform
- const LoadStoreVariant = enum {
- /// 32 bits or 64 bits
- str,
- /// 8 bits, zero-extended
- strb,
- /// 16 bits, zero-extended
- strh,
- /// 32 bits or 64 bits
- ldr,
- /// 8 bits, zero-extended
- ldrb,
- /// 16 bits, zero-extended
- ldrh,
- /// 8 bits, sign extended
- ldrsb,
- /// 16 bits, sign extended
- ldrsh,
- /// 32 bits, sign extended
- ldrsw,
- };
-
- fn loadStoreRegister(
- rt: Register,
- rn: Register,
- offset: LoadStoreOffset,
- variant: LoadStoreVariant,
- ) Instruction {
- assert(rn.size() == 64);
- assert(rn.id() != Register.xzr.id());
-
- const off = offset.toU12();
-
- const op1: u2 = blk: {
- switch (offset) {
- .immediate => |imm| switch (imm) {
- .unsigned => break :blk 0b01,
- else => {},
- },
- else => {},
- }
- break :blk 0b00;
- };
-
- const opc: u2 = blk: {
- switch (variant) {
- .ldr, .ldrh, .ldrb => break :blk 0b01,
- .str, .strh, .strb => break :blk 0b00,
- .ldrsb,
- .ldrsh,
- => switch (rt.size()) {
- 32 => break :blk 0b11,
- 64 => break :blk 0b10,
- else => unreachable, // unexpected register size
- },
- .ldrsw => break :blk 0b10,
- }
- };
-
- const size: u2 = blk: {
- switch (variant) {
- .ldr, .str => switch (rt.size()) {
- 32 => break :blk 0b10,
- 64 => break :blk 0b11,
- else => unreachable, // unexpected register size
- },
- .ldrsw => break :blk 0b10,
- .ldrh, .ldrsh, .strh => break :blk 0b01,
- .ldrb, .ldrsb, .strb => break :blk 0b00,
- }
- };
-
- return Instruction{
- .load_store_register = .{
- .rt = rt.enc(),
- .rn = rn.enc(),
- .offset = off,
- .opc = opc,
- .op1 = op1,
- .v = 0,
- .size = size,
- },
- };
- }
-
- fn loadStoreRegisterPair(
- rt1: Register,
- rt2: Register,
- rn: Register,
- offset: i9,
- encoding: u2,
- load: bool,
- ) Instruction {
- assert(rn.size() == 64);
- assert(rn.id() != Register.xzr.id());
-
- switch (rt1.size()) {
- 32 => {
- assert(-256 <= offset and offset <= 252);
- const imm7 = @as(u7, @truncate(@as(u9, @bitCast(offset >> 2))));
- return Instruction{
- .load_store_register_pair = .{
- .rt1 = rt1.enc(),
- .rn = rn.enc(),
- .rt2 = rt2.enc(),
- .imm7 = imm7,
- .load = @intFromBool(load),
- .encoding = encoding,
- .opc = 0b00,
- },
- };
- },
- 64 => {
- assert(-512 <= offset and offset <= 504);
- const imm7 = @as(u7, @truncate(@as(u9, @bitCast(offset >> 3))));
- return Instruction{
- .load_store_register_pair = .{
- .rt1 = rt1.enc(),
- .rn = rn.enc(),
- .rt2 = rt2.enc(),
- .imm7 = imm7,
- .load = @intFromBool(load),
- .encoding = encoding,
- .opc = 0b10,
- },
- };
- },
- else => unreachable, // unexpected register size
- }
- }
-
- fn loadLiteral(rt: Register, imm19: u19) Instruction {
- return Instruction{
- .load_literal = .{
- .rt = rt.enc(),
- .imm19 = imm19,
- .opc = switch (rt.size()) {
- 32 => 0b00,
- 64 => 0b01,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- fn exceptionGeneration(
- opc: u3,
- op2: u3,
- ll: u2,
- imm16: u16,
- ) Instruction {
- return Instruction{
- .exception_generation = .{
- .ll = ll,
- .op2 = op2,
- .imm16 = imm16,
- .opc = opc,
- },
- };
- }
-
- fn unconditionalBranchRegister(
- opc: u4,
- op2: u5,
- op3: u6,
- rn: Register,
- op4: u5,
- ) Instruction {
- assert(rn.size() == 64);
-
- return Instruction{
- .unconditional_branch_register = .{
- .op4 = op4,
- .rn = rn.enc(),
- .op3 = op3,
- .op2 = op2,
- .opc = opc,
- },
- };
- }
-
- fn unconditionalBranchImmediate(
- op: u1,
- offset: i28,
- ) Instruction {
- return Instruction{
- .unconditional_branch_immediate = .{
- .imm26 = @as(u26, @bitCast(@as(i26, @intCast(offset >> 2)))),
- .op = op,
- },
- };
- }
-
- pub const LogicalShiftedRegisterShift = enum(u2) { lsl, lsr, asr, ror };
-
- fn logicalShiftedRegister(
- opc: u2,
- n: u1,
- rd: Register,
- rn: Register,
- rm: Register,
- shift: LogicalShiftedRegisterShift,
- amount: u6,
- ) Instruction {
- assert(rd.size() == rn.size());
- assert(rd.size() == rm.size());
- if (rd.size() == 32) assert(amount < 32);
-
- return Instruction{
- .logical_shifted_register = .{
- .rd = rd.enc(),
- .rn = rn.enc(),
- .imm6 = amount,
- .rm = rm.enc(),
- .n = n,
- .shift = @intFromEnum(shift),
- .opc = opc,
- .sf = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable,
- },
- },
- };
- }
-
- fn addSubtractImmediate(
- op: u1,
- s: u1,
- rd: Register,
- rn: Register,
- imm12: u12,
- shift: bool,
- ) Instruction {
- assert(rd.size() == rn.size());
- assert(rn.id() != Register.xzr.id());
-
- return Instruction{
- .add_subtract_immediate = .{
- .rd = rd.enc(),
- .rn = rn.enc(),
- .imm12 = imm12,
- .sh = @intFromBool(shift),
- .s = s,
- .op = op,
- .sf = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- fn logicalImmediate(
- opc: u2,
- rd: Register,
- rn: Register,
- imms: u6,
- immr: u6,
- n: u1,
- ) Instruction {
- assert(rd.size() == rn.size());
- assert(!(rd.size() == 32 and n != 0));
-
- return Instruction{
- .logical_immediate = .{
- .rd = rd.enc(),
- .rn = rn.enc(),
- .imms = imms,
- .immr = immr,
- .n = n,
- .opc = opc,
- .sf = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- fn initBitfield(
- opc: u2,
- n: u1,
- rd: Register,
- rn: Register,
- immr: u6,
- imms: u6,
- ) Instruction {
- assert(rd.size() == rn.size());
- assert(!(rd.size() == 64 and n != 1));
- assert(!(rd.size() == 32 and (n != 0 or immr >> 5 != 0 or immr >> 5 != 0)));
-
- return Instruction{
- .bitfield = .{
- .rd = rd.enc(),
- .rn = rn.enc(),
- .imms = imms,
- .immr = immr,
- .n = n,
- .opc = opc,
- .sf = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- pub const AddSubtractShiftedRegisterShift = enum(u2) { lsl, lsr, asr, _ };
-
- fn addSubtractShiftedRegister(
- op: u1,
- s: u1,
- shift: AddSubtractShiftedRegisterShift,
- rd: Register,
- rn: Register,
- rm: Register,
- imm6: u6,
- ) Instruction {
- assert(rd.size() == rn.size());
- assert(rd.size() == rm.size());
-
- return Instruction{
- .add_subtract_shifted_register = .{
- .rd = rd.enc(),
- .rn = rn.enc(),
- .imm6 = imm6,
- .rm = rm.enc(),
- .shift = @intFromEnum(shift),
- .s = s,
- .op = op,
- .sf = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- pub const AddSubtractExtendedRegisterOption = enum(u3) {
- uxtb,
- uxth,
- uxtw,
- uxtx, // serves also as lsl
- sxtb,
- sxth,
- sxtw,
- sxtx,
- };
-
- fn addSubtractExtendedRegister(
- op: u1,
- s: u1,
- rd: Register,
- rn: Register,
- rm: Register,
- extend: AddSubtractExtendedRegisterOption,
- imm3: u3,
- ) Instruction {
- return Instruction{
- .add_subtract_extended_register = .{
- .rd = rd.enc(),
- .rn = rn.enc(),
- .imm3 = imm3,
- .option = @intFromEnum(extend),
- .rm = rm.enc(),
- .s = s,
- .op = op,
- .sf = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- fn conditionalBranch(
- o0: u1,
- o1: u1,
- cond: Condition,
- offset: i21,
- ) Instruction {
- assert(offset & 0b11 == 0b00);
-
- return Instruction{
- .conditional_branch = .{
- .cond = @intFromEnum(cond),
- .o0 = o0,
- .imm19 = @as(u19, @bitCast(@as(i19, @intCast(offset >> 2)))),
- .o1 = o1,
- },
- };
- }
-
- fn compareAndBranch(
- op: u1,
- rt: Register,
- offset: i21,
- ) Instruction {
- assert(offset & 0b11 == 0b00);
-
- return Instruction{
- .compare_and_branch = .{
- .rt = rt.enc(),
- .imm19 = @as(u19, @bitCast(@as(i19, @intCast(offset >> 2)))),
- .op = op,
- .sf = switch (rt.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- fn conditionalSelect(
- op2: u2,
- op: u1,
- s: u1,
- rd: Register,
- rn: Register,
- rm: Register,
- cond: Condition,
- ) Instruction {
- assert(rd.size() == rn.size());
- assert(rd.size() == rm.size());
-
- return Instruction{
- .conditional_select = .{
- .rd = rd.enc(),
- .rn = rn.enc(),
- .op2 = op2,
- .cond = @intFromEnum(cond),
- .rm = rm.enc(),
- .s = s,
- .op = op,
- .sf = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- fn dataProcessing3Source(
- op54: u2,
- op31: u3,
- o0: u1,
- rd: Register,
- rn: Register,
- rm: Register,
- ra: Register,
- ) Instruction {
- return Instruction{
- .data_processing_3_source = .{
- .rd = rd.enc(),
- .rn = rn.enc(),
- .ra = ra.enc(),
- .o0 = o0,
- .rm = rm.enc(),
- .op31 = op31,
- .op54 = op54,
- .sf = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- fn dataProcessing2Source(
- s: u1,
- opcode: u6,
- rd: Register,
- rn: Register,
- rm: Register,
- ) Instruction {
- assert(rd.size() == rn.size());
- assert(rd.size() == rm.size());
-
- return Instruction{
- .data_processing_2_source = .{
- .rd = rd.enc(),
- .rn = rn.enc(),
- .opcode = opcode,
- .rm = rm.enc(),
- .s = s,
- .sf = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- },
- },
- };
- }
-
- // Helper functions for assembly syntax functions
-
- // Move wide (immediate)
-
- pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction {
- return moveWideImmediate(0b00, rd, imm16, shift);
- }
-
- pub fn movz(rd: Register, imm16: u16, shift: u6) Instruction {
- return moveWideImmediate(0b10, rd, imm16, shift);
- }
-
- pub fn movk(rd: Register, imm16: u16, shift: u6) Instruction {
- return moveWideImmediate(0b11, rd, imm16, shift);
- }
-
- // PC relative address
-
- pub fn adr(rd: Register, imm21: i21) Instruction {
- return pcRelativeAddress(rd, imm21, 0b0);
- }
-
- pub fn adrp(rd: Register, imm21: i21) Instruction {
- return pcRelativeAddress(rd, imm21, 0b1);
- }
-
- // Load or store register
-
- pub fn ldrLiteral(rt: Register, literal: u19) Instruction {
- return loadLiteral(rt, literal);
- }
-
- pub fn ldr(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
- return loadStoreRegister(rt, rn, offset, .ldr);
- }
-
- pub fn ldrh(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
- return loadStoreRegister(rt, rn, offset, .ldrh);
- }
-
- pub fn ldrb(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
- return loadStoreRegister(rt, rn, offset, .ldrb);
- }
-
- pub fn ldrsb(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
- return loadStoreRegister(rt, rn, offset, .ldrsb);
- }
-
- pub fn ldrsh(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
- return loadStoreRegister(rt, rn, offset, .ldrsh);
- }
-
- pub fn ldrsw(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
- return loadStoreRegister(rt, rn, offset, .ldrsw);
- }
-
- pub fn str(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
- return loadStoreRegister(rt, rn, offset, .str);
- }
-
- pub fn strh(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
- return loadStoreRegister(rt, rn, offset, .strh);
- }
-
- pub fn strb(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
- return loadStoreRegister(rt, rn, offset, .strb);
- }
-
- // Load or store pair of registers
-
- pub const LoadStorePairOffset = struct {
- encoding: enum(u2) {
- post_index = 0b01,
- signed = 0b10,
- pre_index = 0b11,
- },
- offset: i9,
-
- pub fn none() LoadStorePairOffset {
- return .{ .encoding = .signed, .offset = 0 };
- }
-
- pub fn post_index(imm: i9) LoadStorePairOffset {
- return .{ .encoding = .post_index, .offset = imm };
- }
-
- pub fn pre_index(imm: i9) LoadStorePairOffset {
- return .{ .encoding = .pre_index, .offset = imm };
- }
-
- pub fn signed(imm: i9) LoadStorePairOffset {
- return .{ .encoding = .signed, .offset = imm };
- }
- };
-
- pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction {
- return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @intFromEnum(offset.encoding), true);
- }
-
- pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction {
- return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true);
- }
-
- pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction {
- return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @intFromEnum(offset.encoding), false);
- }
-
- pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction {
- return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false);
- }
-
- // Exception generation
-
- pub fn svc(imm16: u16) Instruction {
- return exceptionGeneration(0b000, 0b000, 0b01, imm16);
- }
-
- pub fn hvc(imm16: u16) Instruction {
- return exceptionGeneration(0b000, 0b000, 0b10, imm16);
- }
-
- pub fn smc(imm16: u16) Instruction {
- return exceptionGeneration(0b000, 0b000, 0b11, imm16);
- }
-
- pub fn brk(imm16: u16) Instruction {
- return exceptionGeneration(0b001, 0b000, 0b00, imm16);
- }
-
- pub fn hlt(imm16: u16) Instruction {
- return exceptionGeneration(0b010, 0b000, 0b00, imm16);
- }
-
- // Unconditional branch (register)
-
- pub fn br(rn: Register) Instruction {
- return unconditionalBranchRegister(0b0000, 0b11111, 0b000000, rn, 0b00000);
- }
-
- pub fn blr(rn: Register) Instruction {
- return unconditionalBranchRegister(0b0001, 0b11111, 0b000000, rn, 0b00000);
- }
-
- pub fn ret(rn: ?Register) Instruction {
- return unconditionalBranchRegister(0b0010, 0b11111, 0b000000, rn orelse .x30, 0b00000);
- }
-
- // Unconditional branch (immediate)
-
- pub fn b(offset: i28) Instruction {
- return unconditionalBranchImmediate(0, offset);
- }
-
- pub fn bl(offset: i28) Instruction {
- return unconditionalBranchImmediate(1, offset);
- }
-
- // Nop
-
- pub fn nop() Instruction {
- return Instruction{ .no_operation = .{} };
- }
-
- // Logical (shifted register)
-
- pub fn andShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: LogicalShiftedRegisterShift,
- amount: u6,
- ) Instruction {
- return logicalShiftedRegister(0b00, 0b0, rd, rn, rm, shift, amount);
- }
-
- pub fn bicShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: LogicalShiftedRegisterShift,
- amount: u6,
- ) Instruction {
- return logicalShiftedRegister(0b00, 0b1, rd, rn, rm, shift, amount);
- }
-
- pub fn orrShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: LogicalShiftedRegisterShift,
- amount: u6,
- ) Instruction {
- return logicalShiftedRegister(0b01, 0b0, rd, rn, rm, shift, amount);
- }
-
- pub fn ornShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: LogicalShiftedRegisterShift,
- amount: u6,
- ) Instruction {
- return logicalShiftedRegister(0b01, 0b1, rd, rn, rm, shift, amount);
- }
-
- pub fn eorShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: LogicalShiftedRegisterShift,
- amount: u6,
- ) Instruction {
- return logicalShiftedRegister(0b10, 0b0, rd, rn, rm, shift, amount);
- }
-
- pub fn eonShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: LogicalShiftedRegisterShift,
- amount: u6,
- ) Instruction {
- return logicalShiftedRegister(0b10, 0b1, rd, rn, rm, shift, amount);
- }
-
- pub fn andsShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: LogicalShiftedRegisterShift,
- amount: u6,
- ) Instruction {
- return logicalShiftedRegister(0b11, 0b0, rd, rn, rm, shift, amount);
- }
-
- pub fn bicsShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: LogicalShiftedRegisterShift,
- amount: u6,
- ) Instruction {
- return logicalShiftedRegister(0b11, 0b1, rd, rn, rm, shift, amount);
- }
-
- // Add/subtract (immediate)
-
- pub fn add(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
- return addSubtractImmediate(0b0, 0b0, rd, rn, imm, shift);
- }
-
- pub fn adds(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
- return addSubtractImmediate(0b0, 0b1, rd, rn, imm, shift);
- }
-
- pub fn sub(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
- return addSubtractImmediate(0b1, 0b0, rd, rn, imm, shift);
- }
-
- pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction {
- return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift);
- }
-
- // Logical (immediate)
-
- pub fn andImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
- return logicalImmediate(0b00, rd, rn, imms, immr, n);
- }
-
- pub fn orrImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
- return logicalImmediate(0b01, rd, rn, imms, immr, n);
- }
-
- pub fn eorImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
- return logicalImmediate(0b10, rd, rn, imms, immr, n);
- }
-
- pub fn andsImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
- return logicalImmediate(0b11, rd, rn, imms, immr, n);
- }
-
- // Bitfield
-
- pub fn sbfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction {
- const n: u1 = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- };
- return initBitfield(0b00, n, rd, rn, immr, imms);
- }
-
- pub fn bfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction {
- const n: u1 = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- };
- return initBitfield(0b01, n, rd, rn, immr, imms);
- }
-
- pub fn ubfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction {
- const n: u1 = switch (rd.size()) {
- 32 => 0b0,
- 64 => 0b1,
- else => unreachable, // unexpected register size
- };
- return initBitfield(0b10, n, rd, rn, immr, imms);
- }
-
- pub fn asrImmediate(rd: Register, rn: Register, shift: u6) Instruction {
- const imms = @as(u6, @intCast(rd.size() - 1));
- return sbfm(rd, rn, shift, imms);
- }
-
- pub fn sbfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction {
- return sbfm(rd, rn, lsb, @as(u6, @intCast(lsb + width - 1)));
- }
-
- pub fn sxtb(rd: Register, rn: Register) Instruction {
- return sbfm(rd, rn, 0, 7);
- }
-
- pub fn sxth(rd: Register, rn: Register) Instruction {
- return sbfm(rd, rn, 0, 15);
- }
-
- pub fn sxtw(rd: Register, rn: Register) Instruction {
- assert(rd.size() == 64);
- return sbfm(rd, rn, 0, 31);
- }
-
- pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction {
- const size = @as(u6, @intCast(rd.size() - 1));
- return ubfm(rd, rn, size - shift + 1, size - shift);
- }
-
- pub fn lsrImmediate(rd: Register, rn: Register, shift: u6) Instruction {
- const imms = @as(u6, @intCast(rd.size() - 1));
- return ubfm(rd, rn, shift, imms);
- }
-
- pub fn ubfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction {
- return ubfm(rd, rn, lsb, @as(u6, @intCast(lsb + width - 1)));
- }
-
- pub fn uxtb(rd: Register, rn: Register) Instruction {
- return ubfm(rd, rn, 0, 7);
- }
-
- pub fn uxth(rd: Register, rn: Register) Instruction {
- return ubfm(rd, rn, 0, 15);
- }
-
- // Add/subtract (shifted register)
-
- pub fn addShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: AddSubtractShiftedRegisterShift,
- imm6: u6,
- ) Instruction {
- return addSubtractShiftedRegister(0b0, 0b0, shift, rd, rn, rm, imm6);
- }
-
- pub fn addsShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: AddSubtractShiftedRegisterShift,
- imm6: u6,
- ) Instruction {
- return addSubtractShiftedRegister(0b0, 0b1, shift, rd, rn, rm, imm6);
- }
-
- pub fn subShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: AddSubtractShiftedRegisterShift,
- imm6: u6,
- ) Instruction {
- return addSubtractShiftedRegister(0b1, 0b0, shift, rd, rn, rm, imm6);
- }
-
- pub fn subsShiftedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- shift: AddSubtractShiftedRegisterShift,
- imm6: u6,
- ) Instruction {
- return addSubtractShiftedRegister(0b1, 0b1, shift, rd, rn, rm, imm6);
- }
-
- // Add/subtract (extended register)
-
- pub fn addExtendedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- extend: AddSubtractExtendedRegisterOption,
- imm3: u3,
- ) Instruction {
- return addSubtractExtendedRegister(0b0, 0b0, rd, rn, rm, extend, imm3);
- }
-
- pub fn addsExtendedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- extend: AddSubtractExtendedRegisterOption,
- imm3: u3,
- ) Instruction {
- return addSubtractExtendedRegister(0b0, 0b1, rd, rn, rm, extend, imm3);
- }
-
- pub fn subExtendedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- extend: AddSubtractExtendedRegisterOption,
- imm3: u3,
- ) Instruction {
- return addSubtractExtendedRegister(0b1, 0b0, rd, rn, rm, extend, imm3);
- }
-
- pub fn subsExtendedRegister(
- rd: Register,
- rn: Register,
- rm: Register,
- extend: AddSubtractExtendedRegisterOption,
- imm3: u3,
- ) Instruction {
- return addSubtractExtendedRegister(0b1, 0b1, rd, rn, rm, extend, imm3);
- }
-
- // Conditional branch
-
- pub fn bCond(cond: Condition, offset: i21) Instruction {
- return conditionalBranch(0b0, 0b0, cond, offset);
- }
-
- // Compare and branch
-
- pub fn cbz(rt: Register, offset: i21) Instruction {
- return compareAndBranch(0b0, rt, offset);
- }
-
- pub fn cbnz(rt: Register, offset: i21) Instruction {
- return compareAndBranch(0b1, rt, offset);
- }
-
- // Conditional select
-
- pub fn csel(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
- return conditionalSelect(0b00, 0b0, 0b0, rd, rn, rm, cond);
- }
-
- pub fn csinc(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
- return conditionalSelect(0b01, 0b0, 0b0, rd, rn, rm, cond);
- }
-
- pub fn csinv(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
- return conditionalSelect(0b00, 0b1, 0b0, rd, rn, rm, cond);
- }
-
- pub fn csneg(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
- return conditionalSelect(0b01, 0b1, 0b0, rd, rn, rm, cond);
- }
-
- // Data processing (3 source)
-
- pub fn madd(rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
- return dataProcessing3Source(0b00, 0b000, 0b0, rd, rn, rm, ra);
- }
-
- pub fn smaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
- assert(rd.size() == 64 and rn.size() == 32 and rm.size() == 32 and ra.size() == 64);
- return dataProcessing3Source(0b00, 0b001, 0b0, rd, rn, rm, ra);
- }
-
- pub fn umaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
- assert(rd.size() == 64 and rn.size() == 32 and rm.size() == 32 and ra.size() == 64);
- return dataProcessing3Source(0b00, 0b101, 0b0, rd, rn, rm, ra);
- }
-
- pub fn msub(rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
- return dataProcessing3Source(0b00, 0b000, 0b1, rd, rn, rm, ra);
- }
-
- pub fn mul(rd: Register, rn: Register, rm: Register) Instruction {
- return madd(rd, rn, rm, .xzr);
- }
-
- pub fn smull(rd: Register, rn: Register, rm: Register) Instruction {
- return smaddl(rd, rn, rm, .xzr);
- }
-
- pub fn smulh(rd: Register, rn: Register, rm: Register) Instruction {
- assert(rd.size() == 64);
- return dataProcessing3Source(0b00, 0b010, 0b0, rd, rn, rm, .xzr);
- }
-
- pub fn umull(rd: Register, rn: Register, rm: Register) Instruction {
- return umaddl(rd, rn, rm, .xzr);
- }
-
- pub fn umulh(rd: Register, rn: Register, rm: Register) Instruction {
- assert(rd.size() == 64);
- return dataProcessing3Source(0b00, 0b110, 0b0, rd, rn, rm, .xzr);
- }
-
- pub fn mneg(rd: Register, rn: Register, rm: Register) Instruction {
- return msub(rd, rn, rm, .xzr);
- }
-
- // Data processing (2 source)
-
- pub fn udiv(rd: Register, rn: Register, rm: Register) Instruction {
- return dataProcessing2Source(0b0, 0b000010, rd, rn, rm);
- }
-
- pub fn sdiv(rd: Register, rn: Register, rm: Register) Instruction {
- return dataProcessing2Source(0b0, 0b000011, rd, rn, rm);
- }
-
- pub fn lslv(rd: Register, rn: Register, rm: Register) Instruction {
- return dataProcessing2Source(0b0, 0b001000, rd, rn, rm);
- }
-
- pub fn lsrv(rd: Register, rn: Register, rm: Register) Instruction {
- return dataProcessing2Source(0b0, 0b001001, rd, rn, rm);
- }
-
- pub fn asrv(rd: Register, rn: Register, rm: Register) Instruction {
- return dataProcessing2Source(0b0, 0b001010, rd, rn, rm);
- }
-
- pub const asrRegister = asrv;
- pub const lslRegister = lslv;
- pub const lsrRegister = lsrv;
-};
-
-test {
- testing.refAllDecls(@This());
-}
-
-test "serialize instructions" {
- const Testcase = struct {
- inst: Instruction,
- expected: u32,
- };
-
- const testcases = [_]Testcase{
- .{ // orr x0, xzr, x1
- .inst = Instruction.orrShiftedRegister(.x0, .xzr, .x1, .lsl, 0),
- .expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
- },
- .{ // orn x0, xzr, x1
- .inst = Instruction.ornShiftedRegister(.x0, .xzr, .x1, .lsl, 0),
- .expected = 0b1_01_01010_00_1_00001_000000_11111_00000,
- },
- .{ // movz x1, #4
- .inst = Instruction.movz(.x1, 4, 0),
- .expected = 0b1_10_100101_00_0000000000000100_00001,
- },
- .{ // movz x1, #4, lsl 16
- .inst = Instruction.movz(.x1, 4, 16),
- .expected = 0b1_10_100101_01_0000000000000100_00001,
- },
- .{ // movz x1, #4, lsl 32
- .inst = Instruction.movz(.x1, 4, 32),
- .expected = 0b1_10_100101_10_0000000000000100_00001,
- },
- .{ // movz x1, #4, lsl 48
- .inst = Instruction.movz(.x1, 4, 48),
- .expected = 0b1_10_100101_11_0000000000000100_00001,
- },
- .{ // movz w1, #4
- .inst = Instruction.movz(.w1, 4, 0),
- .expected = 0b0_10_100101_00_0000000000000100_00001,
- },
- .{ // movz w1, #4, lsl 16
- .inst = Instruction.movz(.w1, 4, 16),
- .expected = 0b0_10_100101_01_0000000000000100_00001,
- },
- .{ // svc #0
- .inst = Instruction.svc(0),
- .expected = 0b1101_0100_000_0000000000000000_00001,
- },
- .{ // svc #0x80 ; typical on Darwin
- .inst = Instruction.svc(0x80),
- .expected = 0b1101_0100_000_0000000010000000_00001,
- },
- .{ // ret
- .inst = Instruction.ret(null),
- .expected = 0b1101_011_00_10_11111_0000_00_11110_00000,
- },
- .{ // bl #0x10
- .inst = Instruction.bl(0x10),
- .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100,
- },
- .{ // ldr x2, [x1]
- .inst = Instruction.ldr(.x2, .x1, Instruction.LoadStoreOffset.none),
- .expected = 0b11_111_0_01_01_000000000000_00001_00010,
- },
- .{ // ldr x2, [x1, #1]!
- .inst = Instruction.ldr(.x2, .x1, Instruction.LoadStoreOffset.imm_pre_index(1)),
- .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010,
- },
- .{ // ldr x2, [x1], #-1
- .inst = Instruction.ldr(.x2, .x1, Instruction.LoadStoreOffset.imm_post_index(-1)),
- .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010,
- },
- .{ // ldr x2, [x1], (x3)
- .inst = Instruction.ldr(.x2, .x1, Instruction.LoadStoreOffset.reg(.x3)),
- .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010,
- },
- .{ // ldr x2, label
- .inst = Instruction.ldrLiteral(.x2, 0x1),
- .expected = 0b01_011_0_00_0000000000000000001_00010,
- },
- .{ // ldrh x7, [x4], #0xaa
- .inst = Instruction.ldrh(.x7, .x4, Instruction.LoadStoreOffset.imm_post_index(0xaa)),
- .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111,
- },
- .{ // ldrb x9, [x15, #0xff]!
- .inst = Instruction.ldrb(.x9, .x15, Instruction.LoadStoreOffset.imm_pre_index(0xff)),
- .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001,
- },
- .{ // str x2, [x1]
- .inst = Instruction.str(.x2, .x1, Instruction.LoadStoreOffset.none),
- .expected = 0b11_111_0_01_00_000000000000_00001_00010,
- },
- .{ // str x2, [x1], (x3)
- .inst = Instruction.str(.x2, .x1, Instruction.LoadStoreOffset.reg(.x3)),
- .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010,
- },
- .{ // strh w0, [x1]
- .inst = Instruction.strh(.w0, .x1, Instruction.LoadStoreOffset.none),
- .expected = 0b01_111_0_01_00_000000000000_00001_00000,
- },
- .{ // strb w8, [x9]
- .inst = Instruction.strb(.w8, .x9, Instruction.LoadStoreOffset.none),
- .expected = 0b00_111_0_01_00_000000000000_01001_01000,
- },
- .{ // adr x2, #0x8
- .inst = Instruction.adr(.x2, 0x8),
- .expected = 0b0_00_10000_0000000000000000010_00010,
- },
- .{ // adr x2, -#0x8
- .inst = Instruction.adr(.x2, -0x8),
- .expected = 0b0_00_10000_1111111111111111110_00010,
- },
- .{ // adrp x2, #0x8
- .inst = Instruction.adrp(.x2, 0x8),
- .expected = 0b1_00_10000_0000000000000000010_00010,
- },
- .{ // adrp x2, -#0x8
- .inst = Instruction.adrp(.x2, -0x8),
- .expected = 0b1_00_10000_1111111111111111110_00010,
- },
- .{ // stp x1, x2, [sp, #8]
- .inst = Instruction.stp(.x1, .x2, .sp, Instruction.LoadStorePairOffset.signed(8)),
- .expected = 0b10_101_0_010_0_0000001_00010_11111_00001,
- },
- .{ // ldp x1, x2, [sp, #8]
- .inst = Instruction.ldp(.x1, .x2, .sp, Instruction.LoadStorePairOffset.signed(8)),
- .expected = 0b10_101_0_010_1_0000001_00010_11111_00001,
- },
- .{ // stp x1, x2, [sp, #-16]!
- .inst = Instruction.stp(.x1, .x2, .sp, Instruction.LoadStorePairOffset.pre_index(-16)),
- .expected = 0b10_101_0_011_0_1111110_00010_11111_00001,
- },
- .{ // ldp x1, x2, [sp], #16
- .inst = Instruction.ldp(.x1, .x2, .sp, Instruction.LoadStorePairOffset.post_index(16)),
- .expected = 0b10_101_0_001_1_0000010_00010_11111_00001,
- },
- .{ // and x0, x4, x2
- .inst = Instruction.andShiftedRegister(.x0, .x4, .x2, .lsl, 0),
- .expected = 0b1_00_01010_00_0_00010_000000_00100_00000,
- },
- .{ // and x0, x4, x2, lsl #0x8
- .inst = Instruction.andShiftedRegister(.x0, .x4, .x2, .lsl, 0x8),
- .expected = 0b1_00_01010_00_0_00010_001000_00100_00000,
- },
- .{ // add x0, x10, #10
- .inst = Instruction.add(.x0, .x10, 10, false),
- .expected = 0b1_0_0_100010_0_0000_0000_1010_01010_00000,
- },
- .{ // subs x0, x5, #11, lsl #12
- .inst = Instruction.subs(.x0, .x5, 11, true),
- .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000,
- },
- .{ // b.hi #-4
- .inst = Instruction.bCond(.hi, -4),
- .expected = 0b0101010_0_1111111111111111111_0_1000,
- },
- .{ // cbz x10, #40
- .inst = Instruction.cbz(.x10, 40),
- .expected = 0b1_011010_0_0000000000000001010_01010,
- },
- .{ // add x0, x1, x2, lsl #5
- .inst = Instruction.addShiftedRegister(.x0, .x1, .x2, .lsl, 5),
- .expected = 0b1_0_0_01011_00_0_00010_000101_00001_00000,
- },
- .{ // csinc x1, x2, x4, eq
- .inst = Instruction.csinc(.x1, .x2, .x4, .eq),
- .expected = 0b1_0_0_11010100_00100_0000_0_1_00010_00001,
- },
- .{ // mul x1, x4, x9
- .inst = Instruction.mul(.x1, .x4, .x9),
- .expected = 0b1_00_11011_000_01001_0_11111_00100_00001,
- },
- .{ // eor x3, x5, #1
- .inst = Instruction.eorImmediate(.x3, .x5, 0b000000, 0b000000, 0b1),
- .expected = 0b1_10_100100_1_000000_000000_00101_00011,
- },
- .{ // lslv x6, x9, x10
- .inst = Instruction.lslv(.x6, .x9, .x10),
- .expected = 0b1_0_0_11010110_01010_0010_00_01001_00110,
- },
- .{ // lsl x4, x2, #42
- .inst = Instruction.lslImmediate(.x4, .x2, 42),
- .expected = 0b1_10_100110_1_010110_010101_00010_00100,
- },
- .{ // lsl x4, x2, #63
- .inst = Instruction.lslImmediate(.x4, .x2, 63),
- .expected = 0b1_10_100110_1_000001_000000_00010_00100,
- },
- .{ // lsr x4, x2, #42
- .inst = Instruction.lsrImmediate(.x4, .x2, 42),
- .expected = 0b1_10_100110_1_101010_111111_00010_00100,
- },
- .{ // lsr x4, x2, #63
- .inst = Instruction.lsrImmediate(.x4, .x2, 63),
- .expected = 0b1_10_100110_1_111111_111111_00010_00100,
- },
- .{ // umull x0, w0, w1
- .inst = Instruction.umull(.x0, .w0, .w1),
- .expected = 0b1_00_11011_1_01_00001_0_11111_00000_00000,
- },
- .{ // smull x0, w0, w1
- .inst = Instruction.smull(.x0, .w0, .w1),
- .expected = 0b1_00_11011_0_01_00001_0_11111_00000_00000,
- },
- .{ // tst x0, #0xffffffff00000000
- .inst = Instruction.andsImmediate(.xzr, .x0, 0b011111, 0b100000, 0b1),
- .expected = 0b1_11_100100_1_100000_011111_00000_11111,
- },
- .{ // umulh x0, x1, x2
- .inst = Instruction.umulh(.x0, .x1, .x2),
- .expected = 0b1_00_11011_1_10_00010_0_11111_00001_00000,
- },
- .{ // smulh x0, x1, x2
- .inst = Instruction.smulh(.x0, .x1, .x2),
- .expected = 0b1_00_11011_0_10_00010_0_11111_00001_00000,
- },
- .{ // adds x0, x1, x2, sxtx
- .inst = Instruction.addsExtendedRegister(.x0, .x1, .x2, .sxtx, 0),
- .expected = 0b1_0_1_01011_00_1_00010_111_000_00001_00000,
- },
- };
-
- for (testcases) |case| {
- const actual = case.inst.toU32();
- try testing.expectEqual(case.expected, actual);
- }
-}
diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig
index 0753cc5d16..ffff65d4d1 100644
--- a/src/arch/riscv64/CodeGen.zig
+++ b/src/arch/riscv64/CodeGen.zig
@@ -744,7 +744,7 @@ pub fn generate(
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
air: *const Air,
- liveness: *const Air.Liveness,
+ liveness: *const ?Air.Liveness,
) CodeGenError!Mir {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -767,7 +767,7 @@ pub fn generate(
.pt = pt,
.mod = mod,
.bin_file = bin_file,
- .liveness = liveness.*,
+ .liveness = liveness.*.?,
.target = &mod.resolved_target.result,
.owner = .{ .nav_index = func.owner_nav },
.args = undefined, // populated after `resolveCallingConventionValues`
@@ -4584,7 +4584,7 @@ fn structFieldPtr(func: *Func, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
const field_offset: i32 = switch (container_ty.containerLayout(zcu)) {
.auto, .@"extern" => @intCast(container_ty.structFieldOffset(index, zcu)),
.@"packed" => @divExact(@as(i32, ptr_container_ty.ptrInfo(zcu).packed_offset.bit_offset) +
- (if (zcu.typeToStruct(container_ty)) |struct_obj| pt.structPackedFieldBitOffset(struct_obj, index) else 0) -
+ (if (zcu.typeToStruct(container_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, index) else 0) -
ptr_field_ty.ptrInfo(zcu).packed_offset.bit_offset, 8),
};
@@ -4615,7 +4615,7 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void {
const field_off: u32 = switch (struct_ty.containerLayout(zcu)) {
.auto, .@"extern" => @intCast(struct_ty.structFieldOffset(index, zcu) * 8),
.@"packed" => if (zcu.typeToStruct(struct_ty)) |struct_type|
- pt.structPackedFieldBitOffset(struct_type, index)
+ zcu.structPackedFieldBitOffset(struct_type, index)
else
0,
};
@@ -8059,7 +8059,7 @@ fn airAggregateInit(func: *Func, inst: Air.Inst.Index) !void {
const elem_abi_size: u32 = @intCast(elem_ty.abiSize(zcu));
const elem_abi_bits = elem_abi_size * 8;
- const elem_off = pt.structPackedFieldBitOffset(struct_obj, elem_i);
+ const elem_off = zcu.structPackedFieldBitOffset(struct_obj, elem_i);
const elem_byte_off: i32 = @intCast(elem_off / elem_abi_bits * elem_abi_size);
const elem_bit_off = elem_off % elem_abi_bits;
const elem_mcv = try func.resolveInst(elem);
diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig
index 31a7f39d69..6ab5dea4ec 100644
--- a/src/arch/sparc64/CodeGen.zig
+++ b/src/arch/sparc64/CodeGen.zig
@@ -267,7 +267,7 @@ pub fn generate(
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
air: *const Air,
- liveness: *const Air.Liveness,
+ liveness: *const ?Air.Liveness,
) CodeGenError!Mir {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -288,7 +288,7 @@ pub fn generate(
.gpa = gpa,
.pt = pt,
.air = air.*,
- .liveness = liveness.*,
+ .liveness = liveness.*.?,
.target = target,
.bin_file = lf,
.func_index = func_index,
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index 50d104a7bc..e56fb1df96 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -1173,7 +1173,7 @@ pub fn generate(
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
air: *const Air,
- liveness: *const Air.Liveness,
+ liveness: *const ?Air.Liveness,
) Error!Mir {
_ = src_loc;
_ = bin_file;
@@ -1194,7 +1194,7 @@ pub fn generate(
.gpa = gpa,
.pt = pt,
.air = air.*,
- .liveness = liveness.*,
+ .liveness = liveness.*.?,
.owner_nav = cg.owner_nav,
.target = target,
.ptr_size = switch (target.cpu.arch) {
@@ -1886,8 +1886,10 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.call_never_tail => cg.airCall(inst, .never_tail),
.call_never_inline => cg.airCall(inst, .never_inline),
- .is_err => cg.airIsErr(inst, .i32_ne),
- .is_non_err => cg.airIsErr(inst, .i32_eq),
+ .is_err => cg.airIsErr(inst, .i32_ne, .value),
+ .is_non_err => cg.airIsErr(inst, .i32_eq, .value),
+ .is_err_ptr => cg.airIsErr(inst, .i32_ne, .ptr),
+ .is_non_err_ptr => cg.airIsErr(inst, .i32_eq, .ptr),
.is_null => cg.airIsNull(inst, .i32_eq, .value),
.is_non_null => cg.airIsNull(inst, .i32_ne, .value),
@@ -1970,8 +1972,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.runtime_nav_ptr => cg.airRuntimeNavPtr(inst),
.assembly,
- .is_err_ptr,
- .is_non_err_ptr,
.err_return_trace,
.set_err_return_trace,
@@ -3776,7 +3776,7 @@ fn structFieldPtr(
break :offset @as(u32, 0);
}
const struct_type = zcu.typeToStruct(struct_ty).?;
- break :offset @divExact(pt.structPackedFieldBitOffset(struct_type, index) + struct_ptr_ty_info.packed_offset.bit_offset, 8);
+ break :offset @divExact(zcu.structPackedFieldBitOffset(struct_type, index) + struct_ptr_ty_info.packed_offset.bit_offset, 8);
},
.@"union" => 0,
else => unreachable,
@@ -3812,7 +3812,7 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.@"packed" => switch (struct_ty.zigTypeTag(zcu)) {
.@"struct" => result: {
const packed_struct = zcu.typeToPackedStruct(struct_ty).?;
- const offset = pt.structPackedFieldBitOffset(packed_struct, field_index);
+ const offset = zcu.structPackedFieldBitOffset(packed_struct, field_index);
const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));
const host_bits = backing_ty.intInfo(zcu).bits;
@@ -4105,7 +4105,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
return cg.finishAir(inst, .none, &.{br.operand});
}
-fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void {
+fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
const zcu = cg.pt.zcu;
const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
const operand = try cg.resolveInst(un_op);
@@ -4122,7 +4122,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerEr
}
try cg.emitWValue(operand);
- if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
+ if (op_kind == .ptr or pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
try cg.addMemArg(.i32_load16_u, .{
.offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))),
.alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
@@ -5696,7 +5696,7 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.auto, .@"extern" => parent_ty.structFieldOffset(field_index, zcu),
.@"packed" => offset: {
const parent_ptr_offset = parent_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset;
- const field_offset = if (zcu.typeToStruct(parent_ty)) |loaded_struct| pt.structPackedFieldBitOffset(loaded_struct, field_index) else 0;
+ const field_offset = if (zcu.typeToStruct(parent_ty)) |loaded_struct| zcu.structPackedFieldBitOffset(loaded_struct, field_index) else 0;
const field_ptr_offset = field_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset;
break :offset @divExact(parent_ptr_offset + field_offset - field_ptr_offset, 8);
},
@@ -6462,9 +6462,6 @@ fn lowerTry(
operand_is_ptr: bool,
) InnerError!WValue {
const zcu = cg.pt.zcu;
- if (operand_is_ptr) {
- return cg.fail("TODO: lowerTry for pointers", .{});
- }
const pl_ty = err_union_ty.errorUnionPayload(zcu);
const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu);
@@ -6475,7 +6472,7 @@ fn lowerTry(
// check if the error tag is set for the error union.
try cg.emitWValue(err_union);
- if (pl_has_bits) {
+ if (pl_has_bits or operand_is_ptr) {
const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
try cg.addMemArg(.i32_load16_u, .{
.offset = err_union.offset() + err_offset,
@@ -6497,12 +6494,12 @@ fn lowerTry(
}
// if we reach here it means error was not set, and we want the payload
- if (!pl_has_bits) {
+ if (!pl_has_bits and !operand_is_ptr) {
return .none;
}
const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
- if (isByRef(pl_ty, zcu, cg.target)) {
+ if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) {
return buildPointerOffset(cg, err_union, pl_offset, .new);
}
const payload = try cg.load(err_union, pl_ty, pl_offset);
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 6341f7e3d2..ad2d1b580f 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -878,7 +878,7 @@ pub fn generate(
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
air: *const Air,
- liveness: *const Air.Liveness,
+ liveness: *const ?Air.Liveness,
) codegen.CodeGenError!Mir {
_ = bin_file;
const zcu = pt.zcu;
@@ -894,7 +894,7 @@ pub fn generate(
.gpa = gpa,
.pt = pt,
.air = air.*,
- .liveness = liveness.*,
+ .liveness = liveness.*.?,
.target = &mod.resolved_target.result,
.mod = mod,
.owner = .{ .nav_index = func.owner_nav },
@@ -1103,11 +1103,7 @@ const FormatAirData = struct {
inst: Air.Inst.Index,
};
fn formatAir(data: FormatAirData, w: *std.io.Writer) Writer.Error!void {
- // not acceptable implementation because it ignores `w`:
- //data.self.air.dumpInst(data.inst, data.self.pt, data.self.liveness);
- _ = data;
- _ = w;
- @panic("TODO: unimplemented");
+ data.self.air.writeInst(w, data.inst, data.self.pt, data.self.liveness);
}
fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(FormatAirData, formatAir) {
return .{ .data = .{ .self = self, .inst = inst } };
@@ -100674,11 +100670,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const ty_pl = air_datas[@intFromEnum(inst)].ty_pl;
const struct_field = cg.air.extraData(Air.StructField, ty_pl.payload).data;
var ops = try cg.tempsFromOperands(inst, .{struct_field.struct_operand});
- try ops[0].toOffset(cg.fieldOffset(
+ try ops[0].toOffset(@intCast(codegen.fieldOffset(
cg.typeOf(struct_field.struct_operand),
ty_pl.ty.toType(),
struct_field.field_index,
- ), cg);
+ zcu,
+ )), cg);
try ops[0].finish(inst, &.{struct_field.struct_operand}, &ops, cg);
},
.struct_field_ptr_index_0,
@@ -100688,7 +100685,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
=> |air_tag| {
const ty_op = air_datas[@intFromEnum(inst)].ty_op;
var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
- try ops[0].toOffset(cg.fieldOffset(
+ try ops[0].toOffset(@intCast(codegen.fieldOffset(
cg.typeOf(ty_op.operand),
ty_op.ty.toType(),
switch (air_tag) {
@@ -100698,7 +100695,8 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.struct_field_ptr_index_2 => 2,
.struct_field_ptr_index_3 => 3,
},
- ), cg);
+ zcu,
+ )), cg);
try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg);
},
.struct_field_val => {
@@ -168108,11 +168106,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
const ty_pl = air_datas[@intFromEnum(inst)].ty_pl;
const field_parent_ptr = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
var ops = try cg.tempsFromOperands(inst, .{field_parent_ptr.field_ptr});
- try ops[0].toOffset(-cg.fieldOffset(
+ try ops[0].toOffset(-@as(i32, @intCast(codegen.fieldOffset(
ty_pl.ty.toType(),
cg.typeOf(field_parent_ptr.field_ptr),
field_parent_ptr.field_index,
- ), cg);
+ zcu,
+ ))), cg);
try ops[0].finish(inst, &.{field_parent_ptr.field_ptr}, &ops, cg);
},
.wasm_memory_size, .wasm_memory_grow => unreachable,
@@ -168138,7 +168137,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.unused,
.unused,
},
- .dst_temps = .{ .{ .cc = .b }, .unused },
+ .dst_temps = .{ .{ .cc = .be }, .unused },
.clobbers = .{ .eflags = true },
.each = .{ .once = &.{
.{ ._, ._, .lea, .tmp1p, .lea(.tmp0), ._, ._ },
@@ -168162,7 +168161,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.unused,
.unused,
},
- .dst_temps = .{ .{ .cc = .b }, .unused },
+ .dst_temps = .{ .{ .cc = .be }, .unused },
.clobbers = .{ .eflags = true },
.each = .{ .once = &.{
.{ ._, ._, .lea, .tmp1p, .lea(.tmp0), ._, ._ },
@@ -168186,7 +168185,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
.unused,
.unused,
},
- .dst_temps = .{ .{ .cc = .b }, .unused },
+ .dst_temps = .{ .{ .cc = .be }, .unused },
.clobbers = .{ .eflags = true },
.each = .{ .once = &.{
.{ ._, ._, .lea, .tmp1p, .lea(.tmp0), ._, ._ },
@@ -174809,18 +174808,6 @@ fn airStore(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
}
-fn fieldOffset(self: *CodeGen, ptr_agg_ty: Type, ptr_field_ty: Type, field_index: u32) i32 {
- const pt = self.pt;
- const zcu = pt.zcu;
- const agg_ty = ptr_agg_ty.childType(zcu);
- return switch (agg_ty.containerLayout(zcu)) {
- .auto, .@"extern" => @intCast(agg_ty.structFieldOffset(field_index, zcu)),
- .@"packed" => @divExact(@as(i32, ptr_agg_ty.ptrInfo(zcu).packed_offset.bit_offset) +
- (if (zcu.typeToStruct(agg_ty)) |loaded_struct| pt.structPackedFieldBitOffset(loaded_struct, field_index) else 0) -
- ptr_field_ty.ptrInfo(zcu).packed_offset.bit_offset, 8),
- };
-}
-
fn genUnOp(self: *CodeGen, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: Air.Inst.Ref) !MCValue {
const pt = self.pt;
const zcu = pt.zcu;
@@ -179309,10 +179296,13 @@ fn lowerSwitchBr(
} else undefined;
const table_start: u31 = @intCast(cg.mir_table.items.len);
{
- const condition_index_reg = if (condition_index.isRegister())
- condition_index.getReg().?
- else
- try cg.copyToTmpRegister(.usize, condition_index);
+ const condition_index_reg = condition_index_reg: {
+ if (condition_index.isRegister()) {
+ const condition_index_reg = condition_index.getReg().?;
+ if (condition_index_reg.isClass(.general_purpose)) break :condition_index_reg condition_index_reg;
+ }
+ break :condition_index_reg try cg.copyToTmpRegister(.usize, condition_index);
+ };
const condition_index_lock = cg.register_manager.lockReg(condition_index_reg);
defer if (condition_index_lock) |lock| cg.register_manager.unlockReg(lock);
try cg.truncateRegister(condition_ty, condition_index_reg);
@@ -184575,7 +184565,7 @@ fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void {
}
const elem_abi_size: u32 = @intCast(elem_ty.abiSize(zcu));
const elem_abi_bits = elem_abi_size * 8;
- const elem_off = pt.structPackedFieldBitOffset(loaded_struct, elem_i);
+ const elem_off = zcu.structPackedFieldBitOffset(loaded_struct, elem_i);
const elem_byte_off: i32 = @intCast(elem_off / elem_abi_bits * elem_abi_size);
const elem_bit_off = elem_off % elem_abi_bits;
const elem_mcv = try self.resolveInst(elem);
@@ -185625,21 +185615,19 @@ fn resolveCallingConventionValues(
fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
@branchHint(.cold);
const zcu = cg.pt.zcu;
- switch (cg.owner) {
- .nav_index => |i| return zcu.codegenFail(i, format, args),
- .lazy_sym => |s| return zcu.codegenFailType(s.ty, format, args),
- }
- return error.CodegenFail;
+ return switch (cg.owner) {
+ .nav_index => |i| zcu.codegenFail(i, format, args),
+ .lazy_sym => |s| zcu.codegenFailType(s.ty, format, args),
+ };
}
fn failMsg(cg: *CodeGen, msg: *Zcu.ErrorMsg) error{ OutOfMemory, CodegenFail } {
@branchHint(.cold);
const zcu = cg.pt.zcu;
- switch (cg.owner) {
- .nav_index => |i| return zcu.codegenFailMsg(i, msg),
- .lazy_sym => |s| return zcu.codegenFailTypeMsg(s.ty, msg),
- }
- return error.CodegenFail;
+ return switch (cg.owner) {
+ .nav_index => |i| zcu.codegenFailMsg(i, msg),
+ .lazy_sym => |s| zcu.codegenFailTypeMsg(s.ty, msg),
+ };
}
fn parseRegName(name: []const u8) ?Register {
@@ -191932,18 +191920,15 @@ const Select = struct {
error.InvalidInstruction => {
const fixes = @tagName(mir_tag[0]);
const fixes_blank = std.mem.indexOfScalar(u8, fixes, '_').?;
- return s.cg.fail(
- "invalid instruction: '{s}{s}{s} {s} {s} {s} {s}'",
- .{
- fixes[0..fixes_blank],
- @tagName(mir_tag[1]),
- fixes[fixes_blank + 1 ..],
- @tagName(mir_ops[0]),
- @tagName(mir_ops[1]),
- @tagName(mir_ops[2]),
- @tagName(mir_ops[3]),
- },
- );
+ return s.cg.fail("invalid instruction: '{s}{s}{s} {s} {s} {s} {s}'", .{
+ fixes[0..fixes_blank],
+ @tagName(mir_tag[1]),
+ fixes[fixes_blank + 1 ..],
+ @tagName(mir_ops[0]),
+ @tagName(mir_ops[1]),
+ @tagName(mir_ops[2]),
+ @tagName(mir_ops[3]),
+ });
},
else => |e| return e,
};
@@ -194435,6 +194420,18 @@ fn select(
while (true) for (pattern.src[0..src_temps.len], src_temps) |src_pattern, *src_temp| {
if (try src_pattern.convert(src_temp, cg)) break;
} else break;
+ var src_locks: [s_src_temps.len][2]?RegisterLock = @splat(@splat(null));
+ for (src_locks[0..src_temps.len], src_temps) |*locks, src_temp| {
+ const regs: [2]Register = switch (src_temp.tracking(cg).short) {
+ else => continue,
+ .register => |reg| .{ reg, .none },
+ .register_pair => |regs| regs,
+ };
+ for (regs, locks) |reg, *lock| {
+ if (reg == .none) continue;
+ lock.* = cg.register_manager.lockRegIndex(RegisterManager.indexOfRegIntoTracked(reg) orelse continue);
+ }
+ }
@memcpy(s_src_temps[0..src_temps.len], src_temps);
std.mem.swap(Temp, &s_src_temps[pattern.commute[0]], &s_src_temps[pattern.commute[1]]);
@@ -194453,6 +194450,7 @@ fn select(
}
assert(s.top == 0);
+ for (src_locks) |locks| for (locks) |lock| if (lock) |reg| cg.register_manager.unlockReg(reg);
for (tmp_locks) |locks| for (locks) |lock| if (lock) |reg| cg.register_manager.unlockReg(reg);
for (dst_locks) |locks| for (locks) |lock| if (lock) |reg| cg.register_manager.unlockReg(reg);
caller_preserved: {
diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig
index da15dc6bfb..49c67620d5 100644
--- a/src/arch/x86_64/Emit.zig
+++ b/src/arch/x86_64/Emit.zig
@@ -168,11 +168,12 @@ pub fn emitMir(emit: *Emit) Error!void {
else if (emit.bin_file.cast(.macho)) |macho_file|
macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
- else if (emit.bin_file.cast(.coff)) |coff_file| sym_index: {
- const atom = coff_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym) catch |err|
- return emit.fail("{s} creating lazy symbol", .{@errorName(err)});
- break :sym_index coff_file.getAtom(atom).getSymbolIndex().?;
- } else if (emit.bin_file.cast(.plan9)) |p9_file|
+ else if (emit.bin_file.cast(.coff)) |coff_file|
+ if (coff_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym)) |atom|
+ coff_file.getAtom(atom).getSymbolIndex().?
+ else |err|
+ return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
+ else if (emit.bin_file.cast(.plan9)) |p9_file|
p9_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym) catch |err|
return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
else