unsafe fn gen_statement(
        &mut self,
        context: &mut FunctionContext<'_>,
        statement: &Statement,
    ) -> WeldResult<()> {
        use crate::ast::Type::*;
        use crate::sir::StatementKind::*;
        let output = &statement
            .output
            .clone()
            .unwrap_or_else(|| Symbol::new("unused", 0));

        if self.conf.trace_run {
            self.gen_print(
                context.builder,
                context.get_run(),
                CString::new(format!("{}", statement)).unwrap(),
            )?;
        }

        match statement.kind {
            Assign(ref value) => {
                let loaded = self.load(context.builder, context.get_value(value)?)?;
                LLVMBuildStore(context.builder, loaded, context.get_value(output)?);
                Ok(())
            }
            AssignLiteral(_) => {
                use self::numeric::NumericExpressionGen;
                self.gen_assign_literal(context, statement)
            }
            BinOp { .. } => {
                use self::numeric::NumericExpressionGen;
                self.gen_binop(context, statement)
            }
            Broadcast(ref child) => {
                let output_pointer = context.get_value(output)?;
                let child_value = self.load(context.builder, context.get_value(child)?)?;
                let ty = self.llvm_type(context.sir_function.symbol_type(output)?)?;
                let mut result = LLVMGetUndef(ty);
                for i in 0..LLVM_VECTOR_WIDTH {
                    result = LLVMBuildInsertElement(
                        context.builder,
                        result,
                        child_value,
                        self.i32(i as i32),
                        c_str!(""),
                    );
                }
                LLVMBuildStore(context.builder, result, output_pointer);
                Ok(())
            }
            Cast(_, _) => {
                use self::numeric::NumericExpressionGen;
                self.gen_cast(context, statement)
            }
            CUDF {
                ref symbol_name,
                ref args,
            } => {
                let output_pointer = context.get_value(output)?;
                let return_ty = self.llvm_type(context.sir_function.symbol_type(output)?)?;
                let mut arg_tys = vec![];

                // A CUDF with declaration Name[R](T1, T2, T3) has a signature `void Name(T1, T2, T3, R)`.
                for arg in args.iter() {
                    arg_tys.push(LLVMPointerType(
                        self.llvm_type(context.sir_function.symbol_type(arg)?)?,
                        0,
                    ));
                }
                arg_tys.push(LLVMPointerType(return_ty, 0));

                let fn_ret_ty = self.void_type();
                self.intrinsics.add(symbol_name, fn_ret_ty, &mut arg_tys);

                let mut arg_values = vec![];
                for arg in args.iter() {
                    arg_values.push(context.get_value(arg)?);
                }

                arg_values.push(output_pointer);
                let _ = self
                    .intrinsics
                    .call(context.builder, symbol_name, &mut arg_values)?;

                Ok(())
            }
            Deserialize(_) => {
                use self::serde::SerDeGen;
                self.gen_deserialize(context, statement)
            }
            GetField { ref value, index } => {
                let output_pointer = context.get_value(output)?;
                let value_pointer = context.get_value(value)?;
                let elem_pointer =
                    LLVMBuildStructGEP(context.builder, value_pointer, index, c_str!(""));
                let elem = self.load(context.builder, elem_pointer)?;
                LLVMBuildStore(context.builder, elem, output_pointer);
                Ok(())
            }
            KeyExists { ref child, ref key } => {
                use self::hash::GenHash;
                let output_pointer = context.get_value(output)?;
                let child_value = self.load(context.builder, context.get_value(child)?)?;
                let key_pointer = context.get_value(key)?;
                let child_type = context.sir_function.symbol_type(child)?;
                let hash = if let Dict(ref key, _) = *child_type {
                    self.gen_hash(key, context.builder, key_pointer, None)?
                } else {
                    unreachable!()
                };

                let result = {
                    let methods = self.dictionaries.get_mut(child_type).unwrap();
                    methods.gen_key_exists(
                        context.builder,
                        child_value,
                        context.get_value(key)?,
                        hash,
                    )?
                };
                LLVMBuildStore(context.builder, result, output_pointer);
                Ok(())
            }
            Length(ref child) => {
                let output_pointer = context.get_value(output)?;
                let child_value = self.load(context.builder, context.get_value(child)?)?;
                let child_type = context.sir_function.symbol_type(child)?;
                if let Vector(ref elem_type) = *child_type {
                    let methods = self.vectors.get_mut(elem_type).unwrap();
                    let result = methods.gen_size(context.builder, child_value)?;
                    LLVMBuildStore(context.builder, result, output_pointer);
                    Ok(())
                } else if let Dict(_, _) = *child_type {
                    let pointer = {
                        let methods = self.dictionaries.get_mut(child_type).unwrap();
                        methods.gen_size(context.builder, child_value)?
                    };
                    let result = self.load(context.builder, pointer)?;
                    LLVMBuildStore(context.builder, result, output_pointer);
                    Ok(())
                } else {
                    unreachable!()
                }
            }
            Lookup {
                ref child,
                ref index,
            } => {
                let output_pointer = context.get_value(output)?;
                let child_value = self.load(context.builder, context.get_value(child)?)?;
                let child_type = context.sir_function.symbol_type(child)?;
                if let Vector(_) = *child_type {
                    use self::vector::VectorExt;
                    let index_value = self.load(context.builder, context.get_value(index)?)?;
                    let pointer =
                        self.gen_at(context.builder, child_type, child_value, index_value)?;
                    let result = self.load(context.builder, pointer)?;
                    LLVMBuildStore(context.builder, result, output_pointer);
                    Ok(())
                } else if let Dict(ref key, _) = *child_type {
                    use self::hash::GenHash;
                    let hash =
                        self.gen_hash(key, context.builder, context.get_value(index)?, None)?;
                    let result = {
                        let methods = self.dictionaries.get_mut(child_type).unwrap();
                        let slot = methods.gen_lookup(
                            context.builder,
                            &mut self.intrinsics,
                            child_value,
                            context.get_value(index)?,
                            hash,
                            context.get_run(),
                        )?;
                        let value_pointer = methods.slot_ty.value(context.builder, slot);
                        LLVMBuildLoad(context.builder, value_pointer, c_str!(""))
                    };
                    LLVMBuildStore(context.builder, result, output_pointer);
                    Ok(())
                } else {
                    unreachable!()
                }
            }
            OptLookup {
                ref child,
                ref index,
            } => {
                let output_pointer = context.get_value(output)?;
                let child_value = self.load(context.builder, context.get_value(child)?)?;
                let child_type = context.sir_function.symbol_type(child)?;
                if let Dict(ref key, _) = *child_type {
                    use self::hash::GenHash;
                    let hash =
                        self.gen_hash(key, context.builder, context.get_value(index)?, None)?;
                    let (filled, value) = {
                        let methods = self.dictionaries.get_mut(child_type).unwrap();
                        let slot = methods.gen_opt_lookup(
                            context.builder,
                            child_value,
                            context.get_value(index)?,
                            hash,
                        )?;
                        let filled = methods.slot_ty.filled(context.builder, slot);
                        let value_pointer = methods.slot_ty.value(context.builder, slot);
                        // NOTE: This could be an invalid (zeroed value) -- code should check the
                        // boolean.
                        let loaded_value =
                            LLVMBuildLoad(context.builder, value_pointer, c_str!(""));

                        (filled, loaded_value)
                    };

                    let filled = self.i1_to_bool(context.builder, filled);

                    let filled_output_pointer =
                        LLVMBuildStructGEP(context.builder, output_pointer, 0, c_str!(""));
                    LLVMBuildStore(context.builder, filled, filled_output_pointer);
                    let value_output_pointer =
                        LLVMBuildStructGEP(context.builder, output_pointer, 1, c_str!(""));
                    LLVMBuildStore(context.builder, value, value_output_pointer);
                    Ok(())
                } else {
                    unreachable!()
                }
            }
            MakeStruct(ref elems) => {
                let output_pointer = context.get_value(output)?;
                for (i, elem) in elems.iter().enumerate() {
                    let elem_pointer =
                        LLVMBuildStructGEP(context.builder, output_pointer, i as u32, c_str!(""));
                    let value = self.load(context.builder, context.get_value(elem)?)?;
                    LLVMBuildStore(context.builder, value, elem_pointer);
                }
                Ok(())
            }
            MakeVector(ref elems) => {
                use self::vector::VectorExt;
                let output_pointer = context.get_value(output)?;
                let output_type = context.sir_function.symbol_type(output)?;
                let size = self.i64(elems.len() as i64);
                let vector = self.gen_new(context.builder, output_type, size, context.get_run())?;
                for (i, elem) in elems.iter().enumerate() {
                    let index = self.i64(i as i64);
                    let vec_pointer = self.gen_at(context.builder, output_type, vector, index)?;
                    let loaded = self.load(context.builder, context.get_value(elem)?)?;
                    LLVMBuildStore(context.builder, loaded, vec_pointer);
                }
                LLVMBuildStore(context.builder, vector, output_pointer);
                Ok(())
            }
            Merge { .. } => {
                use self::builder::BuilderExpressionGen;
                self.gen_merge(context, statement)
            }
            Negate(_) => {
                use self::numeric::NumericExpressionGen;
                self.gen_negate(context, statement)
            }
            Not(_) => {
                use self::numeric::NumericExpressionGen;
                self.gen_not(context, statement)
            }
            Assert(ref cond) => {
                let output_pointer = context.get_value(output)?;
                let cond = self.load(context.builder, context.get_value(cond)?)?;
                let result = self.intrinsics.call_weld_run_assert(
                    context.builder,
                    context.get_run(),
                    cond,
                    None,
                );
                // If assert returns, this expression returns true.
                LLVMBuildStore(context.builder, result, output_pointer);
                Ok(())
            }
            NewBuilder { .. } => {
                use self::builder::BuilderExpressionGen;
                self.gen_new_builder(context, statement)
            }
            ParallelFor(_) => {
                use self::builder::BuilderExpressionGen;
                self.gen_for(context, statement)
            }
            Res(_) => {
                use self::builder::BuilderExpressionGen;
                self.gen_result(context, statement)
            }
            Select {
                ref cond,
                ref on_true,
                ref on_false,
            } => {
                let output_pointer = context.get_value(output)?;
                let cond = self.load(context.builder, context.get_value(cond)?)?;
                let cond = self.bool_to_i1(context.builder, cond);
                let on_true = self.load(context.builder, context.get_value(on_true)?)?;
                let on_false = self.load(context.builder, context.get_value(on_false)?)?;
                let result = LLVMBuildSelect(context.builder, cond, on_true, on_false, c_str!(""));
                LLVMBuildStore(context.builder, result, output_pointer);
                Ok(())
            }
            Serialize(_) => {
                use self::serde::SerDeGen;
                self.gen_serialize(context, statement)
            }
            Slice {
                ref child,
                ref index,
                ref size,
            } => {
                let output_pointer = context.get_value(output)?;
                let child_value = self.load(context.builder, context.get_value(child)?)?;
                let index_value = self.load(context.builder, context.get_value(index)?)?;
                let size_value = self.load(context.builder, context.get_value(size)?)?;
                let child_type = context.sir_function.symbol_type(child)?;
                if let Vector(ref elem_type) = *child_type {
                    let result = {
                        let methods = self.vectors.get_mut(elem_type).unwrap();
                        methods.gen_slice(context.builder, child_value, index_value, size_value)?
                    };
                    LLVMBuildStore(context.builder, result, output_pointer);
                    Ok(())
                } else {
                    unreachable!()
                }
            }
            Sort {
                ref child,
                ref cmpfunc,
            } => {
                let output_pointer = context.get_value(output)?;
                let output_type = context
                    .sir_function
                    .symbol_type(statement.output.as_ref().unwrap())?;

                if let Vector(ref elem_ty) = *output_type {
                    use self::vector::VectorExt;

                    let child_value = self.load(context.builder, context.get_value(child)?)?;

                    // Sort clones the vector at the moment.
                    let output_value = self.gen_clone(
                        context.builder,
                        output_type,
                        child_value,
                        context.get_run(),
                    )?;

                    let zero = self.zero(self.i64_type());
                    let elems = self.gen_at(context.builder, output_type, output_value, zero)?;
                    let elems_ptr = LLVMBuildBitCast(
                        context.builder,
                        elems,
                        self.void_pointer_type(),
                        c_str!(""),
                    );
                    let size = self.gen_size(context.builder, output_type, output_value)?;
                    let elem_ll_ty = self.llvm_type(elem_ty)?;
                    let ty_size = self.size_of(elem_ll_ty);

                    use self::cmp::GenCmp;
                    let cmpfunc_ll_fn = self.functions[cmpfunc];

                    let run = context.get_run();

                    // Generate the comparator from the provided custom code.
                    let comparator = self.gen_custom_cmp(elem_ll_ty, *cmpfunc, cmpfunc_ll_fn)?;

                    // args to qsort_r are: base array pointer, num elements,
                    // element size, comparator function, run handle.
                    //
                    // MacOS and Linux pass arguments to qsort_r in different order.
                    let (mut args, mut arg_tys) = if cfg!(target_os = "macos") {
                        let args = vec![elems_ptr, size, ty_size, run, comparator];
                        let arg_tys = vec![
                            LLVMTypeOf(elems_ptr),
                            LLVMTypeOf(size),
                            LLVMTypeOf(ty_size),
                            LLVMTypeOf(run),
                            LLVMTypeOf(comparator),
                        ];
                        (args, arg_tys)
                    } else if cfg!(target_os = "linux") {
                        let args = vec![elems_ptr, size, ty_size, comparator, run];
                        let arg_tys = vec![
                            LLVMTypeOf(elems_ptr),
                            LLVMTypeOf(size),
                            LLVMTypeOf(ty_size),
                            LLVMTypeOf(comparator),
                            LLVMTypeOf(run),
                        ];
                        (args, arg_tys)
                    } else {
                        unimplemented!("Sort not available on this platform.");
                    };

                    // Generate the call to qsort.
                    let void_type = self.void_type();
                    self.intrinsics.add("qsort_r", void_type, &mut arg_tys);
                    self.intrinsics
                        .call(context.builder, "qsort_r", &mut args)?;

                    LLVMBuildStore(context.builder, output_value, output_pointer);

                    Ok(())
                } else {
                    unreachable!()
                }
            }
            ToVec(ref child) => {
                let output_pointer = context.get_value(output)?;
                let child_value = self.load(context.builder, context.get_value(child)?)?;
                let child_type = context.sir_function.symbol_type(child)?;
                // This is the type of the resulting key/value vector (vec[{K,V}])
                let output_type = context
                    .sir_function
                    .symbol_type(statement.output.as_ref().unwrap())?;
                let elem = if let Vector(ref elem) = *output_type {
                    elem
                } else {
                    unreachable!()
                };

                let _ = self.llvm_type(output_type)?;
                let result = {
                    let vector_methods = self.vectors.get_mut(elem).unwrap();
                    let methods = self.dictionaries.get_mut(child_type).unwrap();
                    methods.gen_to_vec(
                        context.builder,
                        &mut self.intrinsics,
                        vector_methods,
                        child_value,
                        context.get_run(),
                    )?
                };
                LLVMBuildStore(context.builder, result, output_pointer);
                Ok(())
            }
            UnaryOp { .. } => {
                use self::numeric::NumericExpressionGen;
                self.gen_unaryop(context, statement)
            }
        }
    }
Generate code for a single SIR statement.

The code is generated at the position specified by the function context.
weld/src/codegen/llvm2/mod.rs