metal2vulkan 0.1.0

Translate Metal AIR / LLVM IR to Vulkan SPIR-V with a native Rust emitter
Documentation
//! Byte-neutral responsibility split of the former monolith impl; see the parent module.

use super::*;

impl Emitter {
    pub(in crate::native::emitter) fn atomic_i32_pointer_id(
        &mut self,
        ptr_arg: &TypedValue,
        instructions: &mut Vec<Instruction>,
    ) -> Result<Word, String> {
        if let LlValue::Local(name) = &ptr_arg.value {
            if let Some(raw) = self.raw_offsets.get(name).cloned() {
                return self.emit_raw_word_pointer_for_access(&raw, 0, Some(4), instructions);
            }
        }
        if matches!(&ptr_arg.value, LlValue::Local(name) if self.unmodeled_pointers.contains(name))
        {
            return self.unmodeled_atomic_i32_pointer();
        }
        let ptr = self.value_id_in(&ptr_arg.value, &ptr_arg.ty, instructions)?;
        let Some(pointee) = self.pointer_pointee_for_value(&ptr_arg.value)? else {
            return Ok(ptr);
        };
        let pointee = self.resolve_type(&pointee)?;
        if pointee == LlType::Int(32) {
            return Ok(ptr);
        }
        if pointee == LlType::Float {
            return self.bitcast_pointer_to_atomic_i32(ptr_arg, ptr, instructions);
        }

        let LlType::Struct(fields) = pointee else {
            return Err(format!(
                "native emitter: atomic i32 pointer targets {pointee:?}"
            ));
        };
        let [field] = fields.as_slice() else {
            return Err(format!(
                "native emitter: atomic i32 pointer targets struct with {} fields",
                fields.len()
            ));
        };
        let field = self.resolve_type(field)?;
        if field != LlType::Int(32) {
            return Err(format!(
                "native emitter: atomic i32 pointer targets struct field {field:?}"
            ));
        }
        let LlType::Ptr(addrspace) = self.resolve_type(&ptr_arg.ty)? else {
            return Err(format!(
                "native emitter: atomic i32 pointer argument has type {:?}",
                ptr_arg.ty
            ));
        };
        let ptr_type = self.ptr_type_id(llvm_pointer_storage(addrspace)?, &LlType::Int(32))?;
        let zero = self.const_uint(0)?;
        let result = self.fresh();
        instructions.push(Self::inst(
            Op::InBoundsAccessChain,
            Some(ptr_type),
            Some(result),
            vec![Operand::IdRef(ptr), Operand::IdRef(zero)],
        ));
        Ok(result)
    }

    pub(in crate::native::emitter) fn bitcast_pointer_to_atomic_i32(
        &mut self,
        ptr_arg: &TypedValue,
        ptr: Word,
        instructions: &mut Vec<Instruction>,
    ) -> Result<Word, String> {
        let LlType::Ptr(addrspace) = self.resolve_type(&ptr_arg.ty)? else {
            return Err(format!(
                "native emitter: atomic i32 pointer argument has type {:?}",
                ptr_arg.ty
            ));
        };
        let storage = self.pointer_storage_for(&ptr_arg.value, addrspace)?;
        let ptr_type = self.ptr_type_id(storage, &LlType::Int(32))?;
        let result = self.fresh();
        instructions.push(Self::inst(
            Op::Bitcast,
            Some(ptr_type),
            Some(result),
            vec![Operand::IdRef(ptr)],
        ));
        Ok(result)
    }

    pub(in crate::native::emitter) fn atomic_f32_pointer_id(
        &mut self,
        ptr_arg: &TypedValue,
        instructions: &mut Vec<Instruction>,
    ) -> Result<Word, String> {
        if matches!(&ptr_arg.value, LlValue::Local(name) if self.unmodeled_pointers.contains(name))
        {
            return self.unmodeled_atomic_f32_pointer();
        }
        let ptr = self.value_id_in(&ptr_arg.value, &ptr_arg.ty, instructions)?;
        let Some(pointee) = self.pointer_pointee_for_value(&ptr_arg.value)? else {
            return Ok(ptr);
        };
        let pointee = self.resolve_type(&pointee)?;
        if pointee == LlType::Float {
            return Ok(ptr);
        }

        let LlType::Struct(fields) = pointee else {
            return Err(format!(
                "native emitter: atomic f32 pointer targets {pointee:?}"
            ));
        };
        let [field] = fields.as_slice() else {
            return Err(format!(
                "native emitter: atomic f32 pointer targets struct with {} fields",
                fields.len()
            ));
        };
        let field = self.resolve_type(field)?;
        if field != LlType::Float {
            return Err(format!(
                "native emitter: atomic f32 pointer targets struct field {field:?}"
            ));
        }
        let LlType::Ptr(addrspace) = self.resolve_type(&ptr_arg.ty)? else {
            return Err(format!(
                "native emitter: atomic f32 pointer argument has type {:?}",
                ptr_arg.ty
            ));
        };
        let ptr_type = self.ptr_type_id(llvm_pointer_storage(addrspace)?, &LlType::Float)?;
        let zero = self.const_uint(0)?;
        let result = self.fresh();
        instructions.push(Self::inst(
            Op::InBoundsAccessChain,
            Some(ptr_type),
            Some(result),
            vec![Operand::IdRef(ptr), Operand::IdRef(zero)],
        ));
        Ok(result)
    }

    pub(in crate::native::emitter) fn unmodeled_atomic_i32_pointer(
        &mut self,
    ) -> Result<Word, String> {
        let ptr_type = self.ptr_type_id(StorageClass::Workgroup, &LlType::Int(32))?;
        let result = self.fresh();
        self.module.types_global_values.push(Self::inst(
            Op::Variable,
            Some(ptr_type),
            Some(result),
            vec![Operand::StorageClass(StorageClass::Workgroup)],
        ));
        Ok(result)
    }

    pub(in crate::native::emitter) fn unmodeled_atomic_f32_pointer(
        &mut self,
    ) -> Result<Word, String> {
        let ptr_type = self.ptr_type_id(StorageClass::Workgroup, &LlType::Float)?;
        let result = self.fresh();
        self.module.types_global_values.push(Self::inst(
            Op::Variable,
            Some(ptr_type),
            Some(result),
            vec![Operand::StorageClass(StorageClass::Workgroup)],
        ));
        Ok(result)
    }
}