pub unsafe fn gen_lookup(
        &mut self,
        builder: LLVMBuilderRef,
        intrinsics: &mut Intrinsics,
        dict: LLVMValueRef,
        key: LLVMValueRef,
        hash: LLVMValueRef,
        run: LLVMValueRef,
    ) -> WeldResult<LLVMValueRef> {
        use crate::runtime::WeldRuntimeErrno::KeyNotFoundError;

        if self.lookup.is_none() {
            let mut arg_tys = [
                self.dict_ty,
                LLVMPointerType(self.slot_ty.key_ty, 0),
                self.hash_type(),
                self.run_handle_type(),
            ];
            let ret_ty = LLVMPointerType(self.slot_ty.slot_ty, 0);

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

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

            let crash_block =
                LLVMAppendBasicBlockInContext(self.context(), function, c_str!("keynotfound"));
            let return_block =
                LLVMAppendBasicBlockInContext(self.context(), function, c_str!("return"));

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

            let slot_array = self.slot_array(builder, dict);
            let capacity = self.capacity(builder, dict);
            let slot = self.gen_slot_for_key(builder, slot_array, capacity, hash, key);

            let filled = self.slot_ty.filled(builder, slot);
            LLVMBuildCondBr(builder, filled, return_block, crash_block);

            // Crash if the key is not found.
            LLVMPositionBuilderAtEnd(builder, crash_block);
            let error = self.i64(KeyNotFoundError as i64);
            intrinsics.call_weld_run_set_errno(builder, run, error, None);
            LLVMBuildUnreachable(builder);

            LLVMPositionBuilderAtEnd(builder, return_block);
            LLVMBuildRet(builder, slot);

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

        let mut args = [dict, key, hash, run];
        Ok(LLVMBuildCall(
            builder,
            self.lookup.unwrap(),
            args.as_mut_ptr(),
            args.len() as u32,
            c_str!(""),
        ))
    }
Returns the pointer to the slot for a key.

If the key does not exist, throws an KeyNotFoundError.

It is *invalid* to change a filled slot to be unfilled (i.e., setting a non-zero filled
value to 0).
weld/src/codegen/llvm2/dict.rs