working on implementing LLVM 7.0 shader compiler backend
authorJacob Lifshay <programmerjake@gmail.com>
Thu, 11 Oct 2018 08:52:26 +0000 (01:52 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Thu, 11 Oct 2018 08:52:26 +0000 (01:52 -0700)
Currently crashes in LLVM when running tests for shader-compiler-llvm-7

.gitignore
shader-compiler-llvm-7/src/backend.rs
shader-compiler-llvm-7/src/lib.rs
shader-compiler-llvm-7/src/tests.rs [new file with mode: 0644]
shader-compiler/src/backend/mod.rs
shader-compiler/src/backend/types.rs [new file with mode: 0644]
shader-compiler/src/lib.rs

index 3d6ba8a1cfce98f65f9de43c0d47743dad1d29a6..8889c73214a696488957670ff4596cc58affa8a9 100644 (file)
@@ -4,3 +4,4 @@
 /Cargo.lock
 /VK-GL-CTS
 /TestResults.qpa
+/.vscode
\ No newline at end of file
index 74af1b523550289731a9f30d4261c484dadce264..cbb7b51365c9edb16faa12208311fb29cb4e2516 100644 (file)
@@ -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<backend::CompilerIndependentConfig> 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<Self> {
-        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<Vec<llvm_sys::prelude::LLVMModuleRef>>,
+    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<LLVM7Value>) -> 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<LLVM7Module, backend::VerificationFailure<'a, LLVM7Module>> {
+        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<Void>(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<SCU: ShaderCompilerUser>(
-        shader_compiler_user: SCU,
-        config: LLVM7ShaderCompilerConfig,
-    ) -> SCU::ReturnType {
-        let context = unsafe {
-            LLVM7Context {
+    fn run<U: backend::CompilerUser>(
+        self,
+        user: U,
+        config: LLVM7CompilerConfig,
+    ) -> Result<Box<dyn backend::CompiledCode<U::FunctionKey>>, 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!()
+        }
     }
 }
index fad391e86cb14e69246b4b66992bc903e8630f92..c582f509d0e2c6eeb321481b47ef87f035d3a073 100644 (file)
@@ -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 (file)
index 0000000..9e3bfaa
--- /dev/null
@@ -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<CompileInputs<'a, C, FunctionKey>, 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::<GeneratedFunctionType>(),
+                );
+                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();
+        }
+    }
+}
index 71b18e4db987100ce87ec8b06f75fa9417862ff3..1ff6b9e140c6aeccb83026da06c8ce08beaba3e9 100644 (file)
 
 //! 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) -> <Self::Context as Context<'a>>::BasicBlock;
+    /// build a return instruction
+    fn build_return(
+        self,
+        value: Option<<Self::Context as Context<'a>>::Value>,
+    ) -> <Self::Context as Context<'a>>::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>) -> Ty;
-    /// build a type
-    fn build<T: BuildableType>(&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: <Self::Context as Context<'a>>::BuildableBasicBlock,
+    ) -> <Self::Context as Context<'a>>::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>) -> 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) -> <Self::Context as Context<'a>>::Value;
 }
 
-impl<T: BuildableType> BuildableType for UnsafeCell<T> {
-    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) -> <Self::Context as Context<'a>>::BasicBlock;
+    /// get the `Value` corresponding to `Self`
+    fn as_value(&self) -> <Self::Context as Context<'a>>::Value {
+        self.as_basic_block().as_value()
     }
 }
 
