mod storage;
use self::storage::LiftStorage;
use crate::{
dr,
sr::{
instructions,
module,
ops,
storage::Token,
Constant, StructMember, Type,
},
};
use spirv;
use std::{borrow::Borrow, mem};
struct OpInfo {
op: Token<ops::Op>,
ty: Option<Token<Type>>,
}
impl Borrow<Token<ops::Op>> for OpInfo {
fn borrow(&self) -> &Token<ops::Op> {
&self.op
}
}
pub struct LiftContext {
types: LiftStorage<Type>,
constants: LiftStorage<Constant>,
blocks: LiftStorage<module::Block>,
ops: LiftStorage<ops::Op, OpInfo>,
}
include!("autogen_context.rs");
#[derive(Clone, Debug)]
pub enum OperandError {
WrongType,
WrongEnumValue,
Missing,
}
#[derive(Clone, Debug)]
pub enum InstructionError {
WrongOpcode,
MissingResult,
Operand(OperandError),
}
impl From<OperandError> for InstructionError {
fn from(error: OperandError) -> Self {
InstructionError::Operand(error)
}
}
#[derive(Clone, Debug)]
pub enum ConversionError {
MissingHeader,
MissingFunction,
MissingFunctionType,
MissingLabel,
MissingTerminator,
Instruction(InstructionError),
}
impl From<InstructionError> for ConversionError {
fn from(error: InstructionError) -> Self {
ConversionError::Instruction(error)
}
}
impl LiftContext {
pub fn convert(module: &dr::Module) -> Result<module::Module, ConversionError> {
let mut context = LiftContext {
types: LiftStorage::new(),
constants: LiftStorage::new(),
blocks: LiftStorage::new(),
ops: LiftStorage::new(),
};
let mut functions = Vec::new();
let entry_points = Vec::new();
for inst in module.types_global_values.iter() {
match context.lift_type(inst) {
Ok(value) => {
if let Some(id) = inst.result_id {
context.types.append_id(id, value);
}
continue
}
Err(InstructionError::WrongOpcode) => {},
Err(e) => panic!("Type lift error: {:?}", e),
}
match context.lift_constant(inst) {
Ok(value) => {
if let Some(id) = inst.result_id {
context.constants.append_id(id, value);
}
continue
}
Err(InstructionError::WrongOpcode) => {},
Err(e) => panic!("Constant lift error: {:?}", e),
}
}
for fun in module.functions.iter() {
let def = context.lift_function(
fun.def
.as_ref()
.ok_or(ConversionError::MissingFunction)?
)?;
for block in fun.blocks.iter() {
let mut arguments = Vec::new();
for inst in &block.instructions {
match inst.class.opcode {
spirv::Op::Line => {}
spirv::Op::Phi => {
match inst.operands[0] {
dr::Operand::IdRef(id) => {
let (_, info) = context.ops.lookup(id);
arguments.push(info.ty
.ok_or(InstructionError::MissingResult)?
);
}
_ => return Err(ConversionError::Instruction(
InstructionError::Operand(OperandError::Missing)
)),
};
}
_ => {
if let Some(id) = inst.result_id {
let op = context.lift_op(inst)?;
let types = &context.types;
let (token, entry) = context.ops.append(id, op);
entry.insert(OpInfo {
op: token,
ty: inst.result_type.map(|ty| *types.lookup(ty).1),
});
}
}
}
}
let terminator = context.lift_terminator(
block.instructions
.last()
.ok_or(ConversionError::MissingTerminator)?
)?;
context.blocks.append_id(
block.label.as_ref().unwrap().result_id.unwrap(),
module::Block {
arguments,
ops: Vec::new(),
terminator,
},
);
}
let start_label = fun.blocks[0].label.as_ref().unwrap().result_id.unwrap();
let start_block = context.blocks.lookup_token(start_label);
let blocks = mem::replace(&mut context.blocks, LiftStorage::new())
.unwrap();
functions.push(module::Function {
control: def.function_control,
result: context.types.append_id(1, Type::Void),
parameters: Vec::new(),
blocks,
start_block,
});
}
Ok(module::Module {
version: match module.header {
Some(ref header) => header.version,
None => return Err(ConversionError::MissingHeader),
},
capabilities: module.capabilities
.iter()
.map(|cap| context.lift_capability(cap).map(|cap| cap.capability))
.collect::<Result<_, InstructionError>>()?,
extensions: Vec::new(),
ext_inst_imports: Vec::new(),
memory_model: match module.memory_model {
Some(ref mm) => context.lift_memory_model(mm)?,
None => return Err(ConversionError::MissingHeader),
},
entry_points,
types: context.types.unwrap(),
constants: context.constants.unwrap(),
ops: context.ops.unwrap(),
functions,
})
}
fn lookup_jump(&self, destination: spirv::Word) -> module::Jump {
let (_, block) = self.blocks.lookup(destination);
module::Jump {
block: *block,
arguments: Vec::new(),
}
}
fn lift_constant(
&self, inst: &dr::Instruction
) -> Result<Constant, InstructionError> {
match inst.class.opcode {
spirv::Op::ConstantTrue => Ok(Constant::Bool(true)),
spirv::Op::ConstantFalse => Ok(Constant::Bool(false)),
spirv::Op::Constant => {
match inst.result_type {
Some(id) => {
let oper = inst.operands
.first()
.ok_or(InstructionError::Operand(OperandError::Missing))?;
let (value, width) = match *self.types.lookup(id).0 {
Type::Int { signedness: 0, width } => match *oper {
dr::Operand::LiteralInt32(v) => (Constant::UInt(v), width),
_ => return Err(InstructionError::Operand(OperandError::WrongType)),
},
Type::Int { width, .. } => match *oper {
dr::Operand::LiteralInt32(v) => (Constant::Int(v as i32), width),
_ => return Err(InstructionError::Operand(OperandError::WrongType)),
},
Type::Float { width } => match *oper {
dr::Operand::LiteralFloat32(v) => (Constant::Float(v), width),
_ => return Err(InstructionError::Operand(OperandError::WrongType)),
},
_ => return Err(InstructionError::MissingResult),
};
if width > 32 {
}
Ok(value)
}
_ => return Err(InstructionError::MissingResult),
}
}
spirv::Op::ConstantComposite => {
let mut vec = Vec::with_capacity(inst.operands.len());
for oper in inst.operands.iter() {
let token = match *oper {
dr::Operand::IdRef(v) => self.constants.lookup_token(v),
_ => return Err(InstructionError::Operand(OperandError::WrongType)),
};
vec.push(token);
}
Ok(Constant::Composite(vec))
}
spirv::Op::ConstantSampler => {
if inst.operands.len() < 3 {
return Err(InstructionError::Operand(OperandError::Missing))
}
Ok(Constant::Sampler {
addressing_mode: match inst.operands[0] {
dr::Operand::SamplerAddressingMode(v) => v,
_ => return Err(InstructionError::Operand(OperandError::WrongType)),
},
normalized: match inst.operands[1] {
dr::Operand::LiteralInt32(v) => v != 0,
_ => return Err(InstructionError::Operand(OperandError::WrongType)),
},
filter_mode: match inst.operands[2] {
dr::Operand::SamplerFilterMode(v) => v,
_ => return Err(InstructionError::Operand(OperandError::WrongType)),
},
})
}
spirv::Op::ConstantNull => Ok(Constant::Null),
_ => Err(InstructionError::WrongOpcode)
}
}
}