use crate::arm::arm_instr_info::ArmOpcode;
use crate::opcode::Opcode;
use super::arm_isel_table::IselPattern;
#[derive(Debug, Default, Clone)]
pub struct SVE21IselStats {
pub total_patterns: usize,
pub quad_load_store: usize,
pub multi_vector_extend: usize,
pub interleave_deinterleave: usize,
pub quad_tbl: usize,
pub fp8_ops: usize,
pub quad_arith: usize,
pub ptrue_patterns: usize,
pub frinto_patterns: usize,
}
pub struct SVE21IselTable {
pub patterns: Vec<IselPattern>,
pub stats: SVE21IselStats,
}
pub fn sve21_isel_table() -> Vec<IselPattern> {
let mut table = Vec::new();
table.push(IselPattern {
ir_opcode: Opcode::Load,
description: "ld1q {z0.q-z3.q}, p0/z, [x0] — SVE2.1 quad-word load 4×128-bit",
result_opcode: ArmOpcode::LD1Q_SVE,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Load,
description: "ld1q {z0.q-z1.q}, p0/z, [x0] — SVE2.1 quad-word load 2×128-bit",
result_opcode: ArmOpcode::LD1Q_SVE,
priority: 101,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Load,
description: "ld1q {z0.q}, p0/z, [x0] — SVE2.1 quad-word load 1×128-bit",
result_opcode: ArmOpcode::LD1Q_SVE,
priority: 102,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Store,
description: "st1q {z0.q-z3.q}, p0, [x0] — SVE2.1 quad-word store 4×128-bit",
result_opcode: ArmOpcode::ST1Q_SVE,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Store,
description: "st1q {z0.q-z1.q}, p0, [x0] — SVE2.1 quad-word store 2×128-bit",
result_opcode: ArmOpcode::ST1Q_SVE,
priority: 101,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Store,
description: "st1q {z0.q}, p0, [x0] — SVE2.1 quad-word store 1×128-bit",
result_opcode: ArmOpcode::ST1Q_SVE,
priority: 102,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "sunpk {z0.h-z3.h}, z0.b — SVE2.1 signed unpack 8→16-bit, 4 vectors",
result_opcode: ArmOpcode::SUNPK_SVE21,
priority: 100,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "sunpk {z0.s-z3.s}, z0.h — SVE2.1 signed unpack 16→32-bit, 4 vectors",
result_opcode: ArmOpcode::SUNPK_SVE21,
priority: 101,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "sunpk {z0.d-z3.d}, z0.s — SVE2.1 signed unpack 32→64-bit, 4 vectors",
result_opcode: ArmOpcode::SUNPK_SVE21,
priority: 102,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "uunpk {z0.h-z3.h}, z0.b — SVE2.1 unsigned unpack 8→16-bit, 4 vectors",
result_opcode: ArmOpcode::UUNPK_SVE21,
priority: 100,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "uunpk {z0.s-z3.s}, z0.h — SVE2.1 unsigned unpack 16→32-bit, 4 vectors",
result_opcode: ArmOpcode::UUNPK_SVE21,
priority: 101,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "uunpk {z0.d-z3.d}, z0.s — SVE2.1 unsigned unpack 32→64-bit, 4 vectors",
result_opcode: ArmOpcode::UUNPK_SVE21,
priority: 102,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "sunpk {z0.h-z1.h}, z0.b — SVE2.1 signed unpack 8→16-bit, 2 vectors",
result_opcode: ArmOpcode::SUNPK_SVE21_2,
priority: 110,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "sunpk {z0.s-z1.s}, z0.h — SVE2.1 signed unpack 16→32-bit, 2 vectors",
result_opcode: ArmOpcode::SUNPK_SVE21_2,
priority: 111,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::SExt,
description: "sunpk {z0.d-z1.d}, z0.s — SVE2.1 signed unpack 32→64-bit, 2 vectors",
result_opcode: ArmOpcode::SUNPK_SVE21_2,
priority: 112,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "uunpk {z0.h-z1.h}, z0.b — SVE2.1 unsigned unpack 8→16-bit, 2 vectors",
result_opcode: ArmOpcode::UUNPK_SVE21_2,
priority: 110,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "uunpk {z0.s-z1.s}, z0.h — SVE2.1 unsigned unpack 16→32-bit, 2 vectors",
result_opcode: ArmOpcode::UUNPK_SVE21_2,
priority: 111,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ZExt,
description: "uunpk {z0.d-z1.d}, z0.s — SVE2.1 unsigned unpack 32→64-bit, 2 vectors",
result_opcode: ArmOpcode::UUNPK_SVE21_2,
priority: 112,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description:
"zipq1 {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 interleave even quad-word",
result_opcode: ArmOpcode::ZIPQ1_SVE21,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description:
"zipq2 {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 interleave odd quad-word",
result_opcode: ArmOpcode::ZIPQ2_SVE21,
priority: 101,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description:
"zipq1 {z0.q-z1.q}, {z0.q-z1.q}, {z2.q-z3.q} — SVE2.1 interleave even 2-reg quad-word",
result_opcode: ArmOpcode::ZIPQ1_SVE21_2,
priority: 110,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description:
"zipq2 {z0.q-z1.q}, {z0.q-z1.q}, {z2.q-z3.q} — SVE2.1 interleave odd 2-reg quad-word",
result_opcode: ArmOpcode::ZIPQ2_SVE21_2,
priority: 111,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description:
"uzpq1 {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 deinterleave even quad-word",
result_opcode: ArmOpcode::UZPQ1_SVE21,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description:
"uzpq2 {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 deinterleave odd quad-word",
result_opcode: ArmOpcode::UZPQ2_SVE21,
priority: 101,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description: "uzpq1 {z0.q-z1.q}, {z0.q-z1.q}, {z2.q-z3.q} — SVE2.1 deinterleave even 2-reg quad-word",
result_opcode: ArmOpcode::UZPQ1_SVE21_2,
priority: 110,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description:
"uzpq2 {z0.q-z1.q}, {z0.q-z1.q}, {z2.q-z3.q} — SVE2.1 deinterleave odd 2-reg quad-word",
result_opcode: ArmOpcode::UZPQ2_SVE21_2,
priority: 111,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description: "tblq {z0.q-z3.q}, {z4.q-z7.q}, {z8.q-z11.q} — SVE2.1 4-reg quad-word TBL",
result_opcode: ArmOpcode::TBLQ_SVE21,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description: "tblq {z0.q-z1.q}, {z2.q-z3.q}, {z4.q-z5.q} — SVE2.1 2-reg quad-word TBL",
result_opcode: ArmOpcode::TBLQ_SVE21_2,
priority: 101,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::ShuffleVector,
description: "tblq {z0.q}, {z1.q}, {z2.q} — SVE2.1 1-reg quad-word TBL",
result_opcode: ArmOpcode::TBLQ_SVE21_1,
priority: 102,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FPTrunc,
description: "f1cvtl z0.h, z1.b — SVE2.1 FP8 down-convert high→low precision (16→8-bit)",
result_opcode: ArmOpcode::F1CVTL_SVE21,
priority: 100,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FPExt,
description: "f2cvtl z0.b, z1.h — SVE2.1 FP8 up-convert low→high precision (8→16-bit)",
result_opcode: ArmOpcode::F2CVTL_SVE21,
priority: 100,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FPTrunc,
description: "f1cvtl z0.s, z1.h — SVE2.1 FP16→FP32 down-convert high→low precision",
result_opcode: ArmOpcode::F1CVTL_SVE21,
priority: 101,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FPExt,
description: "f2cvtl z0.h, z1.s — SVE2.1 FP32→FP16 up-convert low→high precision",
result_opcode: ArmOpcode::F2CVTL_SVE21,
priority: 101,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FPTrunc,
description: "f1cvtl {z0.h-z3.h}, z0.b — SVE2.1 multi-vector FP8 down-convert 4-reg",
result_opcode: ArmOpcode::F1CVTL_SVE21_MULTI,
priority: 110,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FPExt,
description: "f2cvtl {z0.b-z3.b}, z0.h — SVE2.1 multi-vector FP8 up-convert 4-reg",
result_opcode: ArmOpcode::F2CVTL_SVE21_MULTI,
priority: 110,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "frinto z0.h, p0/m, z1.h — SVE2.1 FP round-to-odd half-precision",
result_opcode: ArmOpcode::FRINTO_SVE21,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "frinto z0.s, p0/m, z1.s — SVE2.1 FP round-to-odd single-precision",
result_opcode: ArmOpcode::FRINTO_SVE21,
priority: 101,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "frinto z0.d, p0/m, z1.d — SVE2.1 FP round-to-odd double-precision",
result_opcode: ArmOpcode::FRINTO_SVE21,
priority: 102,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Call,
description: "ptrue p0.b, pow2 — SVE2.1 PTRUE with POW2 pattern predicate",
result_opcode: ArmOpcode::PTRUE_PAT,
priority: 100,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Call,
description: "ptrue p0.b, vl1 — SVE2.1 PTRUE with VL1 pattern predicate",
result_opcode: ArmOpcode::PTRUE_PAT,
priority: 101,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Call,
description: "ptrue p0.b, vl2 — SVE2.1 PTRUE with VL2 pattern predicate",
result_opcode: ArmOpcode::PTRUE_PAT,
priority: 102,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Call,
description: "ptrue p0.b, vl3 — SVE2.1 PTRUE with VL3 pattern predicate",
result_opcode: ArmOpcode::PTRUE_PAT,
priority: 103,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Call,
description: "ptrue p0.b, vl4 — SVE2.1 PTRUE with VL4 pattern predicate",
result_opcode: ArmOpcode::PTRUE_PAT,
priority: 104,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Call,
description: "ptrue p0.b, vl5 — SVE2.1 PTRUE with VL5 pattern predicate",
result_opcode: ArmOpcode::PTRUE_PAT,
priority: 105,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Call,
description: "ptrue p0.b, vl6 — SVE2.1 PTRUE with VL6 pattern predicate",
result_opcode: ArmOpcode::PTRUE_PAT,
priority: 106,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Call,
description: "ptrue p0.b, vl7 — SVE2.1 PTRUE with VL7 pattern predicate",
result_opcode: ArmOpcode::PTRUE_PAT,
priority: 107,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Call,
description: "ptrue p0.b, vl8 — SVE2.1 PTRUE with VL8 pattern predicate",
result_opcode: ArmOpcode::PTRUE_PAT,
priority: 108,
num_operands: 1,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fadd {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word FP add",
result_opcode: ArmOpcode::FADD_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "fsub {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word FP sub",
result_opcode: ArmOpcode::FSUB_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmul {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word FP mul",
result_opcode: ArmOpcode::FMUL_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word int add",
result_opcode: ArmOpcode::ADD_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Sub,
description: "sub {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word int sub",
result_opcode: ArmOpcode::SUB_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Shl,
description:
"lsl {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word shift left",
result_opcode: ArmOpcode::LSL_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::LShr,
description: "lsr {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word logical shift right",
result_opcode: ArmOpcode::LSR_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::AShr,
description: "asr {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word arithmetic shift right",
result_opcode: ArmOpcode::ASR_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::And,
description: "and {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word and",
result_opcode: ArmOpcode::AND_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Or,
description: "orr {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word orr",
result_opcode: ArmOpcode::ORR_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Xor,
description: "eor {z0.q-z3.q}, {z0.q-z3.q}, {z4.q-z7.q} — SVE2.1 4-reg quad-word eor",
result_opcode: ArmOpcode::EOR_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fadd {z0.q-z1.q}, {z0.q-z1.q}, {z2.q-z3.q} — SVE2.1 2-reg quad-word FP add",
result_opcode: ArmOpcode::FADD_SVE21_QUAD_2,
priority: 200,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "fsub {z0.q-z1.q}, {z0.q-z1.q}, {z2.q-z3.q} — SVE2.1 2-reg quad-word FP sub",
result_opcode: ArmOpcode::FSUB_SVE21_QUAD_2,
priority: 200,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmul {z0.q-z1.q}, {z0.q-z1.q}, {z2.q-z3.q} — SVE2.1 2-reg quad-word FP mul",
result_opcode: ArmOpcode::FMUL_SVE21_QUAD_2,
priority: 200,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Add,
description: "add {z0.q-z1.q}, {z0.q-z1.q}, {z2.q-z3.q} — SVE2.1 2-reg quad-word int add",
result_opcode: ArmOpcode::ADD_SVE21_QUAD_2,
priority: 200,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::Mul,
description: "mul {z0.q-z1.q}, {z0.q-z1.q}, {z2.q-z3.q} — SVE2.1 2-reg quad-word int mul",
result_opcode: ArmOpcode::MUL_SVE21_QUAD_2,
priority: 200,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description: "fadd{z0.b-z3.b}, {z0.b-z3.b}, {z4.b-z7.b} — SVE2.1 4-reg FP8 packed add",
result_opcode: ArmOpcode::FADD_SVE21_FP8,
priority: 300,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FSub,
description: "fsub{z0.b-z3.b}, {z0.b-z3.b}, {z4.b-z7.b} — SVE2.1 4-reg FP8 packed sub",
result_opcode: ArmOpcode::FSUB_SVE21_FP8,
priority: 300,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description: "fmul{z0.b-z3.b}, {z0.b-z3.b}, {z4.b-z7.b} — SVE2.1 4-reg FP8 packed mul",
result_opcode: ArmOpcode::FMUL_SVE21_FP8,
priority: 300,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FAdd,
description:
"fdot {z0.s-z3.s}, {z0.b-z3.b}, {z4.b-z7.b} — SVE2.1 4-reg FP8→FP32 dot product",
result_opcode: ArmOpcode::FDOT_SVE21,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table.push(IselPattern {
ir_opcode: Opcode::FMul,
description:
"fdot {z0.s-z3.s}, {z0.b-z3.b}, {z4.b-z7.b} — SVE2.1 4-reg FP8 fresh dot product",
result_opcode: ArmOpcode::FDOT_SVE21,
priority: 110,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
});
table
}
pub fn build_sve21_isel_table() -> SVE21IselTable {
let patterns = sve21_isel_table();
let total = patterns.len();
let quad_load_store = patterns
.iter()
.filter(|p| matches!(p.result_opcode, ArmOpcode::LD1Q_SVE | ArmOpcode::ST1Q_SVE))
.count();
let extend = patterns
.iter()
.filter(|p| {
matches!(
p.result_opcode,
ArmOpcode::SUNPK_SVE21
| ArmOpcode::UUNPK_SVE21
| ArmOpcode::SUNPK_SVE21_2
| ArmOpcode::UUNPK_SVE21_2
)
})
.count();
let interleave = patterns
.iter()
.filter(|p| {
matches!(
p.result_opcode,
ArmOpcode::ZIPQ1_SVE21
| ArmOpcode::ZIPQ2_SVE21
| ArmOpcode::ZIPQ1_SVE21_2
| ArmOpcode::ZIPQ2_SVE21_2
| ArmOpcode::UZPQ1_SVE21
| ArmOpcode::UZPQ2_SVE21
| ArmOpcode::UZPQ1_SVE21_2
| ArmOpcode::UZPQ2_SVE21_2
)
})
.count();
let tblq = patterns
.iter()
.filter(|p| {
matches!(
p.result_opcode,
ArmOpcode::TBLQ_SVE21 | ArmOpcode::TBLQ_SVE21_2 | ArmOpcode::TBLQ_SVE21_1
)
})
.count();
let fp8 = patterns
.iter()
.filter(|p| {
matches!(
p.result_opcode,
ArmOpcode::F1CVTL_SVE21
| ArmOpcode::F2CVTL_SVE21
| ArmOpcode::F1CVTL_SVE21_MULTI
| ArmOpcode::F2CVTL_SVE21_MULTI
| ArmOpcode::FADD_SVE21_FP8
| ArmOpcode::FSUB_SVE21_FP8
| ArmOpcode::FMUL_SVE21_FP8
| ArmOpcode::FDOT_SVE21
)
})
.count();
let quad_ops = patterns
.iter()
.filter(|p| {
matches!(
p.result_opcode,
ArmOpcode::FADD_SVE21_QUAD
| ArmOpcode::FSUB_SVE21_QUAD
| ArmOpcode::FMUL_SVE21_QUAD
| ArmOpcode::ADD_SVE21_QUAD
| ArmOpcode::SUB_SVE21_QUAD
| ArmOpcode::LSL_SVE21_QUAD
| ArmOpcode::LSR_SVE21_QUAD
| ArmOpcode::ASR_SVE21_QUAD
| ArmOpcode::AND_SVE21_QUAD
| ArmOpcode::ORR_SVE21_QUAD
| ArmOpcode::EOR_SVE21_QUAD
| ArmOpcode::FADD_SVE21_QUAD_2
| ArmOpcode::FSUB_SVE21_QUAD_2
| ArmOpcode::FMUL_SVE21_QUAD_2
| ArmOpcode::ADD_SVE21_QUAD_2
| ArmOpcode::MUL_SVE21_QUAD_2
)
})
.count();
let ptrue = patterns
.iter()
.filter(|p| matches!(p.result_opcode, ArmOpcode::PTRUE_PAT))
.count();
let frinto = patterns
.iter()
.filter(|p| matches!(p.result_opcode, ArmOpcode::FRINTO_SVE21))
.count();
SVE21IselTable {
patterns,
stats: SVE21IselStats {
total_patterns: total,
quad_load_store,
multi_vector_extend: extend,
interleave_deinterleave: interleave,
quad_tbl: tblq,
fp8_ops: fp8,
quad_arith: quad_ops,
ptrue_patterns: ptrue,
frinto_patterns: frinto,
},
}
}
pub struct SVE21IselEngine {
pub table: SVE21IselTable,
pub features: SVE21Features,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SVE21Features {
pub has_sve2p1: bool,
pub has_sve2p1_fp8: bool,
pub has_sve2p1_quad: bool,
pub allowed_quad_regs: u8, }
impl Default for SVE21Features {
fn default() -> Self {
Self {
has_sve2p1: false,
has_sve2p1_fp8: false,
has_sve2p1_quad: false,
allowed_quad_regs: 1,
}
}
}
impl SVE21Features {
pub fn sve21_full() -> Self {
Self {
has_sve2p1: true,
has_sve2p1_fp8: true,
has_sve2p1_quad: true,
allowed_quad_regs: 4,
}
}
pub fn has_feature(&self, feat: &str) -> bool {
match feat {
"sve2p1" => self.has_sve2p1,
"sve2p1_fp8" => self.has_sve2p1_fp8,
"sve2p1_quad" => self.has_sve2p1_quad,
_ => false,
}
}
}
impl SVE21IselEngine {
pub fn new(features: SVE21Features) -> Self {
let table = build_sve21_isel_table();
Self { table, features }
}
pub fn is_pattern_applicable(&self, pattern: &IselPattern) -> bool {
match pattern.required_feature {
Some("sve2p1") => self.features.has_sve2p1,
Some("sve2p1_fp8") => self.features.has_sve2p1_fp8,
Some("sve2p1_quad") => self.features.has_sve2p1_quad,
_ => true,
}
}
pub fn select_pattern(&self, ir_opcodes: &[Opcode]) -> Option<&IselPattern> {
let mut best: Option<&IselPattern> = None;
for p in &self.table.patterns {
if ir_opcodes.contains(&p.ir_opcode) && self.is_pattern_applicable(p) {
match best {
None => best = Some(p),
Some(b) if p.priority < b.priority => best = Some(p),
_ => {}
}
}
}
best
}
pub fn stats(&self) -> String {
format!(
"SVE2.1 ISel: {} patterns (quad load/store: {}, extend: {}, interleave: {}, TBLQ: {}, FP8: {}, quad arith: {}, PTRUE: {}, FRINTO: {})",
self.table.stats.total_patterns,
self.table.stats.quad_load_store,
self.table.stats.multi_vector_extend,
self.table.stats.interleave_deinterleave,
self.table.stats.quad_tbl,
self.table.stats.fp8_ops,
self.table.stats.quad_arith,
self.table.stats.ptrue_patterns,
self.table.stats.frinto_patterns,
)
}
}
pub mod sve21_opcodes {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SVE21ExtensionOpcode {
LD1Q_SVE,
ST1Q_SVE,
SUNPK_SVE21,
UUNPK_SVE21,
SUNPK_SVE21_2,
UUNPK_SVE21_2,
ZIPQ1_SVE21,
ZIPQ2_SVE21,
ZIPQ1_SVE21_2,
ZIPQ2_SVE21_2,
UZPQ1_SVE21,
UZPQ2_SVE21,
UZPQ1_SVE21_2,
UZPQ2_SVE21_2,
TBLQ_SVE21,
TBLQ_SVE21_2,
TBLQ_SVE21_1,
F1CVTL_SVE21,
F2CVTL_SVE21,
F1CVTL_SVE21_MULTI,
F2CVTL_SVE21_MULTI,
FRINTO_SVE21,
PTRUE_PAT,
FADD_SVE21_QUAD,
FSUB_SVE21_QUAD,
FMUL_SVE21_QUAD,
ADD_SVE21_QUAD,
SUB_SVE21_QUAD,
LSL_SVE21_QUAD,
LSR_SVE21_QUAD,
ASR_SVE21_QUAD,
AND_SVE21_QUAD,
ORR_SVE21_QUAD,
EOR_SVE21_QUAD,
FADD_SVE21_QUAD_2,
FSUB_SVE21_QUAD_2,
FMUL_SVE21_QUAD_2,
ADD_SVE21_QUAD_2,
MUL_SVE21_QUAD_2,
FADD_SVE21_FP8,
FSUB_SVE21_FP8,
FMUL_SVE21_FP8,
FDOT_SVE21,
}
pub fn map_to_arm_opcode(_op: SVE21ExtensionOpcode) -> Option<String> {
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SVE21VectorLength {
VL128,
VL256,
VL512,
VL1024,
VL2048,
}
impl SVE21VectorLength {
pub fn to_bits(&self) -> u32 {
match self {
SVE21VectorLength::VL128 => 128,
SVE21VectorLength::VL256 => 256,
SVE21VectorLength::VL512 => 512,
SVE21VectorLength::VL1024 => 1024,
SVE21VectorLength::VL2048 => 2048,
}
}
pub fn to_bytes(&self) -> u32 {
self.to_bits() / 8
}
pub fn quad_words_per_vector(&self) -> u32 {
self.to_bits() / 128
}
pub fn supports_multi_vector_4(&self) -> bool {
matches!(
self,
SVE21VectorLength::VL128
| SVE21VectorLength::VL256
| SVE21VectorLength::VL512
| SVE21VectorLength::VL1024
| SVE21VectorLength::VL2048
)
}
pub fn supports_quad_tiling(&self) -> bool {
self.to_bits() >= 256
}
}
#[derive(Debug, Clone)]
pub struct SVE21CostModel {
pub vl: SVE21VectorLength,
}
impl SVE21CostModel {
pub fn new(vl: SVE21VectorLength) -> Self {
Self { vl }
}
pub fn ld1q_4reg_cycles(&self) -> u32 {
self.vl.quad_words_per_vector()
}
pub fn quad_arith_cycles(&self) -> u32 {
self.vl.quad_words_per_vector() * 2
}
pub fn fp8_convert_cycles(&self) -> u32 {
4
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sve21_isel_table_builds() {
let table = build_sve21_isel_table();
assert!(table.stats.total_patterns > 30);
assert!(table.stats.quad_load_store > 0);
assert!(table.stats.multi_vector_extend > 0);
assert!(table.stats.interleave_deinterleave > 0);
assert!(table.stats.quad_tbl > 0);
assert!(table.stats.fp8_ops > 0);
assert!(table.stats.ptrue_patterns > 0);
assert!(table.stats.frinto_patterns > 0);
assert!(table.stats.quad_arith > 0);
}
#[test]
fn test_sve21_feature_flags() {
let f = SVE21Features::default();
assert!(!f.has_sve2p1);
assert!(!f.has_feature("sve2p1"));
let f = SVE21Features::sve21_full();
assert!(f.has_sve2p1);
assert!(f.has_feature("sve2p1"));
assert!(f.has_feature("sve2p1_fp8"));
assert!(f.has_feature("sve2p1_quad"));
assert_eq!(f.allowed_quad_regs, 4);
}
#[test]
fn test_sve21_isel_engine_select() {
let engine = SVE21IselEngine::new(SVE21Features::sve21_full());
let pat = engine.select_pattern(&[Opcode::Load]);
assert!(pat.is_some());
}
#[test]
fn test_sve21_isel_engine_pattern_applicable() {
let engine = SVE21IselEngine::new(SVE21Features::sve21_full());
let ld1q_pattern = IselPattern {
ir_opcode: Opcode::Load,
description: "test",
result_opcode: ArmOpcode::LD1Q_SVE,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1"),
cond: None,
};
assert!(engine.is_pattern_applicable(&ld1q_pattern));
let fp8_pattern = IselPattern {
ir_opcode: Opcode::FPTrunc,
description: "test",
result_opcode: ArmOpcode::F1CVTL_SVE21,
priority: 100,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
};
assert!(engine.is_pattern_applicable(&fp8_pattern));
let quad_pattern = IselPattern {
ir_opcode: Opcode::FAdd,
description: "test",
result_opcode: ArmOpcode::FADD_SVE21_QUAD,
priority: 100,
num_operands: 3,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_quad"),
cond: None,
};
assert!(engine.is_pattern_applicable(&quad_pattern));
}
#[test]
fn test_sve21_isel_pattern_disabled_features() {
let engine = SVE21IselEngine::new(SVE21Features::default());
let fp8_pattern = IselPattern {
ir_opcode: Opcode::FPTrunc,
description: "test",
result_opcode: ArmOpcode::F1CVTL_SVE21,
priority: 100,
num_operands: 2,
imm_constraint: None,
is_two_address: false,
required_feature: Some("sve2p1_fp8"),
cond: None,
};
assert!(!engine.is_pattern_applicable(&fp8_pattern));
}
#[test]
fn test_sve21_quad_regs_limited() {
let mut features = SVE21Features::sve21_full();
features.allowed_quad_regs = 2;
let engine = SVE21IselEngine::new(features);
let pat = engine.select_pattern(&[Opcode::FAdd]);
assert!(pat.is_some());
}
}