-mod hidden {
-    pub trait ScalarBuildableTypeBase {}
-}
-
-impl<T: hidden::ScalarBuildableTypeBase> hidden::ScalarBuildableTypeBase for UnsafeCell<T> {}
-
-/// trait for rust types that can be an element of a vector and be built using `TypeBuilder`
-pub trait ScalarBuildableType: BuildableType + hidden::ScalarBuildableTypeBase {}
-
-impl<T: ScalarBuildableType> ScalarBuildableType for UnsafeCell<T> {}
-
-/// 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) -> <Self::Context as Context<'a>>::Value;
+    /// append a new `BasicBlock` to `Self`
+    fn append_new_basic_block(
+        &mut self,
+        name: Option<&str>,
+    ) -> <Self::Context as Context<'a>>::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<T: ToString>(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<T: BuildableType> 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<T: BuildableType> 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<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 Option<unsafe extern "C" fn($($arguments,)*)> {}
-
-            impl<$($arguments: BuildableType),*> ScalarBuildableType for Option<unsafe extern "C" fn($($arguments,)*)> {}
-        };
-    }
-
-macro_rules! build_function_type {
-        ($($arguments:ident,)*) => {
-            impl<R: BuildableType, $($arguments: BuildableType),*> BuildableType for Option<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<'a, M: Module<'a>> Error for VerificationFailure<'a, M> {}
 
-            impl<R: BuildableType, $($arguments: BuildableType),*> hidden::ScalarBuildableTypeBase for Option<unsafe extern "C" fn($($arguments,)*) -> R> {}
-
-            impl<R: BuildableType, $($arguments: BuildableType),*> ScalarBuildableType for Option<unsafe extern "C" fn($($arguments,)*) -> R> {}
-        };
+impl<'a, M: Module<'a>> From<VerificationFailure<'a, M>> 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<T: BuildableType> 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<Element: ScalarBuildableType> {
-            /// elements of the vector `Self`
-            pub elements: [Element; $length],
-        }
-
-        impl<Element: ScalarBuildableType> Deref for $name<Element> {
-            type Target = [Element; $length];
-            fn deref(&self) -> &Self::Target {
-                &self.elements
-            }
-        }
-
-        impl<Element: ScalarBuildableType> DerefMut for $name<Element> {
-            fn deref_mut(&mut self) -> &mut Self::Target {
-                &mut self.elements
-            }
-        }
-
-        impl<Element: ScalarBuildableType> BuildableType for $name<Element> {
-            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<Element: ScalarBuildableType> Vector for $name<Element> {
-            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<Element: ScalarBuildableType> {
-            #[doc(hidden)]
-            __Dummy(__VectorNeverType, PhantomData<Element>),
-        }
-
-        impl<Element: ScalarBuildableType> BuildableType for $name<Element> {
-            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<Element: ScalarBuildableType> Vector for $name<Element> {
-            type Element = Element;
-            const LENGTH: VectorLength = {
-                VectorLength::Variable {
-                    base_length: $base_length,
-                }
-            };
-        }
-    };
 }
 
-/// alternate name for `VecNx1`
-pub type VecN<Element> = VecNx1<Element>;
-
-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: <Self::Context as Context<'a>>::Type,
+    ) -> <Self::Context as Context<'a>>::Function;
+    /// verify `Self`, converting into a `VerifiedModule`
+    fn verify(
+        self,
+    ) -> Result<<Self::Context as Context<'a>>::VerifiedModule, VerificationFailure<'a, Self>>;
+    /// convert into a `VerifiedModule` without verifing
+    unsafe fn to_verified_module_unchecked(self) -> <Self::Context as Context<'a>>::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) -> <Self::Context as Context<'a>>::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<K, C::Function>,
+}
+
+/// the final compiled code
+pub trait CompiledCode<K: Hash + Eq + Send + Sync>: 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<unsafe extern "C" fn()>;
+}
+
+/// 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<CompileInputs<'a, C, Self::FunctionKey>, 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<CompilerIndependentConfig> + 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<SCU: ShaderCompilerUser>(
-        shader_compiler_user: SCU,
+    fn run<U: CompilerUser>(
+        self,
+        user: U,
         config: Self::Config,
-    ) -> SCU::ReturnType;
+    ) -> Result<Box<dyn CompiledCode<U::FunctionKey>>, U::Error>;
 }
