Entry points work.
3 files changed, 401 insertions(+), 118 deletions(-)

M src/compile/mod.rs
M src/compile/tests.rs
M src/verify.rs
M src/compile/mod.rs +228 -42
@@ 1,5 1,9 @@ 
 //! The actual compiler backend!  Should take in a VALIDATED AST and
 //! turn it to SPIR-V.  Uses the `rspirv` crate for output.
+//!
+//! This produces SPIR-V code suitable for the Vulkan execution model,
+//! see [the Vulkan 1.1 spec, Appendix A](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap40.html#spirvenv-module-validation)
+//! for details, particularly the "Validation Rules within a Module" section.
 
 use fnv::FnvHashMap;
 use rspirv::mr::Builder;

          
@@ 106,7 110,7 @@ impl CContext {
     /// Returns the Word bound to the given var name, or panics if not found.
     /// All unknown vars should get caught by the validation step.
     pub fn lookup_var(&self, name: &str) -> &VarBinding {
-        assert!(self.symtable.len() > 1, "No scope for variable lookup!");
+        assert!(self.symtable.len() > 0, "No scope for variable lookup!");
         for scope in self.symtable.iter().rev() {
             if let Some(w) = scope.get(name) {
                 return w;

          
@@ 588,6 592,162 @@ impl CContext {
         Ok(())
     }
 
+    /// This compiles an entry point function with no args or return values,
+    /// and takes the args and return values for the given function and turns
+    /// them into input/output variables and does the appropriate loads and stores.
+    ///
+    /// Returns the word of the compiled function, and the words of all global
+    /// vars it touches.  It declares its own global vars though.  This may not
+    /// always be what we want, but oh well for now.
+    fn compile_entry_stub_function(
+        &mut self,
+        ctx: &verify::VContext,
+        name: &str,
+    ) -> Result<(spirv::Word, Vec<spirv::Word>), crate::Error> {
+        // Dig up the function def, and declare input/output global vars for it.
+        // TODO: Name strings for input/output vars?
+        let def = ctx.get_defined_function(name);
+        if let verify::TypeDef::Function(ref params, ref rettype) = def.functiontype {
+            // Value, value type, pointer type.
+            let input_words: Vec<(spirv::Word, spirv::Word, spirv::Word)> = params
+                .iter()
+                .map(|p| {
+                    let t_word = self.get_type(p);
+                    // We don't bother storing pointer types in our typetable for now,
+                    // this is basically the only place they're used.
+                    let t_ptr_word = self
+                        .b
+                        .type_pointer(None, spirv::StorageClass::Input, t_word);
+                    let value_word =
+                        self.b
+                            .variable(t_ptr_word, None, spirv::StorageClass::Input, None);
+                    (value_word, t_word, t_ptr_word)
+                })
+                .collect();
+
+            let (output_word, t_word, _t_ptr_word) = {
+                let t_word = self.get_type(rettype);
+                let t_ptr_word = self
+                    .b
+                    .type_pointer(None, spirv::StorageClass::Output, t_word);
+                let value_word =
+                    self.b
+                        .variable(t_ptr_word, None, spirv::StorageClass::Output, None);
+                (value_word, t_word, t_ptr_word)
+            };
+
+            // Now actually compile the function that calls our real function.
+            let functype_word = self.add_type(&verify::TypeDef::Function(
+                vec![],
+                Box::new(verify::TypeDef::Unit),
+            ));
+            let voidtype_word = self.add_type(&verify::TypeDef::Unit);
+
+            let f_word = self.b.begin_function(
+                voidtype_word,
+                None,
+                spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
+                functype_word,
+            )?;
+            self.b.begin_basic_block(None)?;
+            // Load inputs
+            let r_inputs: Result<Vec<spirv::Word>, _> = input_words
+                .clone()
+                .into_iter()
+                .map(|(v_word, t_word, _t_ptr_word)| self.b.load(t_word, None, v_word, None, []))
+                .collect();
+            let inputs = r_inputs?;
+            // Call function
+            let fun_binding = *self.lookup_var(name);
+            let funcall_result_word = self
+                .b
+                .function_call(t_word, None, fun_binding.address, &inputs)
+                .unwrap();
+            // Save output
+            self.b.store(output_word, funcall_result_word, None, [])?;
+            self.b.ret()?;
+            self.b.end_function()?;
+
+            // We must also return the function's interface, that is, the words of
+            // all the global variables it touches.
+            let mut interface: Vec<u32> = input_words
+                .into_iter()
+                .map(|(input_word, _, _)| input_word)
+                .collect();
+            interface.push(output_word);
+
+            // That's it!  The actual OpEntryPoint instructions and names and stuff
+            // get handled by the caller.
+            Ok((f_word, interface))
+        } else {
+            unreachable!("Function type is not TypeDef::Function")
+        }
+
+        /*
+                let ftype = self.add_type(&verify::TypeDef::Function(
+                    vec![],
+                    Box::new(verify::TypeDef::Unit),
+                ));
+                let void = self.add_type(&verify::TypeDef::Unit);
+
+                // Actually make our stub functions
+                let v_word = self.b.begin_function(
+                    void,
+                    None,
+                    spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
+                    ftype,
+                )?;
+                self.b.begin_basic_block(None)?;
+                self.b.ret()?;
+                self.b.end_function()?;
+
+                let f_word = self.b.begin_function(
+                    void,
+                    None,
+                    spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
+                    ftype,
+                )?;
+                self.b.begin_basic_block(None)?;
+                self.b.ret()?;
+                self.b.end_function()?;
+
+
+                // Compile arguments
+                let param_words: Result<Vec<(spirv::Word, spirv::Word)>, _> = params
+                    .clone()
+                    .iter()
+                    .map(|param| self.compile_expr(param, ctx))
+                    .collect();
+                let param_words = param_words?;
+                let param_words: Vec<spirv::Word> = param_words
+                    .into_iter()
+                    .map(|(value_word, _type_word)| value_word)
+                    .collect();
+                // Look up function word
+                // ...which requires having compiled the function first.  :|
+                // TODO: Fix!  Somehow...  If it isn't, this will panic
+                // with variable not found.
+                let binding = *self.lookup_var(&fname);
+                // Get the function's return type.  We have to fetch this
+                // from the VContext, since we can't really dig it back
+                // out of the SPIR-V.
+                let functiondef = ctx
+                    .functions
+                    .get(fname)
+                    .expect("Function does not exist for funcall");
+                if let verify::TypeDef::Function(ref _params, ref rettype) = functiondef.functiontype {
+                    // TODO: Tail call optimization would go here if it went anywhere.
+                    let rettype_word = self.get_type(rettype);
+                    let value_word =
+                        self.b
+                            .function_call(rettype_word, None, binding.address, param_words)?;
+                    (value_word, rettype_word)
+                } else {
+                    unreachable!("Function type is not TypeDef::Function")
+                }
+        */
+    }
+
     /// Okay, so SPIR-V is a bit persnickity about entry points.
     /// An entry point MUST be a function that has no parameters
     /// or returns, and must describe the things it reads (and writes?)

          
@@ 603,52 763,78 @@ impl CContext {
     /// anything else.
     ///
     /// TODO: Handle it!
-    fn mongle_entry_points(&mut self, _ctx: &verify::VContext) -> Result<(), crate::Error> {
-        let ftype = self.add_type(&verify::TypeDef::Function(
-            vec![],
-            Box::new(verify::TypeDef::Unit),
-        ));
-        let void = self.add_type(&verify::TypeDef::Unit);
-
-        // Actually make our stub functions
-        let v_word = self.b.begin_function(
-            void,
-            None,
-            spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
-            ftype,
-        )?;
-        self.b.begin_basic_block(None)?;
-        self.b.ret()?;
-        self.b.end_function()?;
+    fn mongle_entry_points(&mut self, ctx: &verify::VContext) -> Result<(), crate::Error> {
+        for (name, typ) in &[
+            ("vertex", spirv::ExecutionModel::Vertex),
+            ("fragment", spirv::ExecutionModel::Fragment),
+        ][..]
+        {
+            let (e_word, e_interface) = self.compile_entry_stub_function(ctx, name)?;
+            let e_name = format!("_{}_entry", name);
+            self.b.name(e_word, &e_name);
+            self.b.entry_point(*typ, e_word, e_name, e_interface);
 
-        let f_word = self.b.begin_function(
-            void,
-            None,
-            spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
-            ftype,
-        )?;
-        self.b.begin_basic_block(None)?;
-        self.b.ret()?;
-        self.b.end_function()?;
-
-        // We're allowed to reuse the same function as different
-        // entry points, so I guess we'll just do that for now
-        let vert_name = "_vertex_entry";
-        let frag_name = "_fragment_entry";
-        self.b.name(v_word, vert_name);
-        self.b.name(f_word, frag_name);
-        self.b
-            .entry_point(spirv::ExecutionModel::Vertex, v_word, vert_name, []);
-        self.b
-            .entry_point(spirv::ExecutionModel::Fragment, f_word, frag_name, []);
-        // TODO: Heck, what the heck to do with this??
-        // Well, GLSL uses OriginUpperLeft, so let's do that for now.
-        self.b
-            .execution_mode(f_word, spirv::ExecutionMode::OriginUpperLeft, []);
+            if let spirv::ExecutionModel::Fragment = typ {
+                // TODO: Heck, what the heck to do with this??
+                // Well, GLSL uses OriginUpperLeft, so let's do that for now.
+                self.b
+                    .execution_mode(e_word, spirv::ExecutionMode::OriginUpperLeft, []);
+            }
+        }
 
         Ok(())
 
         /*
+                let frag_name = "_fragment_entry";
+                self.b.name(f_word, frag_name);
+                self.b
+                    .entry_point(spirv::ExecutionModel::Vertex, v_word, vert_name, []);
+
+                let ftype = self.add_type(&verify::TypeDef::Function(
+                    vec![],
+                    Box::new(verify::TypeDef::Unit),
+                ));
+                let void = self.add_type(&verify::TypeDef::Unit);
+
+                // Actually make our stub functions
+                let v_word = self.b.begin_function(
+                    void,
+                    None,
+                    spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
+                    ftype,
+                )?;
+                self.b.begin_basic_block(None)?;
+                self.b.ret()?;
+                self.b.end_function()?;
+
+                let f_word = self.b.begin_function(
+                    void,
+                    None,
+                    spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
+                    ftype,
+                )?;
+                self.b.begin_basic_block(None)?;
+                self.b.ret()?;
+                self.b.end_function()?;
+
+                // We're allowed to reuse the same function as different
+                // entry points, so I guess we'll just do that for now
+                let vert_name = "_vertex_entry";
+                let frag_name = "_fragment_entry";
+                self.b.name(v_word, vert_name);
+                self.b.name(f_word, frag_name);
+                self.b
+                    .entry_point(spirv::ExecutionModel::Vertex, v_word, vert_name, []);
+                self.b
+                    .entry_point(spirv::ExecutionModel::Fragment, f_word, frag_name, []);
+                // TODO: Heck, what the heck to do with this??
+                // Well, GLSL uses OriginUpperLeft, so let's do that for now.
+                self.b
+                    .execution_mode(f_word, spirv::ExecutionMode::OriginUpperLeft, []);
+
+                Ok(())
+        */
+        /*
         // If this function is an entry point we declare it such.
         // TODO: This is a little jank but works.
         if def.decl.name == "vertex" {

          
M src/compile/tests.rs +168 -76
@@ 129,34 129,45 @@ fn test_compile_function() {
     let c = compile::compile(&ctx).unwrap();
 
     let m = c.b.module();
+    spirv_val(&m);
     let dis = m.disassemble();
     let rest = validate_module_header(&dis);
     println!("{}", dis);
     validate_lines(
         rest,
-        r#"%6 = OpTypeFunction %1                     
-%7 = OpTypeFunction %2 %2                   
-%10 = OpConstant  %1  1.0                                                                      
-%14 = OpTypeFunction %5                                                  
-%8 = OpFunction  %1  DontInline|Pure|Const %6                                            
-%9 = OpLabel                               
-OpReturnValue %10                            
-OpFunctionEnd                                                                  
-%11 = OpFunction  %2  DontInline|Pure|Const %7                                             
-%12 = OpFunctionParameter  %2                                                                  
-%13 = OpLabel                                                                                                      
+        r#"%6 = OpTypeFunction %1
+%7 = OpTypeFunction %2 %2
+%10 = OpConstant  %1  1.0
+%14 = OpTypePointer Output %1
+%15 = OpVariable  %14  Output
+%16 = OpTypeFunction %5
+%20 = OpTypePointer Input %2
+%21 = OpVariable  %20  Input
+%22 = OpTypePointer Output %2
+%23 = OpVariable  %22  Output
+%8 = OpFunction  %1  DontInline|Pure|Const %6
+%9 = OpLabel
+OpReturnValue %10
+OpFunctionEnd
+%11 = OpFunction  %2  DontInline|Pure|Const %7
+%12 = OpFunctionParameter  %2
+%13 = OpLabel
 OpReturnValue %12
 OpFunctionEnd
-%15 = OpFunction  %5  DontInline|Const %14
-%16 = OpLabel
+%17 = OpFunction  %5  DontInline|Const %16
+%18 = OpLabel
+%19 = OpFunctionCall  %1  %8
+OpStore %15 %19
 OpReturn
 OpFunctionEnd
-%17 = OpFunction  %5  DontInline|Const %14
-%18 = OpLabel
+%24 = OpFunction  %5  DontInline|Const %16
+%25 = OpLabel
+%26 = OpLoad  %2  %21
+%27 = OpFunctionCall  %2  %11 %26
+OpStore %23 %27
 OpReturn
 OpFunctionEnd"#,
     );
-    spirv_val(&m);
 }
 
 #[test]

          
@@ 190,23 201,37 @@ fn test_compile_identity() {
     validate_lines(
         rest,
         r#"%6 = OpTypeFunction %2 %2
-%13 = OpTypeFunction %5                                                                                           
-%7 = OpFunction  %1  DontInline|Pure|Const %3                                                                     
-%8 = OpFunctionParameter  %1                                                                                      
+%13 = OpTypePointer Input %1
+%14 = OpVariable  %13  Input
+%15 = OpTypePointer Output %1
+%16 = OpVariable  %15  Output
+%17 = OpTypeFunction %5
+%22 = OpTypePointer Input %2
+%23 = OpVariable  %22  Input
+%24 = OpTypePointer Output %2
+%25 = OpVariable  %24  Output
+%7 = OpFunction  %1  DontInline|Pure|Const %3
+%8 = OpFunctionParameter  %1
 %9 = OpLabel
 OpReturnValue %8
 OpFunctionEnd
-%10 = OpFunction  %2  DontInline|Pure|Const %6                                                                    
-%11 = OpFunctionParameter  %2                                                                                     
+%10 = OpFunction  %2  DontInline|Pure|Const %6
+%11 = OpFunctionParameter  %2
 %12 = OpLabel
 OpReturnValue %11
 OpFunctionEnd
-%14 = OpFunction  %5  DontInline|Const %13
-%15 = OpLabel
+%18 = OpFunction  %5  DontInline|Const %17
+%19 = OpLabel
+%20 = OpLoad  %1  %14
+%21 = OpFunctionCall  %1  %7 %20
+OpStore %16 %21
 OpReturn
 OpFunctionEnd
-%16 = OpFunction  %5  DontInline|Const %13
-%17 = OpLabel
+%26 = OpFunction  %5  DontInline|Const %17
+%27 = OpLabel
+%28 = OpLoad  %2  %23
+%29 = OpFunctionCall  %2  %10 %28
+OpStore %25 %29
 OpReturn
 OpFunctionEnd"#,
     );

          
@@ 246,30 271,41 @@ fn test_compile_literals() {
     let rest = validate_module_header(&dis);
     validate_lines(
         rest,
-        r#"%6 = OpTypeFunction %1                                                                                            
+        r#"%6 = OpTypeFunction %1
 %7 = OpTypeFunction %2 %2
-%10 = OpConstantTrue  %4                                                                                          
+%10 = OpConstantTrue  %4
 %11 = OpConstantFalse  %4
 %12 = OpConstant  %1  -99.1
-%13 = OpConstant  %1  3.0                                                                                         
-%17 = OpTypeFunction %5                                                                                           
-%8 = OpFunction  %1  DontInline|Pure|Const %6                                                                     
+%13 = OpConstant  %1  3.0
+%17 = OpTypePointer Output %1
+%18 = OpVariable  %17  Output
+%19 = OpTypeFunction %5
+%23 = OpTypePointer Input %2
+%24 = OpVariable  %23  Input
+%25 = OpTypePointer Output %2
+%26 = OpVariable  %25  Output
+%8 = OpFunction  %1  DontInline|Pure|Const %6
 %9 = OpLabel
 OpReturnValue %13
 OpFunctionEnd
-%14 = OpFunction  %2  DontInline|Pure|Const %7                                                                    
-%15 = OpFunctionParameter  %2                                                                                     
+%14 = OpFunction  %2  DontInline|Pure|Const %7
+%15 = OpFunctionParameter  %2
 %16 = OpLabel
 OpReturnValue %15
 OpFunctionEnd
-%18 = OpFunction  %5  DontInline|Const %17
-%19 = OpLabel
+%20 = OpFunction  %5  DontInline|Const %19
+%21 = OpLabel
+%22 = OpFunctionCall  %1  %8
+OpStore %18 %22
 OpReturn
 OpFunctionEnd
-%20 = OpFunction  %5  DontInline|Const %17
-%21 = OpLabel                                                                                                     
+%27 = OpFunction  %5  DontInline|Const %19
+%28 = OpLabel
+%29 = OpLoad  %2  %24
+%30 = OpFunctionCall  %2  %14 %29
+OpStore %26 %30
 OpReturn
-OpFunctionEnd"#
+OpFunctionEnd"#,
     );
     spirv_val(&m);
 }

          
@@ 307,25 343,39 @@ fn test_compile_funcall() {
     let rest = validate_module_header(&dis);
     validate_lines(
         rest,
-        r#"%13 = OpTypeFunction %5
-%6 = OpFunction  %1  DontInline|Pure|Const %3                                                                     
-%7 = OpFunctionParameter  %1                                                                                      
-%8 = OpLabel                                                                                                      
+        r#"%13 = OpTypePointer Input %1
+%14 = OpVariable  %13  Input
+%15 = OpTypePointer Output %1
+%16 = OpVariable  %15  Output
+%17 = OpTypeFunction %5
+%22 = OpTypePointer Input %1
+%23 = OpVariable  %22  Input
+%24 = OpTypePointer Output %1
+%25 = OpVariable  %24  Output
+%6 = OpFunction  %1  DontInline|Pure|Const %3
+%7 = OpFunctionParameter  %1
+%8 = OpLabel
 OpReturnValue %7
 OpFunctionEnd
 %9 = OpFunction  %1  DontInline|Pure|Const %3
-%10 = OpFunctionParameter  %1                                                                                     
-%11 = OpLabel                                                                                                     
+%10 = OpFunctionParameter  %1
+%11 = OpLabel
 %12 = OpFunctionCall  %1  %6 %10
 OpReturnValue %12
 OpFunctionEnd
-%14 = OpFunction  %5  DontInline|Const %13
-%15 = OpLabel
+%18 = OpFunction  %5  DontInline|Const %17
+%19 = OpLabel
+%20 = OpLoad  %1  %14
+%21 = OpFunctionCall  %1  %6 %20
+OpStore %16 %21
 OpReturn
-OpFunctionEnd                                                                                                     
-%16 = OpFunction  %5  DontInline|Const %13
-%17 = OpLabel
-OpReturn                                                                                                          
+OpFunctionEnd
+%26 = OpFunction  %5  DontInline|Const %17
+%27 = OpLabel
+%28 = OpLoad  %1  %23
+%29 = OpFunctionCall  %1  %9 %28
+OpStore %25 %29
+OpReturn
 OpFunctionEnd"#,
     );
     spirv_val(&m);

          
@@ 364,25 414,39 @@ fn test_compile_recursive_funcall() {
     let rest = validate_module_header(&dis);
     validate_lines(
         rest,
-        r#"%13 = OpTypeFunction %5
-%6 = OpFunction  %1  DontInline|Pure|Const %3                                                                     
-%7 = OpFunctionParameter  %1                                                                                      
-%8 = OpLabel                                                                                                      
+        r#"%13 = OpTypePointer Input %1
+%14 = OpVariable  %13  Input
+%15 = OpTypePointer Output %1
+%16 = OpVariable  %15  Output
+%17 = OpTypeFunction %5
+%22 = OpTypePointer Input %1
+%23 = OpVariable  %22  Input
+%24 = OpTypePointer Output %1
+%25 = OpVariable  %24  Output
+%6 = OpFunction  %1  DontInline|Pure|Const %3
+%7 = OpFunctionParameter  %1
+%8 = OpLabel
 OpReturnValue %7
 OpFunctionEnd
 %9 = OpFunction  %1  DontInline|Pure|Const %3
-%10 = OpFunctionParameter  %1                                                                                     
-%11 = OpLabel                                                                                                     
+%10 = OpFunctionParameter  %1
+%11 = OpLabel
 %12 = OpFunctionCall  %1  %9 %10
 OpReturnValue %12
 OpFunctionEnd
-%14 = OpFunction  %5  DontInline|Const %13
-%15 = OpLabel
+%18 = OpFunction  %5  DontInline|Const %17
+%19 = OpLabel
+%20 = OpLoad  %1  %14
+%21 = OpFunctionCall  %1  %6 %20
+OpStore %16 %21
 OpReturn
-OpFunctionEnd                                                                                                     
-%16 = OpFunction  %5  DontInline|Const %13
-%17 = OpLabel
-OpReturn                                                                                                          
+OpFunctionEnd
+%26 = OpFunction  %5  DontInline|Const %17
+%27 = OpLabel
+%28 = OpLoad  %1  %23
+%29 = OpFunctionCall  %1  %9 %28
+OpStore %25 %29
+OpReturn
 OpFunctionEnd"#,
     );
     spirv_val(&m);

          
@@ 419,9 483,9 @@ fn test_compile_types_misc() {
         rest,
         r#"%1 = OpTypeFloat 32
 %2 = OpTypeStruct %1 %1 %1 %1
-%3 = OpTypeFunction %1 %1                                                                                           
-%4 = OpTypeBool                                                                                                     
-%5 = OpTypeVoid"#
+%3 = OpTypeFunction %1 %1
+%4 = OpTypeBool
+%5 = OpTypeVoid"#,
     );
 }
 

          
@@ 482,10 546,10 @@ fn test_compile_structdefs() {
         rest,
         r#"%1 = OpTypeFloat 32
 %2 = OpTypeStruct %1 %1 %1 %1
-%3 = OpTypeFunction %1 %1                                                                                           
+%3 = OpTypeFunction %1 %1
 %4 = OpTypeBool
 %5 = OpTypeVoid
-%6 = OpTypeStruct %1 %1 %4                                                                                          
+%6 = OpTypeStruct %1 %1 %4
 %7 = OpTypeStruct %6 %1 %6"#,
     );
 }

          
@@ 525,7 589,15 @@ fn test_compile_add() {
     validate_lines(
         rest,
         r#"%12 = OpConstant  %1  5.0
-%14 = OpTypeFunction %5
+%14 = OpTypePointer Input %1
+%15 = OpVariable  %14  Input
+%16 = OpTypePointer Output %1
+%17 = OpVariable  %16  Output
+%18 = OpTypeFunction %5
+%23 = OpTypePointer Input %1
+%24 = OpVariable  %23  Input
+%25 = OpTypePointer Output %1
+%26 = OpVariable  %25  Output
 %6 = OpFunction  %1  DontInline|Pure|Const %3
 %7 = OpFunctionParameter  %1
 %8 = OpLabel

          
@@ 537,12 609,18 @@ OpFunctionEnd
 %13 = OpFAdd  %1  %12 %10
 OpReturnValue %13
 OpFunctionEnd
-%15 = OpFunction  %5  DontInline|Const %14
-%16 = OpLabel
+%19 = OpFunction  %5  DontInline|Const %18
+%20 = OpLabel
+%21 = OpLoad  %1  %15
+%22 = OpFunctionCall  %1  %6 %21
+OpStore %17 %22
 OpReturn
 OpFunctionEnd
-%17 = OpFunction  %5  DontInline|Const %14
-%18 = OpLabel
+%27 = OpFunction  %5  DontInline|Const %18
+%28 = OpLabel
+%29 = OpLoad  %1  %24
+%30 = OpFunctionCall  %1  %9 %29
+OpStore %26 %30
 OpReturn
 OpFunctionEnd"#,
     );

          
@@ 595,7 673,15 @@ fn test_compile_if() {
         r#"%15 = OpConstantTrue  %4
 %16 = OpConstant  %1  5.0
 %18 = OpConstant  %1  50.0
-%21 = OpTypeFunction %5
+%21 = OpTypePointer Input %1
+%22 = OpVariable  %21  Input
+%23 = OpTypePointer Output %1
+%24 = OpVariable  %23  Output
+%25 = OpTypeFunction %5
+%30 = OpTypePointer Input %1
+%31 = OpVariable  %30  Input
+%32 = OpTypePointer Output %1
+%33 = OpVariable  %32  Output
 %6 = OpFunction  %1  DontInline|Pure|Const %3
 %7 = OpFunctionParameter  %1
 %8 = OpLabel

          
@@ 616,12 702,18 @@ OpBranch %14
 %20 = OpPhi  %1  %17 %12 %19 %13
 OpReturnValue %20
 OpFunctionEnd
-%22 = OpFunction  %5  DontInline|Const %21
-%23 = OpLabel
+%26 = OpFunction  %5  DontInline|Const %25
+%27 = OpLabel
+%28 = OpLoad  %1  %22
+%29 = OpFunctionCall  %1  %6 %28
+OpStore %24 %29
 OpReturn
 OpFunctionEnd
-%24 = OpFunction  %5  DontInline|Const %21
-%25 = OpLabel
+%34 = OpFunction  %5  DontInline|Const %25
+%35 = OpLabel
+%36 = OpLoad  %1  %31
+%37 = OpFunctionCall  %1  %9 %36
+OpStore %33 %37
 OpReturn
 OpFunctionEnd"#,
     );

          
M src/verify.rs +5 -0
@@ 222,6 222,11 @@ impl VContext {
     pub fn get_defined_type(&self, name: &Type) -> &TypeDef {
         self.types.get(name).unwrap()
     }
+
+    /// Gets a function, panics if it doesn't exist.
+    pub fn get_defined_function(&self, name: &str) -> &FunctionDef {
+        self.functions.get(name).unwrap()
+    }
 }
 
 // /// Takes a list of `Lit` and returns true if they're