use super::amdgpu_instr_info::{
AmdgpuEncodingFormat, AmdgpuInstrDesc, AmdgpuInstrInfo, AmdgpuOpcode,
};
use super::amdgpu_register_info::AmdgpuRegisterInfo;
pub struct AmdgpuAsmPrinter {
pub instr_info: AmdgpuInstrInfo,
pub reg_info: AmdgpuRegisterInfo,
output: String,
}
impl AmdgpuAsmPrinter {
pub fn new() -> Self {
Self {
instr_info: AmdgpuInstrInfo::new(),
reg_info: AmdgpuRegisterInfo::new(),
output: String::new(),
}
}
pub fn print_instruction(&mut self, opcode: AmdgpuOpcode, operands: &[String]) -> String {
self.output.clear();
let Some(desc) = self.instr_info.get(opcode) else {
return format!("s_nop 0 ; unknown opcode");
};
self.output.push_str(desc.mnemonic);
match desc.encoding_format {
AmdgpuEncodingFormat::SOP1 => {
if operands.len() >= 2 {
self.output
.push_str(&format!(" {}, {}", operands[0], operands[1]));
}
}
AmdgpuEncodingFormat::SOP2 => {
if operands.len() >= 3 {
self.output.push_str(&format!(
" {}, {}, {}",
operands[0], operands[1], operands[2]
));
}
}
AmdgpuEncodingFormat::SOPC => {
if operands.len() >= 2 {
self.output
.push_str(&format!(" {}, {}", operands[0], operands[1]));
}
}
AmdgpuEncodingFormat::SOPK => {
if operands.len() >= 2 {
self.output
.push_str(&format!(" {}, {}", operands[0], operands[1]));
}
}
AmdgpuEncodingFormat::SOPP => {
if operands.len() >= 1 {
self.output.push_str(&format!(" {}", operands[0]));
}
}
AmdgpuEncodingFormat::VOP1 => {
if operands.len() >= 2 {
self.output
.push_str(&format!(" {}, {}", operands[0], operands[1]));
}
}
AmdgpuEncodingFormat::VOP2 | AmdgpuEncodingFormat::VOPC => {
if operands.len() >= 3 {
self.output.push_str(&format!(
" {}, {}, {}",
operands[0], operands[1], operands[2]
));
}
}
AmdgpuEncodingFormat::VOP3 | AmdgpuEncodingFormat::VOP3P => {
if operands.len() >= 4 {
self.output.push_str(&format!(
" {}, {}, {}, {}",
operands[0], operands[1], operands[2], operands[3]
));
}
}
AmdgpuEncodingFormat::SMEM => {
self.output.push_str(&format!(
" {}, s[{}], {}",
operands.get(0).unwrap_or(&"s0".to_string()),
operands.get(1).unwrap_or(&"0".to_string()),
operands.get(2).unwrap_or(&"0".to_string()),
));
}
AmdgpuEncodingFormat::FLAT => {
self.output.push_str(&format!(
" {}, v[{}]",
operands.get(0).unwrap_or(&"v0".to_string()),
operands.get(1).unwrap_or(&"0".to_string()),
));
}
AmdgpuEncodingFormat::DS => {
self.output.push_str(&format!(
" {}, {}, {}",
operands.get(0).unwrap_or(&"v0".to_string()),
operands.get(1).unwrap_or(&"v1".to_string()),
operands.get(2).unwrap_or(&"0".to_string()),
));
}
AmdgpuEncodingFormat::MIMG => {
self.output.push_str(&format!(
" {}, v[{}], s[{}]",
operands.get(0).unwrap_or(&"v0".to_string()),
operands.get(1).unwrap_or(&"0".to_string()),
operands.get(2).unwrap_or(&"0:3".to_string()),
));
}
AmdgpuEncodingFormat::EXP => {
self.output.push_str(&format!(
" mrt0 {}, {}, {}, {}",
operands.get(0).unwrap_or(&"v0".to_string()),
operands.get(1).unwrap_or(&"v1".to_string()),
operands.get(2).unwrap_or(&"v2".to_string()),
operands.get(3).unwrap_or(&"v3".to_string()),
));
}
_ => {}
}
self.output.push_str(&format!(
" ; {} ({} bits)",
desc.mnemonic,
desc.encoding_format.size_bits()
));
self.output.clone()
}
pub fn print_kernel_prologue(&mut self, kernel_name: &str) -> String {
format!(
r#"{name}:
; Kernel: {name}
; Wavefront size: 64
; Implicit args in SGPRs:
; s0-s2: workgroup_id (x, y, z)
; s4-s5: workgroup_size
; s8-s9: kernarg_segment_ptr
s_load_dwordx2 s[4:5], s[8:9], 0x0
s_waitcnt lgkmcnt(0)
"#,
name = kernel_name
)
}
pub fn print_kernel_epilogue(&self) -> &'static str {
" s_endpgm\n"
}
pub fn print_comment(&mut self, comment: &str) -> String {
format!(" ; {}", comment)
}
pub fn print_label(&mut self, label: &str) -> String {
format!("{}:", label)
}
}
impl Default for AmdgpuAsmPrinter {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_print_s_nop() {
let mut printer = AmdgpuAsmPrinter::new();
let asm = printer.print_instruction(AmdgpuOpcode::SNop, &["0".into()]);
assert!(asm.contains("s_nop"));
}
#[test]
fn test_print_v_add_f32() {
let mut printer = AmdgpuAsmPrinter::new();
let asm = printer.print_instruction(
AmdgpuOpcode::VAddF32,
&["v0".into(), "v1".into(), "v2".into()],
);
assert!(asm.contains("v_add_f32"));
assert!(asm.contains("v0"));
assert!(asm.contains("v1"));
}
#[test]
fn test_print_s_load_dword() {
let mut printer = AmdgpuAsmPrinter::new();
let asm = printer.print_instruction(
AmdgpuOpcode::SLoadDword,
&["s5".into(), "2".into(), "0".into()],
);
assert!(asm.contains("s_load_dword"));
}
#[test]
fn test_print_global_load() {
let mut printer = AmdgpuAsmPrinter::new();
let asm = printer.print_instruction(
AmdgpuOpcode::GlobalLoadDword,
&["v5".into(), "v[2]".into(), "0".into()],
);
assert!(asm.contains("global_load_dword"));
}
#[test]
fn test_print_ds_write() {
let mut printer = AmdgpuAsmPrinter::new();
let asm = printer.print_instruction(
AmdgpuOpcode::DsWriteB32,
&["v0".into(), "42".into(), "0".into()],
);
assert!(asm.contains("ds_write_b32"));
}
#[test]
fn test_kernel_prologue() {
let mut printer = AmdgpuAsmPrinter::new();
let asm = printer.print_kernel_prologue("my_kernel");
assert!(asm.contains("my_kernel:"));
assert!(asm.contains("s_load_dwordx2"));
}
#[test]
fn test_print_image_sample() {
let mut printer = AmdgpuAsmPrinter::new();
let asm = printer.print_instruction(
AmdgpuOpcode::ImageSample,
&["v[0:3]".into(), "v[4:7]".into(), "s[0:3]".into()],
);
assert!(asm.contains("image_sample"));
}
#[test]
fn test_comment() {
let mut printer = AmdgpuAsmPrinter::new();
let asm = printer.print_comment("this is a comment");
assert!(asm.starts_with(" ;"));
assert!(asm.contains("this is a comment"));
}
#[test]
fn test_label() {
let mut printer = AmdgpuAsmPrinter::new();
let asm = printer.print_label("loop_start");
assert_eq!(asm, "loop_start:");
}
}