def _unpack_opargs(code):
        """
        Returns a 4-int-tuple of
        (bytecode offset, opcode, argument, offset of next bytecode).
        """
        extended_arg = 0
        n = len(code)
        offset = i = 0
        while i < n:
            op = code[i]
            i += CODE_LEN
            if op >= HAVE_ARGUMENT:
                arg = code[i] | extended_arg
                for j in range(ARG_LEN):
                    arg |= code[i + j] << (8 * j)
                i += ARG_LEN
                if PYVERSION in ((3, 12),):
                    # Python 3.12 introduced cache slots. We need to account for
                    # cache slots when we determine the offset of the next
                    # opcode. The number of cache slots is specific to each
                    # opcode and can be looked up in the _inline_cache_entries
                    # dictionary.
                    i += _inline_cache_entries[op] * INSTR_LEN
                elif PYVERSION in ((3, 10), (3, 11)):
                    pass
                else:
                    raise NotImplementedError(PYVERSION)
                if op == EXTENDED_ARG:
                    # This is a deviation from what dis does...
                    # In python 3.11 it seems like EXTENDED_ARGs appear more
                    # often and are also used as jump targets. So as to not have
                    # to do "book keeping" for where EXTENDED_ARGs have been
                    # "skipped" they are replaced with NOPs so as to provide a
                    # legal jump target and also ensure that the bytecode
                    # offsets are correct.
                    yield (offset, OPCODE_NOP, arg, i)
                    extended_arg = arg << 8 * ARG_LEN
                    offset = i
                    continue
            else:
                arg = None
                i += NO_ARG_LEN
                if PYVERSION in ((3, 12),):
                    # Python 3.12 introduced cache slots. We need to account for
                    # cache slots when we determine the offset of the next
                    # opcode. The number of cache slots is specific to each
                    # opcode and can be looked up in the _inline_cache_entries
                    # dictionary.
                    i += _inline_cache_entries[op] * INSTR_LEN
                elif PYVERSION in ((3, 10), (3, 11)):
                    pass
                else:
                    raise NotImplementedError(PYVERSION)

            extended_arg = 0
            yield (offset, op, arg, i)
            offset = i  # Mark inst offset at first extended

        Returns a 4-int-tuple of
        (bytecode offset, opcode, argument, offset of next bytecode).
        
numba/core/bytecode.py