use super::amdgpu_register_info::*;
use super::amdgpu_target_machine::{AmdgpuIsaVersion, AmdgpuTargetMachine};
#[derive(Debug, Clone)]
pub struct AmdgpuKernelImplicitArgs {
pub workgroup_id: [u32; 3],
pub workgroup_size: u32,
pub local_size: [u32; 3],
pub dispatch_ptr: u64,
pub kernarg_segment_ptr: u64,
pub is_valid: bool,
}
impl AmdgpuKernelImplicitArgs {
pub fn new() -> Self {
Self {
workgroup_id: [0; 3],
workgroup_size: 0,
local_size: [0; 3],
dispatch_ptr: 0,
kernarg_segment_ptr: 0,
is_valid: true,
}
}
}
pub struct AmdgpuWorkItemId {
pub local_id_x: u32,
pub local_id_y: u32,
pub local_id_z: u32,
pub global_id_x: u32,
pub global_id_y: u32,
pub global_id_z: u32,
pub group_id_x: u32,
pub group_id_y: u32,
pub group_id_z: u32,
pub local_size_x: u32,
pub local_size_y: u32,
pub local_size_z: u32,
}
impl AmdgpuWorkItemId {
pub fn new(local_id: [u32; 3], group_id: [u32; 3], local_size: [u32; 3]) -> Self {
Self {
local_id_x: local_id[0],
local_id_y: local_id[1],
local_id_z: local_id[2],
global_id_x: group_id[0] * local_size[0] + local_id[0],
global_id_y: group_id[1] * local_size[1] + local_id[1],
global_id_z: group_id[2] * local_size[2] + local_id[2],
group_id_x: group_id[0],
group_id_y: group_id[1],
group_id_z: group_id[2],
local_size_x: local_size[0],
local_size_y: local_size[1],
local_size_z: local_size[2],
}
}
}
pub struct AmdgpuKernelLowering {
target: AmdgpuTargetMachine,
implicit_args: AmdgpuKernelImplicitArgs,
work_item_id: Option<AmdgpuWorkItemId>,
}
impl AmdgpuKernelLowering {
pub fn new(target: AmdgpuTargetMachine) -> Self {
Self {
target,
implicit_args: AmdgpuKernelImplicitArgs::new(),
work_item_id: None,
}
}
pub fn gen_prologue_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("; AMDGPU Kernel Prologue\n");
asm.push_str("; Save implicit kernel arguments\n");
asm.push_str(" s_mov_b32 s0, s0 ; workgroup_id_x in s0\n");
asm.push_str(" s_mov_b32 s1, s1 ; workgroup_id_y in s1\n");
asm.push_str(" s_mov_b32 s2, s2 ; workgroup_id_z in s2\n");
asm.push_str(" ; Compute work-item IDs\n");
asm.push_str(" v_mov_b32 v0, 0 ; local_id_x placeholder\n");
asm.push_str(" v_mov_b32 v1, 0 ; local_id_y placeholder\n");
asm.push_str(" v_mov_b32 v2, 0 ; local_id_z placeholder\n");
if self.target.features.has("flat-scratch") {
asm.push_str(" ; Initialize FLAT_SCRATCH\n");
asm.push_str(" s_mov_b32 flat_scratch_lo, 0\n");
asm.push_str(" s_mov_b32 flat_scratch_hi, 0\n");
}
asm.push_str(" ; Jump to kernel body\n");
asm
}
pub fn gen_epilogue_asm(&self) -> String {
let mut asm = String::new();
asm.push_str("; AMDGPU Kernel Epilogue\n");
asm.push_str(" s_endpgm\n");
asm
}
pub fn gen_work_item_id_ir(&self) -> String {
r#"
; AMDGPU Work-Item ID computation
%local_id_x = call i32 @llvm.amdgcn.workitem.id.x()
%local_id_y = call i32 @llvm.amdgcn.workitem.id.y()
%local_id_z = call i32 @llvm.amdgcn.workitem.id.z()
%group_id_x = call i32 @llvm.amdgcn.workgroup.id.x()
%group_id_y = call i32 @llvm.amdgcn.workgroup.id.y()
%group_id_z = call i32 @llvm.amdgcn.workgroup.id.z()
%local_size_x = call i32 @llvm.r600.read.local.size.x()
%local_size_y = call i32 @llvm.r600.read.local.size.y()
%local_size_z = call i32 @llvm.r600.read.local.size.z()
%global_id_x = add i32 %local_id_x, %group_id_x
%global_id_y = add i32 %local_id_y, %group_id_y
%global_id_z = add i32 %local_id_z, %group_id_z
"#
.to_string()
}
pub fn gen_intrinsic_decls() -> &'static str {
r#"
declare i32 @llvm.amdgcn.workitem.id.x()
declare i32 @llvm.amdgcn.workitem.id.y()
declare i32 @llvm.amdgcn.workitem.id.z()
declare i32 @llvm.amdgcn.workgroup.id.x()
declare i32 @llvm.amdgcn.workgroup.id.y()
declare i32 @llvm.amdgcn.workgroup.id.z()
declare i32 @llvm.r600.read.local.size.x()
declare i32 @llvm.r600.read.local.size.y()
declare i32 @llvm.r600.read.local.size.z()
declare i32 @llvm.amdgcn.dispatch.id()
declare i32 @llvm.amdgcn.dispatch.ptr()
"#
}
pub fn set_work_item_id(
&mut self,
local_id: [u32; 3],
group_id: [u32; 3],
local_size: [u32; 3],
) {
self.work_item_id = Some(AmdgpuWorkItemId::new(local_id, group_id, local_size));
}
}
#[derive(Debug, Clone)]
pub struct AmdgpuKernelDescriptor {
pub group_segment_fixed_size: u32,
pub private_segment_fixed_size: u32,
pub sgpr_count: u32,
pub vgpr_count: u32,
pub kernel_code_entry_byte_offset: u64,
pub uses_flat_scratch: bool,
pub uses_dynamic_lds: bool,
pub workgroup_group_segment_byte_size: u32,
}
impl AmdgpuKernelDescriptor {
pub fn new() -> Self {
Self {
group_segment_fixed_size: 0,
private_segment_fixed_size: 0,
sgpr_count: 0,
vgpr_count: 0,
kernel_code_entry_byte_offset: 256, uses_flat_scratch: false,
uses_dynamic_lds: false,
workgroup_group_segment_byte_size: 0,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(64);
bytes.extend_from_slice(&self.group_segment_fixed_size.to_le_bytes());
bytes.extend_from_slice(&self.private_segment_fixed_size.to_le_bytes());
bytes.extend_from_slice(&[0u8; 8]);
bytes.extend_from_slice(&self.kernel_code_entry_byte_offset.to_le_bytes());
bytes.extend_from_slice(&[0u8; 12]);
let pgm_rsrc1 = self.encode_pgm_rsrc1();
bytes.extend_from_slice(&pgm_rsrc1.to_le_bytes());
let pgm_rsrc2 = self.encode_pgm_rsrc2();
bytes.extend_from_slice(&pgm_rsrc2.to_le_bytes());
bytes.extend_from_slice(&[0u8; 8]);
bytes.extend_from_slice(&[0u8; 8]);
bytes
}
fn encode_pgm_rsrc1(&self) -> u32 {
let mut rsrc1: u32 = 0;
rsrc1 |= (self.vgpr_count.min(256) & 0x3F);
rsrc1 |= ((self.sgpr_count.min(104) / 8) & 0x0F) << 6;
rsrc1 |= 1 << 27; rsrc1
}
fn encode_pgm_rsrc2(&self) -> u32 {
let mut rsrc2: u32 = 0;
if self.uses_flat_scratch {
rsrc2 |= 1;
}
if self.uses_dynamic_lds {
rsrc2 |= 1 << 1;
}
rsrc2
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_implicit_args_default() {
let args = AmdgpuKernelImplicitArgs::new();
assert!(args.is_valid);
assert_eq!(args.workgroup_id, [0, 0, 0]);
}
#[test]
fn test_work_item_id_computation() {
let wid = AmdgpuWorkItemId::new([3, 2, 1], [0, 1, 0], [64, 1, 1]);
assert_eq!(wid.local_id_x, 3);
assert_eq!(wid.local_id_y, 2);
assert_eq!(wid.group_id_x, 0);
assert_eq!(wid.group_id_y, 1);
assert_eq!(wid.global_id_x, 3);
assert_eq!(wid.global_id_y, 3); }
#[test]
fn test_kernel_lowering_prologue() {
let tm = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let lowering = AmdgpuKernelLowering::new(tm);
let asm = lowering.gen_prologue_asm();
assert!(asm.contains("s_mov_b32"));
assert!(asm.contains("FLAT_SCRATCH"));
}
#[test]
fn test_kernel_lowering_epilogue() {
let tm = AmdgpuTargetMachine::from_cpu("gfx900").unwrap();
let lowering = AmdgpuKernelLowering::new(tm);
let asm = lowering.gen_epilogue_asm();
assert!(asm.contains("s_endpgm"));
}
#[test]
fn test_kernel_descriptor_basic() {
let mut kd = AmdgpuKernelDescriptor::new();
kd.sgpr_count = 16;
kd.vgpr_count = 32;
kd.uses_flat_scratch = true;
let bytes = kd.to_bytes(); assert_eq!(bytes.len(), 60);
}
#[test]
fn test_kernel_descriptor_pgm_rsrc1() {
let mut kd = AmdgpuKernelDescriptor::new();
kd.vgpr_count = 64;
kd.sgpr_count = 32;
let rsrc1 = kd.encode_pgm_rsrc1();
assert_eq!(rsrc1 & 0x3F, 0); assert_eq!((rsrc1 >> 6) & 0x0F, 4); }
}