unsafe fn hash(
        &mut self,
        function: LLVMValueRef,
        builder: LLVMBuilderRef,
        ty: &Type,
        funcs: &HashFuncs,
        seed: LLVMValueRef,
        param: LLVMValueRef,
    ) -> WeldResult<LLVMValueRef> {
        let hash = match *ty {
            Scalar(kind) => {
                let loaded = self.load(builder, param)?;
                self.hash_scalar(builder, kind, funcs, seed, loaded)
            }
            Simd(_) => unimplemented!(),
            /*  // NOTE: For now, since we use padded structs, these optimizations are not safe.
            Struct(_) if !ty.has_pointer() => {
                // The static size of the value in bits. We decompose this into a series of
                // calls to `hash_scalar`, calling the widest possible version.
                let llvm_ty = self.llvm_type(ty)?;
                let size = self.size_of_bits(llvm_ty);
                let mut remaining = size;
                let mut hash = seed;

                let bytebuf = LLVMBuildBitCast(builder, param, LLVMPointerType(self.i8_type(), 0), c_str!(""));

                while remaining > 0 {
                    // Compute the base pointer to load next.
                    let mut offset = [self.i32(((size - remaining) / 8) as i32)];
                    let pointer = LLVMBuildInBoundsGEP(builder,
                                                       bytebuf,
                                                       offset.as_mut_ptr(),
                                                       offset.len() as u32,
                                                       c_str!(""));
                    hash = if remaining >= 64 {
                        let pointer = LLVMBuildBitCast(builder, pointer, LLVMPointerType(self.i64_type(), 0), c_str!(""));
                        let loaded = self.load(builder, pointer)?;
                        remaining -= 64;
                        self.hash_scalar(builder, I64, funcs, hash, loaded)
                    } else if remaining >= 32 {
                        let pointer = LLVMBuildBitCast(builder, pointer, LLVMPointerType(self.i32_type(), 0), c_str!(""));
                        let loaded = self.load(builder, pointer)?;
                        remaining -= 32;
                        self.hash_scalar(builder, I32, funcs, hash, loaded)
                    } else if remaining >= 16 {
                        let pointer = LLVMBuildBitCast(builder, pointer, LLVMPointerType(self.i16_type(), 0), c_str!(""));
                        let loaded = self.load(builder, pointer)?;
                        remaining -= 16;
                        self.hash_scalar(builder, I16, funcs, hash, loaded)
                    } else if remaining == 8 {
                        // Already an i8*
                        let loaded = self.load(builder, pointer)?;
                        remaining -= 8;
                        self.hash_scalar(builder, I8, funcs, hash, loaded)
                    } else {
                        unreachable!()
                    };
                }
                assert_eq!(remaining, 0);
                hash
            }
            */
            Struct(ref elems) => {
                // We don't want to hash pointers, so just hash each struct element
                // individually.
                let mut hash = seed;
                for (i, ty) in elems.iter().enumerate() {
                    let pointer = LLVMBuildStructGEP(builder, param, i as u32, c_str!(""));
                    hash = self.gen_hash(ty, builder, pointer, Some(hash))?;
                }
                hash
            }
            // NOTE: Ideally, we would play this trick for any type without pointers. Padding gets
            // in our way for now, however.
            Vector(ref elem) if elem.is_scalar() => {
                // Vectors are hashed in a manner similar to structs: we use the widest available
                // hash function in a loop until all bytes are hashed. The generated code looks
                // like this (where offsets are in *bits*):
                //
                // while offset > len(vec) - 64:
                //      hash(vec[offset..offset+64])
                //      offset += 64
                // while offset > len(vec) - 32:
                //      hash(vec[offset..offset+32])
                //      offset += 32
                // while offset > len(vec) - 16:
                //      hash(vec[offset..offset+16])
                //      offset += 16
                // while offset > len(vec) - 8:
                //      offset += 8
                let elem_llvm_ty = self.llvm_type(elem)?;
                let vector = self.load(builder, param)?;
                let size = self.gen_size(builder, ty, vector)?;

                let zero = self.i64(0);
                let base_pointer = self.gen_at(builder, ty, vector, zero)?;

                // The loops we will generate. If the element type is a multiple of one of the
                // widths, we do not need to generate loops for widths smaller than the multiple.
                let mut kinds = vec![I64];
                let elem_size_bits = self.size_of_bits(elem_llvm_ty);
                if elem_size_bits % 64 != 0 {
                    kinds.push(I32);
                }
                if elem_size_bits % 32 != 0 {
                    kinds.push(I16);
                }
                if elem_size_bits % 16 != 0 {
                    kinds.push(I8);
                }

                // Total number of bytes remaining in the vector (size * sizeof(elem))
                let mut length_in_bytes =
                    LLVMBuildNUWMul(builder, size, self.size_of(elem_llvm_ty), c_str!(""));
                // The base pointer in bytes.
                let mut base_pointer = LLVMBuildBitCast(
                    builder,
                    base_pointer,
                    LLVMPointerType(self.i8_type(), 0),
                    c_str!(""),
                );
                // Holds the updated hash value
                let mut hash = seed;
                // Holds the number of bytes consumed so far (equivalently, the offset from the
                // base pointer)
                let mut consumed;

                // Generate each loop in turn.
                for kind in kinds.into_iter() {
                    // Generates the actual loop: hash holds the new hash value,
                    let (new_hash, new_consumed) = self.hash_loop(
                        function,
                        builder,
                        kind,
                        funcs,
                        length_in_bytes,
                        base_pointer,
                        hash,
                    )?;

                    hash = new_hash;
                    consumed = new_consumed;

                    // Update the base pointer to point to the first un-consumed byte.
                    let mut indices = [consumed];
                    base_pointer = LLVMBuildInBoundsGEP(
                        builder,
                        base_pointer,
                        indices.as_mut_ptr(),
                        indices.len() as u32,
                        c_str!(""),
                    );

                    // Update the remaining bytes to (PreviousRemainingBytes - BytesConsumed)
                    length_in_bytes =
                        LLVMBuildNUWSub(builder, length_in_bytes, consumed, c_str!(""));
                }
                hash
            }
            Vector(ref elem) => {
                // Since each element has a pointer, we loop over the vector and call each
                // element's hash function.
                let start_block =
                    LLVMAppendBasicBlockInContext(self.context(), function, c_str!(""));
                let loop_block =
                    LLVMAppendBasicBlockInContext(self.context(), function, c_str!(""));
                let end_block = LLVMAppendBasicBlockInContext(self.context(), function, c_str!(""));

                LLVMBuildBr(builder, start_block);
                LLVMPositionBuilderAtEnd(builder, start_block);

                let vector = self.load(builder, param)?;
                let size = self.gen_size(builder, ty, vector)?;

                // Build a loop that hashes each vector element.
                let check = LLVMBuildICmp(builder, LLVMIntNE, size, self.i64(0), c_str!(""));
                LLVMBuildCondBr(builder, check, loop_block, end_block);
                LLVMPositionBuilderAtEnd(builder, loop_block);

                // Induction variable.
                let phi_i = LLVMBuildPhi(builder, self.i64_type(), c_str!(""));
                // Hash value so far.
                let phi_hash = LLVMBuildPhi(builder, LLVMTypeOf(seed), c_str!(""));

                let pointer = self.gen_at(builder, ty, vector, phi_i)?;
                let updated_hash = self.gen_hash(elem, builder, pointer, Some(phi_hash))?;

                let updated_i = LLVMBuildNSWAdd(builder, phi_i, self.i64(1), c_str!(""));
                let check2 = LLVMBuildICmp(builder, LLVMIntEQ, updated_i, size, c_str!(""));
                LLVMBuildCondBr(builder, check2, end_block, loop_block);

                let mut blocks = [start_block, loop_block];
                let mut values = [self.i64(0), updated_i];
                LLVMAddIncoming(
                    phi_i,
                    values.as_mut_ptr(),
                    blocks.as_mut_ptr(),
                    values.len() as u32,
                );

                let mut values = [seed, updated_hash];
                LLVMAddIncoming(
                    phi_hash,
                    values.as_mut_ptr(),
                    blocks.as_mut_ptr(),
                    values.len() as u32,
                );

                // Finish block - set result and compute number of consumed bits.
                LLVMPositionBuilderAtEnd(builder, end_block);

                let result = LLVMBuildPhi(builder, LLVMTypeOf(seed), c_str!(""));
                let mut blocks = [start_block, loop_block];
                let mut values = [seed, updated_hash];
                LLVMAddIncoming(
                    result,
                    values.as_mut_ptr(),
                    blocks.as_mut_ptr(),
                    values.len() as u32,
                );
                result
            }
            Dict(_, _) | Builder(_, _) | Function(_, _) | Unknown | Alias(_, _) => {
                return compile_err!("Unhashable type {}", ty);
            }
        };
        Ok(hash)
    }
Hash a typed value given some hash functions.
weld/src/codegen/llvm2/hash.rs