public void mergeCodeUnits(AddressSetView originAddressSet, AddressSetView byteDiffs,
			boolean mergeDataBytes, TaskMonitor monitor)
			throws MemoryAccessException, CancelledException, UnsupportedOperationException {

		if (!originToResultTranslator.isOneForOneTranslator()) {
			String message = originToResultTranslator.getClass().getName() +
				" is not a one for one translator and can't merge code units.";
			throw new UnsupportedOperationException(message);
		}
		monitor.setMessage("Applying Code Units...");
		if (originAddressSet.isEmpty()) {
			return;
		}

		ProgramContext originContext = originProgram.getProgramContext();
		ProgramContext resultContext = resultProgram.getProgramContext();
		Register originContextReg = originContext.getBaseContextRegister();
		Register resultContextReg = resultContext.getBaseContextRegister();

		// Expand ranges to keep delay-slots together within origin range
		// May need to expand each range up or down
		originAddressSet = SimpleDiffUtility.expandAddressSetToIncludeFullDelaySlots(originProgram,
			originAddressSet);

		// Don't clear instructions which match (same instruction prototype) so that references
		// are preserved.
		for (AddressRange originRange : originAddressSet.getAddressRanges()) {
			// Get a new address set for this range that does not include any addresses where
			// the origin and result instruction prototypes are the same.
			// The range address set will be used to clear code units.
			AddressSet rangeSet = new AddressSet(originRange);
			InstructionIterator instructions =
				originListing.getInstructions(new AddressSet(originRange), true);
			for (Instruction instruction : instructions) {
				Address resultAddress =
					originToResultTranslator.getAddress(instruction.getMinAddress());
				Instruction resultInstruction = resultListing.getInstructionAt(resultAddress);
				if (!shouldClearInstruction(instruction, resultInstruction)) {
					// Remove any instructions with matching prototypes, so they won't be cleared.
					rangeSet.delete(instruction.getMinAddress(), instruction.getMaxAddress());
				}
			}
			// Clear the code units still in our range set.
			for (AddressRange newOriginRange : rangeSet.getAddressRanges()) {
				AddressRange resultRange = originToResultTranslator.getAddressRange(newOriginRange);

				// Clear any existing code units in the merged program
				// where this code unit needs to go.
				resultListing.clearCodeUnits(resultRange.getMinAddress(),
					resultRange.getMaxAddress(), false);

				try {
					if (resultContextReg != Register.NO_CONTEXT) {
						if (originContextReg != Register.NO_CONTEXT) {
							// Copy context register value
							mergeProgramContext(resultContext, originContext,
								originContext.getBaseContextRegister(), newOriginRange, resultRange,
								monitor);
						}
						else {
							// Clear context register value if it did not exist in original
							resultContext.remove(resultRange.getMinAddress(),
								resultRange.getMaxAddress(), resultContextReg);
						}
					}
				}
				catch (ContextChangeException e) {
					Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
				}
			}
		}

		CodeUnitIterator originSourceCodeUnits = originListing.getCodeUnits(originAddressSet, true);

		// Get each code unit out of the iterator and set it in the merged
		// program if it is an instruction.
		for (long count = 0; originSourceCodeUnits.hasNext() && !monitor.isCancelled(); count++) {
			monitor.checkCancelled();
			CodeUnit originCodeUnit = originSourceCodeUnits.next();
			if (count == PROGRESS_COUNTER_GRANULARITY) {
				monitor.setMessage(
					"Applying Code Units...   " + originCodeUnit.getAddressString(true, false));
				count = 0;
			}

			// Note : performMergeInstruction() and performMergeData()
			//        clear the existing code units and then create a
			//        new instruction or data.
			//        May cause the merge code unit to lose info attached
			//        to it.
			if (originCodeUnit instanceof Instruction) {
				Instruction originInstruction = (Instruction) originCodeUnit;
				Address resultAddress =
					originToResultTranslator.getAddress(originInstruction.getMinAddress());
				Instruction resultInstruction = resultListing.getInstructionAt(resultAddress);
				if (resultInstruction == null ||
					!originInstruction.getPrototype().equals(resultInstruction.getPrototype())) {
					performMergeInstruction(originInstruction, byteDiffs);
				}
				else {
					copyInstructionAttributes(originInstruction, resultInstruction);
				}
			}
			else if (originCodeUnit instanceof Data) {
				try {
					performMergeData((Data) originCodeUnit, byteDiffs, mergeDataBytes);
				}
				catch (CodeUnitInsertionException e) {
					infoMsg.append("Diff/Merge can't apply data from " +
						originCodeUnit.getMinAddress() + ". " + e.getMessage());
				}
			}
		}
	}
<CODE>mergeCodeUnits</CODE> merges all instructions and/or data
(as indicated) in the specified address set from the origin program.
It merges them into the result program. When merging
instructions, the bytes are also replaced if they differ.
This assumes originToResultTranslator maps address spaces and does
not do fine-grained mapping of addresses.
@param originAddressSet the addresses to be merged.
The addresses in this set should be derived from the origin program.
@param byteDiffs address set indicating addresses where the bytes differ
between the result program and the origin program.
The addresses in this set should be derived from the origin program.
@param mergeDataBytes true indicates bytes that differ should be copied when merging Data.
false means don't copy any bytes for Data.
@param monitor the task monitor for notifying the user of this merge's
progress.

@throws MemoryAccessException if bytes can't be copied.
@throws CancelledException if user cancels via the monitor.
@throws UnsupportedOperationException if the ProgramMerge translator is not a
"one for one translator".
Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMerge.java