unsafe fn gen_merge_grouped(
        &mut self,
        builder: LLVMBuilderRef,
        intrinsics: &mut Intrinsics,
        group_vector: &mut Vector,
        dict: LLVMValueRef,
        key: LLVMValueRef,
        hash: LLVMValueRef,
        value: LLVMValueRef,
        run: LLVMValueRef,
    ) -> WeldResult<LLVMValueRef> {
        if self.merge_grouped.is_none() {
            let mut arg_tys = [
                self.dict_ty,
                LLVMPointerType(self.slot_ty.key_ty, 0),
                self.hash_type(),
                group_vector.elem_ty,
                self.run_handle_type(),
            ];
            let ret_ty = self.void_type();

            let name = format!("{}.merge_grouped", self.name);
            let (function, builder, _) = self.define_function(ret_ty, &mut arg_tys, name);

            let dict = LLVMGetParam(function, 0);
            let key = LLVMGetParam(function, 1);
            let hash = LLVMGetParam(function, 2);
            let value = LLVMGetParam(function, 3);
            let run = LLVMGetParam(function, 4);

            LLVMExtAddAttrsOnParameter(self.context, function, &[NoAlias], 0);
            LLVMExtAddAttrsOnParameter(self.context, function, &[NoAlias], 1);
            LLVMExtAddAttrsOnParameter(self.context, function, &[NoAlias], 4);

            let init_block = LLVMAppendBasicBlockInContext(self.context, function, c_str!("init"));
            let checkcap_block =
                LLVMAppendBasicBlockInContext(self.context, function, c_str!("checkcapacity"));
            let resize_block =
                LLVMAppendBasicBlockInContext(self.context, function, c_str!("resize"));
            let merge_block =
                LLVMAppendBasicBlockInContext(self.context, function, c_str!("merge"));

            let mut zero_vector = LLVMGetUndef(group_vector.vector_ty);
            zero_vector = LLVMConstInsertValue(
                zero_vector,
                self.null_ptr(group_vector.elem_ty),
                [vector::POINTER_INDEX].as_mut_ptr(),
                1,
            );
            zero_vector = LLVMConstInsertValue(
                zero_vector,
                self.i64(0),
                [vector::SIZE_INDEX].as_mut_ptr(),
                1,
            );

            // Upsert the zero vector. This will create a slot if one does not exist already. We
            // can then initialize the grouping vector if necessary, and append the new value to
            // the grouping vector.
            //
            // Generated Code:
            //
            //  slot = upsert(dict, key, hash, zeroinitializer, run)
            //  filled = slot.filled
            //  if filled == 1 ? init : checkcapacity
            // init:
            //  vec = vector.new()
            //  store vec into slot.value
            //  filled = log2(DEFAULT_CAPACITY)
            //  br merge
            //
            // checkcapacity:
            //  capacity = (1 << filled)
            //  vector = slot.value
            //  if capacity <= vector.size ? resize : merge
            //
            // resize:
            //  vector.extend
            //  filled ++
            //  br merge
            //
            // merge:
            //  size = vector.size
            //  ptr = vector.at(size)
            //  store value at ptr

            let slot = self.gen_upsert(builder, intrinsics, dict, key, hash, zero_vector, run)?;

            let value_pointer = self.slot_ty.value(builder, slot);

            // Array's size pointer. This points into the slot's value.
            let size_pointer = LLVMBuildStructGEP(
                builder,
                value_pointer,
                vector::SIZE_INDEX,
                c_str!("sizePtr"),
            );
            let filled_value = self.slot_ty.filled_value(builder, slot);

            // If capacity == 1, the slot was just initialized via the upsert. We need to
            // initialize the vector.
            let was_initialized = LLVMBuildICmp(
                builder,
                LLVMIntEQ,
                filled_value,
                self.i8(1),
                c_str!("initialized"),
            );
            LLVMBuildCondBr(builder, was_initialized, init_block, checkcap_block);

            // Initalize the vector with the default capacity. The vector capacity must be a
            // power-of-2 so we can represent it using the filled byte.
            LLVMPositionBuilderAtEnd(builder, init_block);
            let new_vector =
                group_vector.gen_new(builder, intrinsics, run, self.i64(DEFAULT_GROUP_CAPACITY))?;
            LLVMBuildStore(builder, new_vector, value_pointer);

            // We must set the size to 0, since we manually track the size within the vector. The
            // capacity is tracked via the filled byte.
            LLVMBuildStore(builder, self.i64(0), size_pointer);

            // Store the capacity as a power-of-2.
            let default_filled = self.i8(DEFAULT_GROUP_FILLED);
            self.slot_ty.set_filled(builder, slot, default_filled);
            LLVMBuildBr(builder, merge_block);

            // If the vector was already there, make sure we can fit the new element.
            LLVMPositionBuilderAtEnd(builder, checkcap_block);
            let ext_filled_value =
                LLVMBuildZExt(builder, filled_value, self.u64_type(), c_str!(""));
            let capacity = LLVMBuildShl(builder, self.i64(1), ext_filled_value, c_str!("capacity"));
            let cur_vector = self.load(builder, value_pointer).unwrap();
            let size = group_vector.gen_size(builder, cur_vector)?;
            let needs_resize =
                LLVMBuildICmp(builder, LLVMIntEQ, size, capacity, c_str!("shouldResize"));
            LLVMBuildCondBr(builder, needs_resize, resize_block, merge_block);

            // Extend the vector and update the slot.
            LLVMPositionBuilderAtEnd(builder, resize_block);
            let new_capacity =
                LLVMBuildNUWMul(builder, capacity, self.i64(2), c_str!("newCapacity"));
            let resized_vector =
                group_vector.gen_extend(builder, intrinsics, run, cur_vector, new_capacity)?;

            // Since extend sets the size, we need to "reset" it back to the previous size.
            LLVMBuildStore(builder, resized_vector, value_pointer);
            LLVMBuildStore(builder, size, size_pointer);

            // Set the new capacity: since we double the size of the vector, we just incremented
            // the filled value by 1.
            let new_filled_value =
                LLVMBuildAdd(builder, filled_value, self.i8(1), c_str!("newFilled"));
            self.slot_ty.set_filled(builder, slot, new_filled_value);
            LLVMBuildBr(builder, merge_block);

            // Merge the value. The capacity of the vector is guaranteed to accomdate the merge
            // value now.
            LLVMPositionBuilderAtEnd(builder, merge_block);
            let array_pointer = LLVMBuildStructGEP(
                builder,
                value_pointer,
                vector::POINTER_INDEX,
                c_str!("arrayPtr"),
            );
            let array_pointer = self.load(builder, array_pointer).unwrap();

            // Merge the value.
            let offset = self.load(builder, size_pointer).unwrap();
            let merge_pointer = LLVMBuildGEP(
                builder,
                array_pointer,
                [offset].as_mut_ptr(),
                1,
                c_str!("mergePtr"),
            );
            LLVMBuildStore(builder, value, merge_pointer);

            // Update the size.
            let inc_size = LLVMBuildNSWAdd(builder, offset, self.i64(1), c_str!("newSize"));
            LLVMBuildStore(builder, inc_size, size_pointer);
            LLVMBuildRetVoid(builder);

            self.merge_grouped = Some(function);
            LLVMDisposeBuilder(builder);
        }

        let mut args = [dict, key, hash, value, run];
        Ok(LLVMBuildCall(
            builder,
            self.merge_grouped.unwrap(),
            args.as_mut_ptr(),
            args.len() as u32,
            c_str!(""),
        ))
    }
Merge `value` into the group for `key` with the given `hash`.

This method takes a `Vector`, which holds methods for the type `vec[V]`.
weld/src/codegen/llvm2/dict.rs