private void splitEdge(final Block src, final Block dst) {
		// This shouldn't happen since
		// (1) edges from the source are either source->init, or
		// source->catch. Edges with catch blocks are already split.
		// (2) edges to the sink are always unconditional jumps.
		//
		Assert.isFalse((src == srcBlock) || (dst == snkBlock),
				"Can't split an edge from the source or to the sink");

		// Don't split exception edges
		if (handlers.containsKey(dst)) {
			if (FlowGraph.DEBUG) {
				System.out.println("not removing exception edge " + src
						+ " -> " + dst);
			}

			return;
		}

		final Block newBlock = newBlock();

		// Insert in the trace before the dst.
		trace.add(trace.indexOf(dst), newBlock);

		final Tree tree = new Tree(newBlock, src.tree().stack());
		newBlock.setTree(tree);

		tree.addInstruction(new Instruction(Opcode.opcx_goto, dst.label()));

		if (FlowGraph.DEBUG) {
			System.out.println("add edge " + src + " -> " + newBlock);
			System.out.println("add edge " + newBlock + " -> " + dst);
			System.out.println("remove edge " + src + " -> " + dst);
		}

		src.visit(new ReplaceTarget(dst, newBlock));

		addEdge(src, newBlock);
		addEdge(newBlock, dst);
		removeEdge(src, dst);

		Assert.isTrue(hasEdge(src, newBlock));
		Assert.isTrue(hasEdge(newBlock, dst));
		Assert.isFalse(hasEdge(src, dst));

		// If the dst is a protected block, the new block must be
		// also since code can be moved from the dst up.
		final JumpStmt newJump = (JumpStmt) newBlock.tree().lastStmt();

		final Iterator e = handlers.values().iterator();

		while (e.hasNext()) {
			final Handler handler = (Handler) e.next();

			if (handler.protectedBlocks().contains(dst)) {
				Assert.isTrue(succs(dst).contains(handler.catchBlock()));
				handler.protectedBlocks().add(newBlock);
				addEdge(newBlock, handler.catchBlock());
				newJump.catchTargets().add(handler.catchBlock());
			}
		}
	}