From: Jacob Lifshay Date: Thu, 11 Oct 2018 08:52:26 +0000 (-0700) Subject: working on implementing LLVM 7.0 shader compiler backend X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=3286f8ae118d89a0a16fec56bba56582d52a79d8;p=kazan.git working on implementing LLVM 7.0 shader compiler backend Currently crashes in LLVM when running tests for shader-compiler-llvm-7 --- diff --git a/.gitignore b/.gitignore index 3d6ba8a..8889c73 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /Cargo.lock /VK-GL-CTS /TestResults.qpa +/.vscode \ No newline at end of file diff --git a/shader-compiler-llvm-7/src/backend.rs b/shader-compiler-llvm-7/src/backend.rs index 74af1b5..cbb7b51 100644 --- a/shader-compiler-llvm-7/src/backend.rs +++ b/shader-compiler-llvm-7/src/backend.rs @@ -1,22 +1,38 @@ // SPDX-License-Identifier: LGPL-2.1-or-later // Copyright 2018 Jacob Lifshay use llvm_sys; -use shader_compiler::backend::*; +use shader_compiler::backend; +use std::cell::Cell; use std::ffi::{CStr, CString}; use std::fmt; use std::ops::Deref; use std::os::raw::{c_char, c_uint}; +use std::ptr::null_mut; use std::ptr::NonNull; +use std::sync::{Once, ONCE_INIT}; + +fn to_bool(v: llvm_sys::prelude::LLVMBool) -> bool { + v != 0 +} #[derive(Clone)] -pub struct LLVM7ShaderCompilerConfig { +pub struct LLVM7CompilerConfig { pub variable_vector_length_multiplier: u32, + pub optimization_mode: backend::OptimizationMode, } -impl Default for LLVM7ShaderCompilerConfig { +impl Default for LLVM7CompilerConfig { fn default() -> Self { + backend::CompilerIndependentConfig::default().into() + } +} + +impl From for LLVM7CompilerConfig { + fn from(v: backend::CompilerIndependentConfig) -> Self { + let backend::CompilerIndependentConfig { optimization_mode } = v; Self { variable_vector_length_multiplier: 1, + optimization_mode, } } } @@ -53,7 +69,13 @@ impl LLVM7String { LLVM7String(v) } unsafe fn from_ptr(v: *mut c_char) -> Option { - NonNull::new(v).map(LLVM7String) + NonNull::new(v).map(|v| Self::from_nonnull(v)) + } +} + +impl fmt::Debug for LLVM7String { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (**self).fmt(f) } } @@ -71,14 +93,16 @@ impl fmt::Debug for LLVM7Type { } } -impl<'a> Type<'a> for LLVM7Type {} +impl<'a> backend::types::Type<'a> for LLVM7Type { + type Context = LLVM7Context; +} pub struct LLVM7TypeBuilder { context: llvm_sys::prelude::LLVMContextRef, variable_vector_length_multiplier: u32, } -impl<'a> TypeBuilder<'a, LLVM7Type> for LLVM7TypeBuilder { +impl<'a> backend::types::TypeBuilder<'a, LLVM7Type> for LLVM7TypeBuilder { fn build_bool(&self) -> LLVM7Type { unsafe { LLVM7Type(llvm_sys::core::LLVMInt1TypeInContext(self.context)) } } @@ -107,10 +131,11 @@ impl<'a> TypeBuilder<'a, LLVM7Type> for LLVM7TypeBuilder { assert_eq!(count as u32 as usize, count); unsafe { LLVM7Type(llvm_sys::core::LLVMArrayType(element.0, count as u32)) } } - fn build_vector(&self, element: LLVM7Type, length: VectorLength) -> LLVM7Type { + fn build_vector(&self, element: LLVM7Type, length: backend::types::VectorLength) -> LLVM7Type { + use self::backend::types::VectorLength::*; let length = match length { - VectorLength::Fixed { length } => length, - VectorLength::Variable { base_length } => base_length + Fixed { length } => length, + Variable { base_length } => base_length .checked_mul(self.variable_vector_length_multiplier) .unwrap(), }; @@ -145,31 +170,128 @@ impl<'a> TypeBuilder<'a, LLVM7Type> for LLVM7TypeBuilder { } } +#[derive(Clone)] +#[repr(transparent)] +pub struct LLVM7Value(llvm_sys::prelude::LLVMValueRef); + +impl fmt::Debug for LLVM7Value { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { + let string = LLVM7String::from_ptr(llvm_sys::core::LLVMPrintValueToString(self.0)) + .ok_or(fmt::Error)?; + f.write_str(&string.to_string_lossy()) + } + } +} + +impl<'a> backend::Value<'a> for LLVM7Value { + type Context = LLVM7Context; +} + +#[derive(Clone)] +#[repr(transparent)] +pub struct LLVM7BasicBlock(llvm_sys::prelude::LLVMBasicBlockRef); + +impl fmt::Debug for LLVM7BasicBlock { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::backend::BasicBlock; + unsafe { + let string = + LLVM7String::from_ptr(llvm_sys::core::LLVMPrintValueToString(self.as_value().0)) + .ok_or(fmt::Error)?; + f.write_str(&string.to_string_lossy()) + } + } +} + +impl<'a> backend::BasicBlock<'a> for LLVM7BasicBlock { + type Context = LLVM7Context; + fn as_value(&self) -> LLVM7Value { + unsafe { LLVM7Value(llvm_sys::core::LLVMBasicBlockAsValue(self.0)) } + } +} + +impl<'a> backend::BuildableBasicBlock<'a> for LLVM7BasicBlock { + type Context = LLVM7Context; + fn as_basic_block(&self) -> LLVM7BasicBlock { + self.clone() + } +} + +pub struct LLVM7Function { + context: llvm_sys::prelude::LLVMContextRef, + function: llvm_sys::prelude::LLVMValueRef, +} + +impl fmt::Debug for LLVM7Function { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { + let string = + LLVM7String::from_ptr(llvm_sys::core::LLVMPrintValueToString(self.function)) + .ok_or(fmt::Error)?; + f.write_str(&string.to_string_lossy()) + } + } +} + +impl<'a> backend::Function<'a> for LLVM7Function { + type Context = LLVM7Context; + fn as_value(&self) -> LLVM7Value { + LLVM7Value(self.function) + } + fn append_new_basic_block(&mut self, name: Option<&str>) -> LLVM7BasicBlock { + let name = CString::new(name.unwrap_or("")).unwrap(); + unsafe { + LLVM7BasicBlock(llvm_sys::core::LLVMAppendBasicBlockInContext( + self.context, + self.function, + name.as_ptr(), + )) + } + } +} + pub struct LLVM7Context { context: llvm_sys::prelude::LLVMContextRef, - config: LLVM7ShaderCompilerConfig, + modules: Cell>, + config: LLVM7CompilerConfig, } impl Drop for LLVM7Context { fn drop(&mut self) { unsafe { + for module in self.modules.get_mut().drain(..) { + llvm_sys::core::LLVMDisposeModule(module); + } llvm_sys::core::LLVMContextDispose(self.context); } } } -impl<'a> Context<'a> for LLVM7Context { +impl<'a> backend::Context<'a> for LLVM7Context { + type Value = LLVM7Value; + type BasicBlock = LLVM7BasicBlock; + type BuildableBasicBlock = LLVM7BasicBlock; + type Function = LLVM7Function; type Type = LLVM7Type; type TypeBuilder = LLVM7TypeBuilder; type Module = LLVM7Module; - type Builder = LLVM7Builder; + type VerifiedModule = LLVM7Module; + type AttachedBuilder = LLVM7Builder; + type DetachedBuilder = LLVM7Builder; fn create_module(&self, name: &str) -> LLVM7Module { let name = CString::new(name).unwrap(); + let mut modules = self.modules.take(); + modules.reserve(1); // so we don't unwind without freeing the new module unsafe { - LLVM7Module(llvm_sys::core::LLVMModuleCreateWithNameInContext( - name.as_ptr(), - self.context, - )) + let module = + llvm_sys::core::LLVMModuleCreateWithNameInContext(name.as_ptr(), self.context); + modules.push(module); + self.modules.set(modules); + LLVM7Module { + context: self.context, + module, + } } } fn create_builder(&self) -> LLVM7Builder { @@ -194,48 +316,230 @@ impl Drop for LLVM7Builder { } } -impl<'a> Builder<'a> for LLVM7Builder {} +impl<'a> backend::AttachedBuilder<'a> for LLVM7Builder { + type Context = LLVM7Context; + fn current_basic_block(&self) -> LLVM7BasicBlock { + unsafe { LLVM7BasicBlock(llvm_sys::core::LLVMGetInsertBlock(self.0)) } + } + fn build_return(self, value: Option) -> LLVM7Builder { + unsafe { + match value { + Some(value) => llvm_sys::core::LLVMBuildRet(self.0, value.0), + None => llvm_sys::core::LLVMBuildRetVoid(self.0), + }; + llvm_sys::core::LLVMClearInsertionPosition(self.0); + } + self + } +} -#[repr(transparent)] -pub struct LLVM7Module(llvm_sys::prelude::LLVMModuleRef); +impl<'a> backend::DetachedBuilder<'a> for LLVM7Builder { + type Context = LLVM7Context; + fn attach(self, basic_block: LLVM7BasicBlock) -> LLVM7Builder { + unsafe { + llvm_sys::core::LLVMPositionBuilderAtEnd(self.0, basic_block.0); + } + self + } +} -impl Drop for LLVM7Module { - fn drop(&mut self) { +pub struct LLVM7Module { + context: llvm_sys::prelude::LLVMContextRef, + module: llvm_sys::prelude::LLVMModuleRef, +} + +impl fmt::Debug for LLVM7Module { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { unsafe { - llvm_sys::core::LLVMDisposeModule(self.0); + let string = + LLVM7String::from_ptr(llvm_sys::core::LLVMPrintModuleToString(self.module)) + .ok_or(fmt::Error)?; + f.write_str(&string.to_string_lossy()) } } } -impl<'a> Module<'a> for LLVM7Module { +impl<'a> backend::Module<'a> for LLVM7Module { + type Context = LLVM7Context; fn set_source_file_name(&mut self, source_file_name: &str) { unsafe { llvm_sys::core::LLVMSetSourceFileName( - self.0, + self.module, source_file_name.as_ptr() as *const c_char, source_file_name.len(), ) } } + fn add_function(&mut self, name: &str, ty: LLVM7Type) -> LLVM7Function { + let name = CString::new(name).unwrap(); + unsafe { + LLVM7Function { + context: self.context, + function: llvm_sys::core::LLVMAddFunction(self.module, name.as_ptr(), ty.0), + } + } + } + fn verify(self) -> Result> { + unsafe { + let mut message = null_mut(); + match to_bool(llvm_sys::analysis::LLVMVerifyModule( + self.module, + llvm_sys::analysis::LLVMVerifierFailureAction::LLVMReturnStatusAction, + &mut message, + )) { + broken if broken != false => { + let message = LLVM7String::from_ptr(message).unwrap(); + let message = message.to_string_lossy(); + Err(backend::VerificationFailure::new( + self, + message.into_owned(), + )) + } + _ => Ok(self), + } + } + } + unsafe fn to_verified_module_unchecked(self) -> LLVM7Module { + self + } } -pub struct LLVM7ShaderCompiler; +impl<'a> backend::VerifiedModule<'a> for LLVM7Module { + type Context = LLVM7Context; + fn into_module(self) -> LLVM7Module { + self + } +} + +struct LLVM7TargetMachine(llvm_sys::target_machine::LLVMTargetMachineRef); + +impl Drop for LLVM7TargetMachine { + fn drop(&mut self) { + unsafe { + llvm_sys::target_machine::LLVMDisposeTargetMachine(self.0); + } + } +} -impl ShaderCompiler for LLVM7ShaderCompiler { - type Config = LLVM7ShaderCompilerConfig; - fn name() -> &'static str { +impl LLVM7TargetMachine { + fn take(mut self) -> llvm_sys::target_machine::LLVMTargetMachineRef { + let retval = self.0; + self.0 = null_mut(); + retval + } +} + +struct LLVM7OrcJITStack(llvm_sys::orc::LLVMOrcJITStackRef); + +impl Drop for LLVM7OrcJITStack { + fn drop(&mut self) { + unsafe { + match llvm_sys::orc::LLVMOrcDisposeInstance(self.0) { + llvm_sys::orc::LLVMOrcErrorCode::LLVMOrcErrSuccess => {} + llvm_sys::orc::LLVMOrcErrorCode::LLVMOrcErrGeneric => { + panic!("LLVMOrcDisposeInstance failed"); + } + } + } + } +} + +fn initialize_native_target() { + static ONCE: Once = ONCE_INIT; + ONCE.call_once(|| unsafe { + assert_eq!(llvm_sys::target::LLVM_InitializeNativeTarget(), 0); + assert_eq!(llvm_sys::target::LLVM_InitializeNativeAsmParser(), 0); + }); +} + +extern "C" fn symbol_resolver_fn(name: *const c_char, _lookup_context: *mut Void) -> u64 { + let name = unsafe { CStr::from_ptr(name) }; + panic!("symbol_resolver_fn is unimplemented: name = {:?}", name) +} + +#[derive(Copy, Clone)] +pub struct LLVM7Compiler; + +impl backend::Compiler for LLVM7Compiler { + type Config = LLVM7CompilerConfig; + fn name(self) -> &'static str { "LLVM 7" } - fn run_with_user( - shader_compiler_user: SCU, - config: LLVM7ShaderCompilerConfig, - ) -> SCU::ReturnType { - let context = unsafe { - LLVM7Context { + fn run( + self, + user: U, + config: LLVM7CompilerConfig, + ) -> Result>, U::Error> { + unsafe { + initialize_native_target(); + let context = LLVM7Context { context: llvm_sys::core::LLVMContextCreate(), - config, + modules: Vec::new().into(), + config: config.clone(), + }; + let backend::CompileInputs { + module, + callable_functions, + } = user.run(&context)?; + for callable_function in callable_functions.values() { + assert_eq!( + llvm_sys::core::LLVMGetGlobalParent(callable_function.function), + module.module + ); } - }; - shader_compiler_user.run_with_context(&context) + let target_triple = + LLVM7String::from_ptr(llvm_sys::target_machine::LLVMGetDefaultTargetTriple()) + .unwrap(); + let mut target = null_mut(); + let mut error = null_mut(); + let success = !to_bool(llvm_sys::target_machine::LLVMGetTargetFromTriple( + target_triple.as_ptr(), + &mut target, + &mut error, + )); + if !success { + let error = LLVM7String::from_ptr(error).unwrap(); + return Err(U::create_error(error.to_string_lossy().into())); + } + if !to_bool(llvm_sys::target_machine::LLVMTargetHasJIT(target)) { + return Err(U::create_error(format!( + "target {:?} doesn't support JIT", + target_triple + ))); + } + let host_cpu_name = + LLVM7String::from_ptr(llvm_sys::target_machine::LLVMGetHostCPUName()).unwrap(); + let host_cpu_features = + LLVM7String::from_ptr(llvm_sys::target_machine::LLVMGetHostCPUFeatures()).unwrap(); + let target_machine = + LLVM7TargetMachine(llvm_sys::target_machine::LLVMCreateTargetMachine( + target, + target_triple.as_ptr(), + host_cpu_name.as_ptr(), + host_cpu_features.as_ptr(), + match config.optimization_mode { + backend::OptimizationMode::NoOptimizations => { + llvm_sys::target_machine::LLVMCodeGenOptLevel::LLVMCodeGenLevelNone + } + backend::OptimizationMode::Normal => { + llvm_sys::target_machine::LLVMCodeGenOptLevel::LLVMCodeGenLevelDefault + } + }, + llvm_sys::target_machine::LLVMRelocMode::LLVMRelocDefault, + llvm_sys::target_machine::LLVMCodeModel::LLVMCodeModelJITDefault, + )); + assert!(!target_machine.0.is_null()); + let orc_jit_stack = + LLVM7OrcJITStack(llvm_sys::orc::LLVMOrcCreateInstance(target_machine.take())); + let mut orc_module_handle = 0; + llvm_sys::orc::LLVMOrcAddEagerlyCompiledIR( + orc_jit_stack.0, + &mut orc_module_handle, + module.module, + Some(symbol_resolver_fn), + null_mut(), + ); + unimplemented!() + } } } diff --git a/shader-compiler-llvm-7/src/lib.rs b/shader-compiler-llvm-7/src/lib.rs index fad391e..c582f50 100644 --- a/shader-compiler-llvm-7/src/lib.rs +++ b/shader-compiler-llvm-7/src/lib.rs @@ -4,7 +4,8 @@ extern crate llvm_sys; extern crate shader_compiler; mod backend; +mod tests; -pub use backend::LLVM7ShaderCompilerConfig; +pub use backend::LLVM7CompilerConfig; -pub const LLVM_7_SHADER_COMPILER: backend::LLVM7ShaderCompiler = backend::LLVM7ShaderCompiler; +pub const LLVM_7_SHADER_COMPILER: backend::LLVM7Compiler = backend::LLVM7Compiler; diff --git a/shader-compiler-llvm-7/src/tests.rs b/shader-compiler-llvm-7/src/tests.rs new file mode 100644 index 0000000..9e3bfaa --- /dev/null +++ b/shader-compiler-llvm-7/src/tests.rs @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright 2018 Jacob Lifshay +#[cfg(test)] +mod tests { + use shader_compiler::backend::types::TypeBuilder; + use shader_compiler::backend::*; + use std::mem; + + fn make_compiler() -> impl Compiler { + ::LLVM_7_SHADER_COMPILER + } + + #[test] + fn test_basic() { + type GeneratedFunctionType = unsafe extern "C" fn(); + #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] + enum FunctionKey { + Function, + } + struct Test; + impl CompilerUser for Test { + type FunctionKey = FunctionKey; + type Error = String; + fn create_error(message: String) -> String { + message + } + fn run<'a, C: Context<'a>>( + self, + context: &'a C, + ) -> Result, String> { + let type_builder = context.create_type_builder(); + let mut module = context.create_module("test_module"); + let mut function = module.add_function( + "test_function", + type_builder.build::(), + ); + let builder = context.create_builder(); + let builder = builder.attach(function.append_new_basic_block(None)); + builder.build_return(None); + let module = module.verify().unwrap(); + Ok(CompileInputs { + module, + callable_functions: vec![(FunctionKey::Function, function)] + .into_iter() + .collect(), + }) + } + } + let compiled_code = make_compiler().run(Test, Default::default()).unwrap(); + let function = compiled_code.get(&FunctionKey::Function).unwrap(); + unsafe { + let function: GeneratedFunctionType = mem::transmute(function); + function(); + } + } +} diff --git a/shader-compiler/src/backend/mod.rs b/shader-compiler/src/backend/mod.rs index 71b18e4..1ff6b9e 100644 --- a/shader-compiler/src/backend/mod.rs +++ b/shader-compiler/src/backend/mod.rs @@ -3,534 +3,250 @@ //! Shader Compiler Backend traits -use std::cell::UnsafeCell; +use std::borrow::Borrow; +use std::collections::HashMap; +use std::error::Error; +use std::fmt; use std::fmt::Debug; use std::hash::Hash; +use std::io; use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; +use std::os::raw::c_void; +use std::ptr::NonNull; -#[macro_export] -macro_rules! buildable_struct_helper { - { - struct $name:ident { - $($member_name:ident: $member_type:ty,)* - } - } => { - impl $crate::backend::BuildableType for $name { - fn build<'a, Ty: $crate::backend::Type<'a>, TB: $crate::backend::TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_struct(&[$(<$member_type as $crate::backend::BuildableType>::build(type_builder),)*]) - } - } - - impl $crate::backend::BuildableStruct for $name { - fn get_members( - ) -> &'static [$crate::backend::BuildableStructMemberDescriptor] { - #[allow(dead_code, non_camel_case_types)] - #[repr(usize)] - enum MemberIndices { - $($member_name,)* - __Last, - } - const MEMBERS: &'static [$crate::backend::BuildableStructMemberDescriptor] = &[ - $($crate::backend::BuildableStructMemberDescriptor { - name: stringify!($member_name), - index: MemberIndices::$member_name as usize, - },)* - ]; - MEMBERS - } - } - } -} - -#[macro_export] -macro_rules! buildable_struct { - { - $(#[derive($derives:ident)])* - pub struct $name:ident { - $($member_name:ident: $member_type:ty,)* - } - } => { - $(#[derive($derives)])* - #[repr(C)] - pub struct $name { - $($member_name: $member_type,)* - } - - buildable_struct_helper!{ - struct $name { - $($member_name: $member_type,)* - } - } - }; - { - $(#[derive($derives:ident)])* - struct $name:ident { - $($member_name:ident: $member_type:ty,)* - } - } => { - $(#[derive($derives)])* - #[repr(C)] - struct $name { - $($member_name: $member_type,)* - } - - buildable_struct_helper!{ - struct $name { - $($member_name: $member_type,)* - } - } - }; -} +#[macro_use] +pub mod types; -/// length of a vector -pub enum VectorLength { - /// fixed length vector - Fixed { - /// length in elements - length: u32, - }, - /// variable length vector - Variable { - /// base length in elements which the runtime vector length is a multiple of - base_length: u32, - }, +/// equivalent to LLVM's 'IRBuilder' +pub trait AttachedBuilder<'a>: Sized { + /// the `Context` type + type Context: Context<'a>; + /// get the current `BasicBlock` + fn current_basic_block(&self) -> >::BasicBlock; + /// build a return instruction + fn build_return( + self, + value: Option<>::Value>, + ) -> >::DetachedBuilder; } -/// equivalent to LLVM's 'Type' -pub trait Type<'a>: Clone + Eq + Hash + Debug {} - -/// trait for building types -pub trait TypeBuilder<'a, Ty: Type<'a>> { - /// build a `bool` type - fn build_bool(&self) -> Ty; - /// build an 8-bit sign-agnostic integer type - fn build_i8(&self) -> Ty; - /// build an 16-bit sign-agnostic integer type - fn build_i16(&self) -> Ty; - /// build an 32-bit sign-agnostic integer type - fn build_i32(&self) -> Ty; - /// build an 64-bit sign-agnostic integer type - fn build_i64(&self) -> Ty; - /// build an 32-bit IEEE 754 floating-point type - fn build_f32(&self) -> Ty; - /// build an 64-bit IEEE 754 floating-point type - fn build_f64(&self) -> Ty; - /// build a pointer - fn build_pointer(&self, target: Ty) -> Ty; - /// build an array - fn build_array(&self, element: Ty, count: usize) -> Ty; - /// build a vector - fn build_vector(&self, element: Ty, length: VectorLength) -> Ty; - /// build a struct - fn build_struct(&self, members: &[Ty]) -> Ty; - /// build a function type - fn build_function(&self, arguments: &[Ty], return_type: Option) -> Ty; - /// build a type - fn build(&self) -> Ty - where - Self: Sized, - { - T::build(self) - } +/// equivalent to LLVM's 'IRBuilder' +pub trait DetachedBuilder<'a>: Sized { + /// the `Context` type + type Context: Context<'a>; + /// attach `basic_block` to `Self`, converting into an `AttachedBuilder` + fn attach( + self, + basic_block: >::BuildableBasicBlock, + ) -> >::AttachedBuilder; } -impl<'a, 'b, Ty: Type<'a>> TypeBuilder<'a, Ty> for &'b TypeBuilder<'a, Ty> { - fn build_bool(&self) -> Ty { - (*self).build_bool() - } - fn build_i8(&self) -> Ty { - (*self).build_i8() - } - fn build_i16(&self) -> Ty { - (*self).build_i16() - } - fn build_i32(&self) -> Ty { - (*self).build_i32() - } - fn build_i64(&self) -> Ty { - (*self).build_i64() - } - fn build_f32(&self) -> Ty { - (*self).build_f32() - } - fn build_f64(&self) -> Ty { - (*self).build_f64() - } - fn build_pointer(&self, target: Ty) -> Ty { - (*self).build_pointer(target) - } - fn build_array(&self, element: Ty, count: usize) -> Ty { - (*self).build_array(element, count) - } - fn build_vector(&self, element: Ty, length: VectorLength) -> Ty { - (*self).build_vector(element, length) - } - fn build_struct(&self, members: &[Ty]) -> Ty { - (*self).build_struct(members) - } - fn build_function(&self, arguments: &[Ty], return_type: Option) -> Ty { - (*self).build_function(arguments, return_type) - } +/// equivalent to LLVM's 'Value' +pub trait Value<'a>: Clone + Debug { + /// the `Context` type + type Context: Context<'a>; } -/// trait for rust types that can be built using `TypeBuilder` -pub trait BuildableType { - /// build the type represented by `Self` - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty; +/// equivalent to LLVM's 'BasicBlock' +pub trait BasicBlock<'a>: Clone + Debug { + /// the `Context` type + type Context: Context<'a>; + /// get the `Value` corresponding to `Self` + fn as_value(&self) -> >::Value; } -impl BuildableType for UnsafeCell { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - T::build(type_builder) +/// equivalent to LLVM's 'BasicBlock' +pub trait BuildableBasicBlock<'a>: Debug + Sized { + /// the `Context` type + type Context: Context<'a>; + /// get the `BasicBlock` corresponding to `Self` + fn as_basic_block(&self) -> >::BasicBlock; + /// get the `Value` corresponding to `Self` + fn as_value(&self) -> >::Value { + self.as_basic_block().as_value() } } -mod hidden { - pub trait ScalarBuildableTypeBase {} -} - -impl hidden::ScalarBuildableTypeBase for UnsafeCell {} - -/// trait for rust types that can be an element of a vector and be built using `TypeBuilder` -pub trait ScalarBuildableType: BuildableType + hidden::ScalarBuildableTypeBase {} - -impl ScalarBuildableType for UnsafeCell {} - -/// descriptor for members of types implementing `BuildableStruct` -pub struct BuildableStructMemberDescriptor { - /// name of member - pub name: &'static str, - /// index of member - pub index: usize, +/// equivalent to LLVM's 'Function' +pub trait Function<'a>: Debug + Sized { + /// the `Context` type + type Context: Context<'a>; + /// get the `Value` corresponding to `Self` + fn as_value(&self) -> >::Value; + /// append a new `BasicBlock` to `Self` + fn append_new_basic_block( + &mut self, + name: Option<&str>, + ) -> >::BuildableBasicBlock; } -/// trait for structs that can be built using TypeBuilder -/// implementing types are usually created using `buildable_struct!` -pub trait BuildableStruct: BuildableType { - /// get the list of members for `Self` - fn get_members() -> &'static [BuildableStructMemberDescriptor]; - /// get the member for `Self` that is named `name` - fn get_member_by_name(name: &str) -> &'static BuildableStructMemberDescriptor { - for member in Self::get_members() { - if name == member.name { - return member; - } - } - unreachable!("{} is not a member", name); - } +/// module verification failure; returned from `Module::verify` +pub struct VerificationFailure<'a, M: Module<'a>> { + module: M, + message: String, + _phantom_data: PhantomData<&'a ()>, } -macro_rules! build_basic_scalar { - ($type:ty, $build_fn:ident) => { - impl BuildableType for $type { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.$build_fn() - } +impl<'a, M: Module<'a>> VerificationFailure<'a, M> { + /// create a new `VerificationFailure` + pub fn new(module: M, message: T) -> Self { + VerificationFailure { + module, + message: message.to_string(), + _phantom_data: PhantomData, } - - impl hidden::ScalarBuildableTypeBase for $type {} - - impl ScalarBuildableType for $type {} - }; -} - -build_basic_scalar!(bool, build_bool); -build_basic_scalar!(u8, build_i8); -build_basic_scalar!(i8, build_i8); -build_basic_scalar!(u16, build_i16); -build_basic_scalar!(i16, build_i16); -build_basic_scalar!(u32, build_i32); -build_basic_scalar!(i32, build_i32); -build_basic_scalar!(u64, build_i64); -build_basic_scalar!(i64, build_i64); -build_basic_scalar!(f32, build_f32); -build_basic_scalar!(f64, build_f64); - -impl<'b, T: BuildableType> BuildableType for &'b T { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_pointer(T::build(type_builder)) } -} - -impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for &'b T {} - -impl<'b, T: BuildableType> ScalarBuildableType for &'b T {} - -impl<'b, T: BuildableType> BuildableType for &'b mut T { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_pointer(T::build(type_builder)) + /// get the `Module` that failed verification + pub fn into_module(self) -> M { + self.module } } -impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for &'b mut T {} - -impl<'b, T: BuildableType> ScalarBuildableType for &'b mut T {} - -impl BuildableType for *mut T { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_pointer(T::build(type_builder)) +impl<'a, M: Module<'a>> fmt::Display for VerificationFailure<'a, M> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "module verification failure: {}", self.message,) } } -impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for *mut T {} - -impl<'b, T: BuildableType> ScalarBuildableType for *mut T {} - -impl BuildableType for *const T { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_pointer(T::build(type_builder)) +impl<'a, M: Module<'a>> Debug for VerificationFailure<'a, M> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("VerificationFailure") + .field("message", &self.message) + .field("module", &self.module) + .finish() } } -impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for *const T {} - -impl<'b, T: BuildableType> ScalarBuildableType for *const T {} - -macro_rules! build_unit_function_type { - ($($arguments:ident,)*) => { - impl<$($arguments: BuildableType),*> BuildableType for Option { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_function(&[$($arguments::build(type_builder),)*], None) - } - } - - impl<$($arguments: BuildableType),*> hidden::ScalarBuildableTypeBase for Option {} - - impl<$($arguments: BuildableType),*> ScalarBuildableType for Option {} - }; - } - -macro_rules! build_function_type { - ($($arguments:ident,)*) => { - impl BuildableType for Option R> { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_function(&[$($arguments::build(type_builder),)*], Some(R::build(type_builder))) - } - } +impl<'a, M: Module<'a>> Error for VerificationFailure<'a, M> {} - impl hidden::ScalarBuildableTypeBase for Option R> {} - - impl ScalarBuildableType for Option R> {} - }; +impl<'a, M: Module<'a>> From> for io::Error { + fn from(v: VerificationFailure<'a, M>) -> Self { + io::Error::new(io::ErrorKind::Other, format!("{}", v)) } - -macro_rules! build_function_types { - () => { - build_unit_function_type!(); - build_function_type!(); - }; - ($first_argument:ident, $($arguments:ident,)*) => { - build_unit_function_type!($first_argument, $($arguments,)*); - build_function_type!($first_argument, $($arguments,)*); - build_function_types!($($arguments,)*); - } - } - -build_function_types!( - T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, -); - -macro_rules! build_array0 { - ($length:expr) => { - impl BuildableType for [T; $length + 1] { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_array(T::build(type_builder), $length + 1) - } - } - }; -} - -macro_rules! build_array1 { - ($length:expr) => { - build_array0!($length * 2); - build_array0!($length * 2 + 1); - }; -} - -macro_rules! build_array2 { - ($length:expr) => { - build_array1!($length * 2); - build_array1!($length * 2 + 1); - }; -} - -macro_rules! build_array3 { - ($length:expr) => { - build_array2!($length * 2); - build_array2!($length * 2 + 1); - }; -} - -macro_rules! build_array4 { - ($length:expr) => { - build_array3!($length * 2); - build_array3!($length * 2 + 1); - }; -} - -macro_rules! build_array5 { - ($length:expr) => { - build_array4!($length * 2); - build_array4!($length * 2 + 1); - }; -} - -build_array5!(0); -build_array5!(1); - -/// buildable vector types -pub trait Vector: BuildableType { - /// element type - type Element: ScalarBuildableType; - /// vector length - const LENGTH: VectorLength; -} - -#[doc(hidden)] -pub enum __VectorNeverType {} - -macro_rules! build_fixed_vector { - ($name:ident, $length:expr) => { - /// Vector of elements `Element` - #[derive(Copy, Clone)] - pub struct $name { - /// elements of the vector `Self` - pub elements: [Element; $length], - } - - impl Deref for $name { - type Target = [Element; $length]; - fn deref(&self) -> &Self::Target { - &self.elements - } - } - - impl DerefMut for $name { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.elements - } - } - - impl BuildableType for $name { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_vector(Element::build(type_builder), Self::LENGTH) - } - } - - impl Vector for $name { - type Element = Element; - const LENGTH: VectorLength = { VectorLength::Fixed { length: $length } }; - } - }; -} - -macro_rules! build_variable_vector { - ($name:ident, $base_length:expr) => { - /// Vector of elements `Element` - pub enum $name { - #[doc(hidden)] - __Dummy(__VectorNeverType, PhantomData), - } - - impl BuildableType for $name { - fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { - type_builder.build_vector(Element::build(type_builder), Self::LENGTH) - } - } - - impl Vector for $name { - type Element = Element; - const LENGTH: VectorLength = { - VectorLength::Variable { - base_length: $base_length, - } - }; - } - }; } -/// alternate name for `VecNx1` -pub type VecN = VecNx1; - -build_fixed_vector!(Vec1, 1); -build_fixed_vector!(Vec2, 2); -build_fixed_vector!(Vec3, 3); -build_fixed_vector!(Vec4, 4); -build_fixed_vector!(Vec5, 5); -build_fixed_vector!(Vec6, 6); -build_fixed_vector!(Vec7, 7); -build_fixed_vector!(Vec8, 8); -build_fixed_vector!(Vec9, 9); -build_fixed_vector!(Vec10, 10); -build_fixed_vector!(Vec11, 11); -build_fixed_vector!(Vec12, 12); -build_fixed_vector!(Vec13, 13); -build_fixed_vector!(Vec14, 14); -build_fixed_vector!(Vec15, 15); -build_fixed_vector!(Vec16, 16); -build_variable_vector!(VecNx1, 1); -build_variable_vector!(VecNx2, 2); -build_variable_vector!(VecNx3, 3); -build_variable_vector!(VecNx4, 4); -build_variable_vector!(VecNx5, 5); -build_variable_vector!(VecNx6, 6); -build_variable_vector!(VecNx7, 7); -build_variable_vector!(VecNx8, 8); -build_variable_vector!(VecNx9, 9); -build_variable_vector!(VecNx10, 10); -build_variable_vector!(VecNx11, 11); -build_variable_vector!(VecNx12, 12); -build_variable_vector!(VecNx13, 13); -build_variable_vector!(VecNx14, 14); -build_variable_vector!(VecNx15, 15); -build_variable_vector!(VecNx16, 16); - -/// equivalent to LLVM's 'IRBuilder' -pub trait Builder<'a> {} - /// equivalent to LLVM's 'Module' -pub trait Module<'a> { +pub trait Module<'a>: Debug + Sized { + /// the `Context` type + type Context: Context<'a>; /// set's the source file name for this module fn set_source_file_name(&mut self, source_file_name: &str); + /// add a new empty function to `Self` + fn add_function( + &mut self, + name: &str, + ty: >::Type, + ) -> >::Function; + /// verify `Self`, converting into a `VerifiedModule` + fn verify( + self, + ) -> Result<>::VerifiedModule, VerificationFailure<'a, Self>>; + /// convert into a `VerifiedModule` without verifing + unsafe fn to_verified_module_unchecked(self) -> >::VerifiedModule; +} + +/// equivalent to LLVM's 'Module'; create using `Module::verify` or `Module::to_verified_module_unchecked` +pub trait VerifiedModule<'a>: Debug + Sized { + /// the `Context` type + type Context: Context<'a>; + /// convert back to an unverified module + fn into_module(self) -> >::Module; } /// instance of a compiler backend; equivalent to LLVM's `LLVMContext` -pub trait Context<'a> { +pub trait Context<'a>: Sized { + /// the `Value` type + type Value: Value<'a, Context = Self>; + /// the `BasicBlock` type + type BasicBlock: BasicBlock<'a, Context = Self>; + /// the `BuildableBasicBlock` type + type BuildableBasicBlock: BuildableBasicBlock<'a, Context = Self>; + /// the `Function` type + type Function: Function<'a, Context = Self>; /// the `Module` type - type Module: Module<'a>; - /// the `Builder` type - type Builder: Builder<'a>; + type Module: Module<'a, Context = Self>; + /// the `VerifiedModule` type + type VerifiedModule: VerifiedModule<'a, Context = Self>; + /// the `AttachedBuilder` type + type AttachedBuilder: AttachedBuilder<'a, Context = Self>; + /// the `DetachedBuilder` type + type DetachedBuilder: DetachedBuilder<'a, Context = Self>; /// the `Type` type - type Type: Type<'a>; + type Type: types::Type<'a, Context = Self>; /// the `TypeBuilder` type - type TypeBuilder: TypeBuilder<'a, Self::Type>; + type TypeBuilder: types::TypeBuilder<'a, Self::Type>; /// create a new `Module` fn create_module(&self, name: &str) -> Self::Module; - /// create a new `Builder` - fn create_builder(&self) -> Self::Builder; + /// create a new `DetachedBuilder` + fn create_builder(&self) -> Self::DetachedBuilder; /// create a new `TypeBuilder` fn create_type_builder(&self) -> Self::TypeBuilder; } -/// trait that the user of `ShaderCompiler` implements -pub trait ShaderCompilerUser { - /// the return type of `run_with_context` - type ReturnType; - /// the function that the user of `ShaderCompiler` implements - fn run_with_context<'a, C: Context<'a>>(self, context: &'a C) -> Self::ReturnType; +/// inputs to the final compilation +pub struct CompileInputs<'a, C: Context<'a>, K: Hash + Eq + Send + Sync> { + /// the input module + pub module: C::VerifiedModule, + /// the list of functions that can be called from the final `CompiledCode` + pub callable_functions: HashMap, +} + +/// the final compiled code +pub trait CompiledCode: Send + Sync { + /// get a function in the final compiled code. + /// the returned function needs to be cast to the correct type and + /// `Self` needs to still exist while the returned function exists + fn get(&self, which: &K) -> Option; +} + +/// trait that the user of `Compiler` implements +pub trait CompilerUser { + /// the type used as a key for visible functions + type FunctionKey: Hash + Eq + Send + Sync; + /// the user's error type + type Error; + /// create an instance of `Error` + fn create_error(message: String) -> Self::Error; + /// the function that the user of `Compiler` implements + fn run<'a, C: Context<'a>>( + self, + context: &'a C, + ) -> Result, Self::Error>; +} + +/// optimization mode +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub enum OptimizationMode { + /// no optimizations are enabled + NoOptimizations, + /// default optimizations are enabled + Normal, +} + +impl Default for OptimizationMode { + fn default() -> Self { + OptimizationMode::Normal + } +} + +/// compiler independent config options +#[derive(Clone, Debug, Default)] +pub struct CompilerIndependentConfig { + /// optimization mode + pub optimization_mode: OptimizationMode, } -/// main shader compiler backend trait -pub trait ShaderCompiler: Send + Sync + 'static { - /// the shader compiler's configuration - type Config: Default + Clone; +/// main compiler backend trait +pub trait Compiler: Copy + Send + Sync + 'static { + /// the compiler's configuration + type Config: Default + Clone + From + Send + Sync; /// get shader compiler's name - fn name() -> &'static str; + fn name(self) -> &'static str; /// run a passed-in function with a new compiler context. /// this round-about method is used because generic associated types are not in stable Rust yet - fn run_with_user( - shader_compiler_user: SCU, + fn run( + self, + user: U, config: Self::Config, - ) -> SCU::ReturnType; + ) -> Result>, U::Error>; } diff --git a/shader-compiler/src/backend/types.rs b/shader-compiler/src/backend/types.rs new file mode 100644 index 0000000..c364796 --- /dev/null +++ b/shader-compiler/src/backend/types.rs @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright 2018 Jacob Lifshay + +//! types in backend IR + +use backend::Context; +use std::cell::UnsafeCell; +use std::fmt::Debug; +use std::hash::Hash; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use std::ptr::NonNull; + +#[macro_export] +macro_rules! buildable_struct_helper { + { + struct $name:ident { + $($member_name:ident: $member_type:ty,)* + } + } => { + impl $crate::backend::types::BuildableType for $name { + fn build<'a, Ty: $crate::backend::types::Type<'a>, TB: $crate::backend::types::TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_struct(&[$(<$member_type as $crate::backend::types::BuildableType>::build(type_builder),)*]) + } + } + + impl $crate::backend::types::BuildableStruct for $name { + fn get_members( + ) -> &'static [$crate::backend::types::BuildableStructMemberDescriptor] { + #[allow(dead_code, non_camel_case_types)] + #[repr(usize)] + enum MemberIndices { + $($member_name,)* + __Last, + } + const MEMBERS: &'static [$crate::backend::types::BuildableStructMemberDescriptor] = &[ + $($crate::backend::types::BuildableStructMemberDescriptor { + name: stringify!($member_name), + index: MemberIndices::$member_name as usize, + },)* + ]; + MEMBERS + } + } + } +} + +#[macro_export] +macro_rules! buildable_struct { + { + $(#[derive($derives:ident)])* + pub struct $name:ident { + $($member_name:ident: $member_type:ty,)* + } + } => { + $(#[derive($derives)])* + #[repr(C)] + pub struct $name { + $($member_name: $member_type,)* + } + + buildable_struct_helper!{ + struct $name { + $($member_name: $member_type,)* + } + } + }; + { + $(#[derive($derives:ident)])* + struct $name:ident { + $($member_name:ident: $member_type:ty,)* + } + } => { + $(#[derive($derives)])* + #[repr(C)] + struct $name { + $($member_name: $member_type,)* + } + + buildable_struct_helper!{ + struct $name { + $($member_name: $member_type,)* + } + } + }; +} + +/// length of a vector +pub enum VectorLength { + /// fixed length vector + Fixed { + /// length in elements + length: u32, + }, + /// variable length vector + Variable { + /// base length in elements which the runtime vector length is a multiple of + base_length: u32, + }, +} + +/// equivalent to LLVM's 'Type' +pub trait Type<'a>: Clone + Eq + Hash + Debug { + /// the `Context` type + type Context: Context<'a>; +} + +/// trait for building types +pub trait TypeBuilder<'a, Ty: Type<'a>> { + /// build a `bool` type + fn build_bool(&self) -> Ty; + /// build an 8-bit sign-agnostic integer type + fn build_i8(&self) -> Ty; + /// build an 16-bit sign-agnostic integer type + fn build_i16(&self) -> Ty; + /// build an 32-bit sign-agnostic integer type + fn build_i32(&self) -> Ty; + /// build an 64-bit sign-agnostic integer type + fn build_i64(&self) -> Ty; + /// build an 32-bit IEEE 754 floating-point type + fn build_f32(&self) -> Ty; + /// build an 64-bit IEEE 754 floating-point type + fn build_f64(&self) -> Ty; + /// build a pointer + fn build_pointer(&self, target: Ty) -> Ty; + /// build an array + fn build_array(&self, element: Ty, count: usize) -> Ty; + /// build a vector + fn build_vector(&self, element: Ty, length: VectorLength) -> Ty; + /// build a struct + fn build_struct(&self, members: &[Ty]) -> Ty; + /// build a function type + fn build_function(&self, arguments: &[Ty], return_type: Option) -> Ty; + /// build a type + fn build(&self) -> Ty + where + Self: Sized, + { + T::build(self) + } +} + +impl<'a, 'b, Ty: Type<'a>> TypeBuilder<'a, Ty> for &'b TypeBuilder<'a, Ty> { + fn build_bool(&self) -> Ty { + (*self).build_bool() + } + fn build_i8(&self) -> Ty { + (*self).build_i8() + } + fn build_i16(&self) -> Ty { + (*self).build_i16() + } + fn build_i32(&self) -> Ty { + (*self).build_i32() + } + fn build_i64(&self) -> Ty { + (*self).build_i64() + } + fn build_f32(&self) -> Ty { + (*self).build_f32() + } + fn build_f64(&self) -> Ty { + (*self).build_f64() + } + fn build_pointer(&self, target: Ty) -> Ty { + (*self).build_pointer(target) + } + fn build_array(&self, element: Ty, count: usize) -> Ty { + (*self).build_array(element, count) + } + fn build_vector(&self, element: Ty, length: VectorLength) -> Ty { + (*self).build_vector(element, length) + } + fn build_struct(&self, members: &[Ty]) -> Ty { + (*self).build_struct(members) + } + fn build_function(&self, arguments: &[Ty], return_type: Option) -> Ty { + (*self).build_function(arguments, return_type) + } +} + +/// trait for rust types that can be built using `TypeBuilder` +pub trait BuildableType { + /// build the type represented by `Self` + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty; +} + +impl BuildableType for UnsafeCell { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + T::build(type_builder) + } +} + +mod hidden { + pub trait ScalarBuildableTypeBase {} +} + +impl hidden::ScalarBuildableTypeBase for UnsafeCell {} + +/// trait for rust types that can be an element of a vector and be built using `TypeBuilder` +pub trait ScalarBuildableType: BuildableType + hidden::ScalarBuildableTypeBase {} + +impl ScalarBuildableType for UnsafeCell {} + +/// descriptor for members of types implementing `BuildableStruct` +pub struct BuildableStructMemberDescriptor { + /// name of member + pub name: &'static str, + /// index of member + pub index: usize, +} + +/// trait for structs that can be built using TypeBuilder +/// implementing types are usually created using `buildable_struct!` +pub trait BuildableStruct: BuildableType { + /// get the list of members for `Self` + fn get_members() -> &'static [BuildableStructMemberDescriptor]; + /// get the member for `Self` that is named `name` + fn get_member_by_name(name: &str) -> &'static BuildableStructMemberDescriptor { + for member in Self::get_members() { + if name == member.name { + return member; + } + } + unreachable!("{} is not a member", name); + } +} + +macro_rules! build_basic_scalar { + ($type:ty, $build_fn:ident) => { + impl BuildableType for $type { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.$build_fn() + } + } + + impl hidden::ScalarBuildableTypeBase for $type {} + + impl ScalarBuildableType for $type {} + }; +} + +build_basic_scalar!(bool, build_bool); +build_basic_scalar!(u8, build_i8); +build_basic_scalar!(i8, build_i8); +build_basic_scalar!(u16, build_i16); +build_basic_scalar!(i16, build_i16); +build_basic_scalar!(u32, build_i32); +build_basic_scalar!(i32, build_i32); +build_basic_scalar!(u64, build_i64); +build_basic_scalar!(i64, build_i64); +build_basic_scalar!(f32, build_f32); +build_basic_scalar!(f64, build_f64); + +impl<'b, T: BuildableType> BuildableType for Option<&'b T> { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_pointer(T::build(type_builder)) + } +} + +impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for Option<&'b T> {} + +impl<'b, T: BuildableType> ScalarBuildableType for Option<&'b T> {} + +impl<'b, T: BuildableType> BuildableType for Option<&'b mut T> { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_pointer(T::build(type_builder)) + } +} + +impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for Option<&'b mut T> {} + +impl<'b, T: BuildableType> ScalarBuildableType for Option<&'b mut T> {} + +impl<'b, T: BuildableType> BuildableType for &'b T { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_pointer(T::build(type_builder)) + } +} + +impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for &'b T {} + +impl<'b, T: BuildableType> ScalarBuildableType for &'b T {} + +impl<'b, T: BuildableType> BuildableType for &'b mut T { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_pointer(T::build(type_builder)) + } +} + +impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for &'b mut T {} + +impl<'b, T: BuildableType> ScalarBuildableType for &'b mut T {} + +impl BuildableType for *mut T { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_pointer(T::build(type_builder)) + } +} + +impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for *mut T {} + +impl<'b, T: BuildableType> ScalarBuildableType for *mut T {} + +impl BuildableType for *const T { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_pointer(T::build(type_builder)) + } +} + +impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for *const T {} + +impl<'b, T: BuildableType> ScalarBuildableType for *const T {} + +impl BuildableType for NonNull { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_pointer(T::build(type_builder)) + } +} + +impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for NonNull {} + +impl<'b, T: BuildableType> ScalarBuildableType for NonNull {} + +impl BuildableType for Option> { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_pointer(T::build(type_builder)) + } +} + +impl<'b, T: BuildableType> hidden::ScalarBuildableTypeBase for Option> {} + +impl<'b, T: BuildableType> ScalarBuildableType for Option> {} + +macro_rules! build_unit_function_type { + ($($arguments:ident,)*) => { + impl<$($arguments: BuildableType),*> BuildableType for Option { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_function(&[$($arguments::build(type_builder),)*], None) + } + } + + impl<$($arguments: BuildableType),*> hidden::ScalarBuildableTypeBase for Option {} + + impl<$($arguments: BuildableType),*> ScalarBuildableType for Option {} + + impl<$($arguments: BuildableType),*> BuildableType for unsafe extern "C" fn($($arguments,)*) { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_function(&[$($arguments::build(type_builder),)*], None) + } + } + + impl<$($arguments: BuildableType),*> hidden::ScalarBuildableTypeBase for unsafe extern "C" fn($($arguments,)*) {} + + impl<$($arguments: BuildableType),*> ScalarBuildableType for unsafe extern "C" fn($($arguments,)*) {} + }; + } + +macro_rules! build_function_type { + ($($arguments:ident,)*) => { + impl BuildableType for Option R> { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_function(&[$($arguments::build(type_builder),)*], Some(R::build(type_builder))) + } + } + + impl hidden::ScalarBuildableTypeBase for Option R> {} + + impl ScalarBuildableType for Option R> {} + + impl BuildableType for unsafe extern "C" fn($($arguments,)*) -> R { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_function(&[$($arguments::build(type_builder),)*], Some(R::build(type_builder))) + } + } + + impl hidden::ScalarBuildableTypeBase for unsafe extern "C" fn($($arguments,)*) -> R {} + + impl ScalarBuildableType for unsafe extern "C" fn($($arguments,)*) -> R {} + + }; + } + +macro_rules! build_function_types { + () => { + build_unit_function_type!(); + build_function_type!(); + }; + ($first_argument:ident, $($arguments:ident,)*) => { + build_unit_function_type!($first_argument, $($arguments,)*); + build_function_type!($first_argument, $($arguments,)*); + build_function_types!($($arguments,)*); + } + } + +build_function_types!( + T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, +); + +macro_rules! build_array0 { + ($length:expr) => { + impl BuildableType for [T; $length + 1] { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_array(T::build(type_builder), $length + 1) + } + } + }; +} + +macro_rules! build_array1 { + ($length:expr) => { + build_array0!($length * 2); + build_array0!($length * 2 + 1); + }; +} + +macro_rules! build_array2 { + ($length:expr) => { + build_array1!($length * 2); + build_array1!($length * 2 + 1); + }; +} + +macro_rules! build_array3 { + ($length:expr) => { + build_array2!($length * 2); + build_array2!($length * 2 + 1); + }; +} + +macro_rules! build_array4 { + ($length:expr) => { + build_array3!($length * 2); + build_array3!($length * 2 + 1); + }; +} + +macro_rules! build_array5 { + ($length:expr) => { + build_array4!($length * 2); + build_array4!($length * 2 + 1); + }; +} + +build_array5!(0); +build_array5!(1); + +/// buildable vector types +pub trait Vector: BuildableType { + /// element type + type Element: ScalarBuildableType; + /// vector length + const LENGTH: VectorLength; +} + +#[doc(hidden)] +pub enum __VectorNeverType {} + +macro_rules! build_fixed_vector { + ($name:ident, $length:expr) => { + /// Vector of elements `Element` + #[derive(Copy, Clone)] + pub struct $name { + /// elements of the vector `Self` + pub elements: [Element; $length], + } + + impl Deref for $name { + type Target = [Element; $length]; + fn deref(&self) -> &Self::Target { + &self.elements + } + } + + impl DerefMut for $name { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.elements + } + } + + impl BuildableType for $name { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_vector(Element::build(type_builder), Self::LENGTH) + } + } + + impl Vector for $name { + type Element = Element; + const LENGTH: VectorLength = { VectorLength::Fixed { length: $length } }; + } + }; +} + +macro_rules! build_variable_vector { + ($name:ident, $base_length:expr) => { + /// Vector of elements `Element` + pub enum $name { + #[doc(hidden)] + __Dummy(__VectorNeverType, PhantomData), + } + + impl BuildableType for $name { + fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty { + type_builder.build_vector(Element::build(type_builder), Self::LENGTH) + } + } + + impl Vector for $name { + type Element = Element; + const LENGTH: VectorLength = { + VectorLength::Variable { + base_length: $base_length, + } + }; + } + }; +} + +/// alternate name for `VecNx1` +pub type VecN = VecNx1; + +build_fixed_vector!(Vec1, 1); +build_fixed_vector!(Vec2, 2); +build_fixed_vector!(Vec3, 3); +build_fixed_vector!(Vec4, 4); +build_fixed_vector!(Vec5, 5); +build_fixed_vector!(Vec6, 6); +build_fixed_vector!(Vec7, 7); +build_fixed_vector!(Vec8, 8); +build_fixed_vector!(Vec9, 9); +build_fixed_vector!(Vec10, 10); +build_fixed_vector!(Vec11, 11); +build_fixed_vector!(Vec12, 12); +build_fixed_vector!(Vec13, 13); +build_fixed_vector!(Vec14, 14); +build_fixed_vector!(Vec15, 15); +build_fixed_vector!(Vec16, 16); +build_variable_vector!(VecNx1, 1); +build_variable_vector!(VecNx2, 2); +build_variable_vector!(VecNx3, 3); +build_variable_vector!(VecNx4, 4); +build_variable_vector!(VecNx5, 5); +build_variable_vector!(VecNx6, 6); +build_variable_vector!(VecNx7, 7); +build_variable_vector!(VecNx8, 8); +build_variable_vector!(VecNx9, 9); +build_variable_vector!(VecNx10, 10); +build_variable_vector!(VecNx11, 11); +build_variable_vector!(VecNx12, 12); +build_variable_vector!(VecNx13, 13); +build_variable_vector!(VecNx14, 14); +build_variable_vector!(VecNx15, 15); +build_variable_vector!(VecNx16, 16); diff --git a/shader-compiler/src/lib.rs b/shader-compiler/src/lib.rs index 16b875e..bcbb3f0 100644 --- a/shader-compiler/src/lib.rs +++ b/shader-compiler/src/lib.rs @@ -25,7 +25,7 @@ mod test { buildable_struct!{ struct S3 { p: *mut S2, - v: ::backend::VecNx4, + v: ::backend::types::VecNx4, } } }