diff --git a/shader-compiler/src/backend/types.rs b/shader-compiler/src/backend/types.rs
new file mode 100644 (file)
index 0000000..c364796
--- /dev/null
@@ -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>) -> Ty;
+    /// build a type
+    fn build<T: BuildableType>(&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>) -> 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<T: BuildableType> BuildableType for UnsafeCell<T> {
+    fn build<'a, Ty: Type<'a>, TB: TypeBuilder<'a, Ty>>(type_builder: &TB) -> Ty {
+        T::build(type_builder)
+    }
+}
+
+mod hidden {
+    pub trait ScalarBuildableTypeBase {}
+}
+
+impl<T: hidden::ScalarBuildableTypeBase> hidden::ScalarBuildableTypeBase for UnsafeCell<T> {}
+
+/// trait for rust types that can be an element of a vector and be built using `TypeBuilder`
+pub trait ScalarBuildableType: BuildableType + hidden::ScalarBuildableTypeBase {}
+
+impl<T: ScalarBuildableType> ScalarBuildableType for UnsafeCell<T> {}
+
+/// 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<T: BuildableType> 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<T: BuildableType> 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<T: BuildableType> BuildableType for NonNull<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 NonNull<T> {}
+
+impl<'b, T: BuildableType> ScalarBuildableType for NonNull<T> {}
+
+impl<T: BuildableType> BuildableType for Option<NonNull<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<NonNull<T>> {}
+
+impl<'b, T: BuildableType> ScalarBuildableType for Option<NonNull<T>> {}
+
+macro_rules! build_unit_function_type {
+        ($($arguments:ident,)*) => {
+            impl<$($arguments: BuildableType),*> BuildableType for Option<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 Option<unsafe extern "C" fn($($arguments,)*)> {}
+
+            impl<$($arguments: BuildableType),*> ScalarBuildableType for Option<unsafe extern "C" fn($($arguments,)*)> {}
+
+            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<R: BuildableType, $($arguments: BuildableType),*> BuildableType for Option<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<R: BuildableType, $($arguments: BuildableType),*> hidden::ScalarBuildableTypeBase for Option<unsafe extern "C" fn($($arguments,)*) -> R> {}
+
+            impl<R: BuildableType, $($arguments: BuildableType),*> ScalarBuildableType for Option<unsafe extern "C" fn($($arguments,)*) -> R> {}
+
+            impl<R: BuildableType, $($arguments: BuildableType),*> 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<R: BuildableType, $($arguments: BuildableType),*> hidden::ScalarBuildableTypeBase for unsafe extern "C" fn($($arguments,)*) -> R {}
+
+            impl<R: BuildableType, $($arguments: BuildableType),*> 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<T: BuildableType> 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<Element: ScalarBuildableType> {
+            /// elements of the vector `Self`
+            pub elements: [Element; $length],
+        }
+
+        impl<Element: ScalarBuildableType> Deref for $name<Element> {
+            type Target = [Element; $length];
+            fn deref(&self) -> &Self::Target {
+                &self.elements
+            }
+        }
+
+        impl<Element: ScalarBuildableType> DerefMut for $name<Element> {
+            fn deref_mut(&mut self) -> &mut Self::Target {
+                &mut self.elements
+            }
+        }
+
+        impl<Element: ScalarBuildableType> BuildableType for $name<Element> {
+            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<Element: ScalarBuildableType> Vector for $name<Element> {
+            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<Element: ScalarBuildableType> {
+            #[doc(hidden)]
+            __Dummy(__VectorNeverType, PhantomData<Element>),
+        }
+
+        impl<Element: ScalarBuildableType> BuildableType for $name<Element> {
+            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<Element: ScalarBuildableType> Vector for $name<Element> {
+            type Element = Element;
+            const LENGTH: VectorLength = {
+                VectorLength::Variable {
+                    base_length: $base_length,
+                }
+            };
+        }
+    };
+}
+
+/// alternate name for `VecNx1`
+pub type VecN<Element> = VecNx1<Element>;
+
+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);
index 16b875ec7566695717969c8f1559a9e9d3a4955d..bcbb3f03b6f8cd6350d998cf7375a3d2fb52c53c 100644 (file)
@@ -25,7 +25,7 @@ mod test {
     buildable_struct!{
         struct S3 {
             p: *mut S2,
-            v: ::backend::VecNx4<f32>,
+            v: ::backend::types::VecNx4<f32>,
         }
     }
 }