@Override
	public JitType generateBinOpRunCode(JitCodeGenerator gen, JitIntSubOp op, JitBlock block,
			JitType lType, JitType rType, MethodVisitor rv) {
		rType = TypeConversions.forceUniformZExt(rType, lType, rv);
		switch (rType) {
			case IntJitType t -> rv.visitInsn(ISUB);
			case LongJitType t -> rv.visitInsn(LSUB);
			case MpIntJitType t when t.size() == lType.size() -> generateMpIntSub(gen, t, rv);
			case MpIntJitType t -> TODO("MpInt of differing sizes");
			default -> throw new AssertionError();
		}
		return lType;
	}
The strategy is to allocate a temp local for each leg of the result. First, we'll pop the
right operand into the temp. Then, as we work with each leg of the left operand, we'll
execute the algorithm. Convert both right and left legs to a long and add them (along
with a possible carry in). Store the result back into the temp locals. Shift the leg
right 32 to get the carry out, then continue to the next leg up. The final carry out can
be dropped (overflow). The result legs are then pushed back to the stack.
Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/gen/op/IntSubOpGen.java