private void fillPhiFromBlock(JitPhiOp phi, BlockFlow flow, Set<JitBlock> visited) {
		if (!visited.add(block)) {
			/**
			 * NOTE: We do not need to remove the block before we return. If we didn't find it by
			 * this path, we certainly not going to find it from here by another path.
			 */
			return;
		}

		Varnode phiVn = phi.out().varnode();
		List<JitVal> defs = state.getDefinitions(phiVn);
		if (defs.size() != 1) {
			defs = state.generatePhis(defs, dfm.phiQueue);
			JitVal catOpt = arithmetic.catenate(phiVn, defs);
			phi.addOption(flow, catOpt);
			/**
			 * New phi nodes will be picked up in next round of filling. Since parts are smaller
			 * than the whole, the size of such nodes should shrink until a singular definition is
			 * found.
			 */
			return;
		}

		JitVal val = defs.get(0);
		if (val instanceof JitMissingVar missing) {
			// Require the chain to have a node in this block
			JitPhiOp phi2 = missing.generatePhi(dfm, block);
			dfm.phiQueue.add(phi2);
			state.setVar(missing.varnode(), phi2.out());
			phi.addOption(flow, phi2.out());
			// Will get filled on subsequent round
			//fillPhiFromDeps(phi2, visited);
			return;
		}

		phi.addOption(flow, val);
	}
Consider the given flow as an option for the given phi op, and fill it

<p>
If we've already visited the given block, we return immediately, without further recursion.
Otherwise, we examine the varnode output state of this block for suitable definitions. If
needed, we fill any gaps (possibly the entire varnode sought) with new phi nodes and recurse.

@param phi the phi op for which we seek an option
@param flow the flow from the block to consider
@param visited the blocks which have already been visited during recursion
Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/analysis/JitDataFlowBlockAnalyzer.java