use std::collections::HashMap;
use crate::basic_block::BasicBlock;
use crate::codegen::{MachineBasicBlock, MachineFunction, MachineInstr, MachineOperand};
use crate::function::Function;
use crate::opcode::Opcode;
use crate::types::Type;
use crate::value::{Value, ValueRef};
use super::amdgpu_instr_info::{AmdgpuInstrInfo, AmdgpuOpcode};
use super::amdgpu_register_info::AmdgpuRegisterInfo;
use super::amdgpu_target_machine::AmdgpuTargetMachine;
pub struct AmdgpuInstructionSelector {
pub instr_info: AmdgpuInstrInfo,
pub reg_info: AmdgpuRegisterInfo,
pub target: AmdgpuTargetMachine,
}
impl AmdgpuInstructionSelector {
pub fn new(target: AmdgpuTargetMachine) -> Self {
Self {
instr_info: AmdgpuInstrInfo::new(),
reg_info: AmdgpuRegisterInfo::new(),
target,
}
}
pub fn select(
&self,
ir_opcode: Opcode,
dst_type: &Type,
src_types: &[Type],
) -> Option<AmdgpuOpcode> {
let is_f32 = Self::is_f32(dst_type);
let is_f64 = Self::is_f64(dst_type);
let is_i32 = Self::is_i32(dst_type);
let is_v2f16 = Self::is_v2f16(dst_type);
match ir_opcode {
Opcode::Add => {
if is_i32 {
Some(AmdgpuOpcode::VAddF32)
} else if is_f32 {
Some(AmdgpuOpcode::VAddF32)
} else if is_f64 {
Some(AmdgpuOpcode::VAddF64)
} else {
None
}
}
Opcode::Sub => {
if is_f32 {
Some(AmdgpuOpcode::VSubF32)
} else if is_f64 {
Some(AmdgpuOpcode::VSubF64)
} else {
None
}
}
Opcode::Mul => {
if is_f32 {
Some(AmdgpuOpcode::VMulF32Vop3)
} else if is_f64 {
Some(AmdgpuOpcode::VMulF64)
} else if is_v2f16 {
Some(AmdgpuOpcode::VPkMulF16)
} else {
None
}
}
Opcode::SDiv | Opcode::UDiv => None, Opcode::SRem | Opcode::URem => None,
Opcode::FAdd => {
if is_f32 {
Some(AmdgpuOpcode::VAddF32)
} else if is_f64 {
Some(AmdgpuOpcode::VAddF64)
} else {
None
}
}
Opcode::FSub => {
if is_f32 {
Some(AmdgpuOpcode::VSubF32)
} else {
None
}
}
Opcode::FMul => {
if is_f32 {
Some(AmdgpuOpcode::VMulF32Vop3)
} else {
None
}
}
Opcode::FDiv => {
if is_f32 {
Some(AmdgpuOpcode::VRcpF32)
}
else {
None
}
}
Opcode::FRem => None,
Opcode::And => Some(AmdgpuOpcode::VAndB32),
Opcode::Or => Some(AmdgpuOpcode::VOrB32),
Opcode::Xor => Some(AmdgpuOpcode::VXorB32),
Opcode::Shl => Some(AmdgpuOpcode::VLshlrevB32),
Opcode::LShr => Some(AmdgpuOpcode::VLshrrevB32),
Opcode::AShr => Some(AmdgpuOpcode::VAshrrevI32),
Opcode::ICmp => Some(AmdgpuOpcode::VCmpI32Eq),
Opcode::FCmp => Some(AmdgpuOpcode::VCmpF32Eq),
Opcode::SIToFP => {
if is_f32 {
Some(AmdgpuOpcode::VCvtF32I32)
} else {
None
}
}
Opcode::FPToSI => {
if is_f32 {
Some(AmdgpuOpcode::VCvtI32F32)
} else {
None
}
}
Opcode::Load => Some(AmdgpuOpcode::GlobalLoadDword),
Opcode::Store => Some(AmdgpuOpcode::GlobalStoreDword),
Opcode::Phi | Opcode::Select => Some(AmdgpuOpcode::VMovB32),
_ => None,
}
}
pub fn select_fma(&self, is_f32: bool) -> AmdgpuOpcode {
if is_f32 {
AmdgpuOpcode::VFmaF32
} else {
AmdgpuOpcode::VMadF64
}
}
pub fn cmp_writes_vcc(&self, opcode: AmdgpuOpcode) -> bool {
matches!(
opcode,
AmdgpuOpcode::VCmpF32Eq
| AmdgpuOpcode::VCmpF32Lt
| AmdgpuOpcode::VCmpF32Gt
| AmdgpuOpcode::VCmpU32Eq
| AmdgpuOpcode::VCmpI32Eq
)
}
pub fn use_vop3(&self, num_srcs: u32, has_imm: bool) -> bool {
num_srcs > 2 || has_imm
}
fn is_f32(ty: &Type) -> bool {
ty.is_float()
}
fn is_f64(ty: &Type) -> bool {
ty.is_float()
}
fn is_i32(ty: &Type) -> bool {
ty.is_integer()
}
fn is_v2f16(ty: &Type) -> bool {
false }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{Type, TypeKind};
#[test]
fn test_select_add_f32() {
let target = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let isel = AmdgpuInstructionSelector::new(target);
let f32 = Type::new(TypeKind::Float);
let result = isel.select(Opcode::Add, &f32, &[]);
assert_eq!(result, Some(AmdgpuOpcode::VAddF32));
}
#[test]
fn test_select_mul_f32() {
let target = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let isel = AmdgpuInstructionSelector::new(target);
let f32 = Type::new(TypeKind::Float);
let result = isel.select(Opcode::Mul, &f32, &[]);
assert_eq!(result, Some(AmdgpuOpcode::VMulF32Vop3));
}
#[test]
fn test_select_and() {
let target = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let isel = AmdgpuInstructionSelector::new(target);
let i32 = Type::new(TypeKind::Integer { bits: 32 });
let result = isel.select(Opcode::And, &i32, &[]);
assert_eq!(result, Some(AmdgpuOpcode::VAndB32));
}
#[test]
fn test_select_load() {
let target = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let isel = AmdgpuInstructionSelector::new(target);
let i32 = Type::new(TypeKind::Integer { bits: 32 });
let result = isel.select(Opcode::Load, &i32, &[]);
assert_eq!(result, Some(AmdgpuOpcode::GlobalLoadDword));
}
#[test]
fn test_select_store() {
let target = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let isel = AmdgpuInstructionSelector::new(target);
let i32 = Type::new(TypeKind::Integer { bits: 32 });
let result = isel.select(Opcode::Store, &i32, &[]);
assert_eq!(result, Some(AmdgpuOpcode::GlobalStoreDword));
}
#[test]
fn test_select_fma() {
let target = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let isel = AmdgpuInstructionSelector::new(target);
assert_eq!(isel.select_fma(true), AmdgpuOpcode::VFmaF32);
assert_eq!(isel.select_fma(false), AmdgpuOpcode::VMadF64);
}
#[test]
fn test_cmp_writes_vcc() {
let target = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let isel = AmdgpuInstructionSelector::new(target);
assert!(isel.cmp_writes_vcc(AmdgpuOpcode::VCmpF32Eq));
assert!(!isel.cmp_writes_vcc(AmdgpuOpcode::VAddF32));
}
#[test]
fn test_use_vop3() {
let target = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let isel = AmdgpuInstructionSelector::new(target);
assert!(isel.use_vop3(3, false));
assert!(!isel.use_vop3(2, false));
}
}