use std::collections::HashMap;
use super::amdgpu_kernel_lowering::AmdgpuKernelDescriptor;
use super::amdgpu_target_machine::AmdgpuIsaVersion;
pub const NT_AMDGPU_CODE_PROPERTY: u32 = 0x20000001;
pub const NT_AMDGPU_ISA_VERSION: u32 = 0x20000002;
pub const NT_AMDGPU_METADATA: u32 = 0x20000003;
#[derive(Debug)]
pub struct AmdgpuCodeObject {
pub isa_version: AmdgpuIsaVersion,
pub text_section: Vec<u8>,
pub kernel_descriptors: HashMap<String, AmdgpuKernelDescriptor>,
pub notes: Vec<AmdgpuNoteRecord>,
elf_data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct AmdgpuNoteRecord {
pub note_type: u32,
pub name: String,
pub desc: Vec<u8>,
}
impl AmdgpuCodeObject {
pub fn new(isa: AmdgpuIsaVersion) -> Self {
Self {
isa_version: isa,
text_section: Vec::new(),
kernel_descriptors: HashMap::new(),
notes: Vec::new(),
elf_data: Vec::new(),
}
}
pub fn add_kernel(&mut self, name: &str, code: &[u8], descriptor: AmdgpuKernelDescriptor) {
self.kernel_descriptors.insert(name.to_string(), descriptor);
self.text_section.extend_from_slice(code);
}
pub fn add_isa_note(&mut self) {
let mut isa_data = Vec::new();
let (major, minor, stepping) = self.isa_components();
isa_data.extend_from_slice(&major.to_le_bytes());
isa_data.extend_from_slice(&minor.to_le_bytes());
isa_data.extend_from_slice(&stepping.to_le_bytes());
self.notes.push(AmdgpuNoteRecord {
note_type: NT_AMDGPU_ISA_VERSION,
name: "AMD".to_string(),
desc: isa_data,
});
}
pub fn add_kernel_notes(&mut self) {
for (name, desc) in &self.kernel_descriptors {
let prop_bytes = desc.to_bytes();
self.notes.push(AmdgpuNoteRecord {
note_type: NT_AMDGPU_CODE_PROPERTY,
name: name.clone(),
desc: prop_bytes,
});
}
}
fn isa_components(&self) -> (u16, u16, u16) {
match self.isa_version {
AmdgpuIsaVersion::GFX600 => (6, 0, 0),
AmdgpuIsaVersion::GFX700 => (7, 0, 0),
AmdgpuIsaVersion::GFX800 => (8, 0, 2),
AmdgpuIsaVersion::GFX803 => (8, 0, 3),
AmdgpuIsaVersion::GFX900 => (9, 0, 0),
AmdgpuIsaVersion::GFX906 => (9, 0, 6),
AmdgpuIsaVersion::GFX908 => (9, 0, 8),
AmdgpuIsaVersion::GFX90A => (9, 0, 10),
AmdgpuIsaVersion::GFX1010 => (10, 1, 0),
AmdgpuIsaVersion::GFX1030 => (10, 3, 0),
AmdgpuIsaVersion::GFX1100 => (11, 0, 0),
AmdgpuIsaVersion::GFX1150 => (11, 5, 0),
}
}
pub fn emit_elf(&mut self) -> Vec<u8> {
self.elf_data.clear();
self.add_isa_note();
self.add_kernel_notes();
self.build_elf_header();
self.build_section_headers();
self.build_note_section();
self.elf_data.clone()
}
fn build_elf_header(&mut self) {
let mut header = Vec::with_capacity(64);
header.push(0x7F); header.push(b'E'); header.push(b'L'); header.push(b'F'); header.push(2); header.push(1); header.push(1); header.push(0); header.extend_from_slice(&[0u8; 8]);
header.extend_from_slice(&1u16.to_le_bytes());
header.extend_from_slice(&0xE0u16.to_le_bytes());
header.extend_from_slice(&1u32.to_le_bytes());
header.extend_from_slice(&[0u8; 16]);
let text_offset = 64u64;
header.extend_from_slice(&text_offset.to_le_bytes());
let e_flags = self.elf_flags();
header.extend_from_slice(&e_flags.to_le_bytes());
header.extend_from_slice(&64u16.to_le_bytes());
header.extend_from_slice(&0u16.to_le_bytes());
header.extend_from_slice(&0u16.to_le_bytes());
header.extend_from_slice(&64u16.to_le_bytes());
header.extend_from_slice(&3u16.to_le_bytes());
header.extend_from_slice(&2u16.to_le_bytes());
self.elf_data = header;
}
fn build_section_headers(&mut self) {
}
fn build_note_section(&mut self) {
for note in &self.notes {
let name_bytes = note.name.as_bytes();
let name_size = name_bytes.len() as u32;
let name_padded = (name_size + 3) & !3;
let desc_size = note.desc.len() as u32;
let desc_padded = (desc_size + 3) & !3;
self.elf_data.extend_from_slice(&name_size.to_le_bytes());
self.elf_data.extend_from_slice(&desc_size.to_le_bytes());
self.elf_data
.extend_from_slice(¬e.note_type.to_le_bytes());
self.elf_data.extend_from_slice(name_bytes);
if name_padded > name_size {
self.elf_data
.extend_from_slice(&vec![0u8; (name_padded - name_size) as usize]);
}
self.elf_data.extend_from_slice(¬e.desc);
if desc_padded > desc_size {
self.elf_data
.extend_from_slice(&vec![0u8; (desc_padded - desc_size) as usize]);
}
}
}
fn elf_flags(&self) -> u32 {
match self.isa_version {
AmdgpuIsaVersion::GFX600 => 0x0600,
AmdgpuIsaVersion::GFX700 => 0x0700,
AmdgpuIsaVersion::GFX800 => 0x0800,
AmdgpuIsaVersion::GFX803 => 0x0803,
AmdgpuIsaVersion::GFX900 => 0x0900,
AmdgpuIsaVersion::GFX906 => 0x0906,
AmdgpuIsaVersion::GFX908 => 0x0908,
AmdgpuIsaVersion::GFX90A => 0x090A,
AmdgpuIsaVersion::GFX1010 => 0x0A10,
AmdgpuIsaVersion::GFX1030 => 0x0A30,
AmdgpuIsaVersion::GFX1100 => 0x0B00,
AmdgpuIsaVersion::GFX1150 => 0x0B50,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_code_object_create() {
let co = AmdgpuCodeObject::new(AmdgpuIsaVersion::GFX900);
assert!(co.text_section.is_empty());
assert!(co.kernel_descriptors.is_empty());
}
#[test]
fn test_code_object_add_kernel() {
let mut co = AmdgpuCodeObject::new(AmdgpuIsaVersion::GFX900);
let code = vec![0xBF, 0x80, 0x00, 0x00]; let desc = AmdgpuKernelDescriptor::new();
co.add_kernel("test_kernel", &code, desc);
assert_eq!(co.kernel_descriptors.len(), 1);
assert_eq!(co.text_section.len(), 4);
}
#[test]
fn test_code_object_emit_elf() {
let mut co = AmdgpuCodeObject::new(AmdgpuIsaVersion::GFX900);
let desc = AmdgpuKernelDescriptor::new();
co.add_kernel("kernel0", &[0xBF, 0x80, 0x00, 0x00], desc);
let elf = co.emit_elf();
assert!(elf.len() > 64); assert_eq!(elf[0], 0x7F); assert_eq!(elf[1], b'E');
assert_eq!(elf[2], b'L');
assert_eq!(elf[3], b'F');
}
#[test]
fn test_code_object_elf_flags() {
let co = AmdgpuCodeObject::new(AmdgpuIsaVersion::GFX1030);
assert_eq!(co.elf_flags(), 0x0A30);
}
#[test]
fn test_note_record() {
let note = AmdgpuNoteRecord {
note_type: NT_AMDGPU_ISA_VERSION,
name: "AMD".into(),
desc: vec![9, 0, 6, 0, 0, 0], };
assert_eq!(note.note_type, NT_AMDGPU_ISA_VERSION);
}
}