use super::types::{AddressSpace, Modifier, Opcode, PtxType, SmTarget};
#[derive(Debug, Clone, Default)]
pub struct PtxModule {
pub version: (u8, u8),
pub target: SmTarget,
pub address_size: u8,
pub globals: Vec<GlobalDecl>,
pub kernels: Vec<KernelDef>,
pub functions: Vec<FunctionDef>,
}
#[derive(Debug, Clone)]
pub struct KernelDef {
pub name: String,
pub is_entry: bool,
pub params: Vec<Param>,
pub registers: Vec<RegisterDecl>,
pub shared_mem: Vec<SharedMemDecl>,
pub body: Vec<Statement>,
}
#[derive(Debug, Clone)]
pub struct FunctionDef {
pub name: String,
pub return_type: Option<PtxType>,
pub params: Vec<Param>,
pub registers: Vec<RegisterDecl>,
pub body: Vec<Statement>,
}
#[derive(Debug, Clone)]
pub struct GlobalDecl {
pub name: String,
pub space: AddressSpace,
pub ty: PtxType,
pub size: usize,
pub init: Option<Vec<u8>>,
}
#[derive(Debug, Clone)]
pub struct Param {
pub name: String,
pub ty: PtxType,
}
#[derive(Debug, Clone)]
pub struct RegisterDecl {
pub name: String,
pub ty: PtxType,
}
#[derive(Debug, Clone)]
pub struct SharedMemDecl {
pub name: String,
pub size: usize,
pub ty: PtxType,
}
#[derive(Debug, Clone)]
pub enum Statement {
Label(String),
Instruction(Instruction),
Directive(Directive),
Comment(String),
}
#[derive(Debug, Clone)]
pub struct Instruction {
pub opcode: Opcode,
pub modifiers: Vec<Modifier>,
pub operands: Vec<Operand>,
pub predicate: Option<Predicate>,
pub location: SourceLocation,
}
#[derive(Debug, Clone)]
pub enum Directive {
Loc { file: u32, line: u32, column: u32 },
Pragma(String),
Other(String),
}
#[derive(Debug, Clone)]
pub enum Operand {
Register(String),
Memory(String),
Immediate(i64),
ImmediateFloat(f64),
Label(String),
Vector(Vec<Operand>),
}
impl Operand {
pub fn is_register(&self) -> bool {
matches!(self, Operand::Register(_))
}
pub fn is_memory(&self) -> bool {
matches!(self, Operand::Memory(_))
}
pub fn as_register(&self) -> Option<&str> {
match self {
Operand::Register(name) => Some(name),
_ => None,
}
}
}
impl std::fmt::Display for Operand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Operand::Register(name) => write!(f, "{}", name),
Operand::Memory(addr) => write!(f, "{}", addr),
Operand::Immediate(val) => write!(f, "{}", val),
Operand::ImmediateFloat(val) => write!(f, "{}", val),
Operand::Label(name) => write!(f, "{}", name),
Operand::Vector(ops) => {
write!(f, "{{")?;
for (i, op) in ops.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", op)?;
}
write!(f, "}}")
}
}
}
}
#[derive(Debug, Clone)]
pub struct Predicate {
pub register: String,
pub negated: bool,
}
#[derive(Debug, Clone, Default)]
pub struct SourceLocation {
pub line: usize,
pub column: usize,
pub file: Option<String>,
}
impl std::fmt::Display for SourceLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(file) = &self.file {
write!(f, "{}:{}:{}", file, self.line, self.column)
} else {
write!(f, "{}:{}", self.line, self.column)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_operand_display() {
assert_eq!(format!("{}", Operand::Register("%r0".into())), "%r0");
assert_eq!(format!("{}", Operand::Memory("[%r0]".into())), "[%r0]");
assert_eq!(format!("{}", Operand::Immediate(42)), "42");
}
#[test]
fn test_source_location_display() {
let loc = SourceLocation {
line: 10,
column: 5,
file: Some("test.ptx".into()),
};
assert_eq!(format!("{}", loc), "test.ptx:10:5");
let loc = SourceLocation {
line: 10,
column: 5,
file: None,
};
assert_eq!(format!("{}", loc), "10:5");
}
#[test]
fn test_operand_type_checks() {
let reg = Operand::Register("%r0".into());
assert!(reg.is_register());
assert!(!reg.is_memory());
let mem = Operand::Memory("[%r0]".into());
assert!(!mem.is_register());
assert!(mem.is_memory());
}
}