示例#1
0
def divide(c, m):
    """Division statement. For instance:
        al /= 4
    """
    dst = Operand(c, m.group(1))
    src = Operand(c, m.group(2))


    if src.value == 0:
        raise ValueError('Cannot divide by 0')


    if src.value and abs(src.value) == 1:
        # One optimization: do nothing or negate
        if src.value < 0:
            c.add_code(f'neg {dst}')
        return


    if src.value is not None:
        # Bit shift optimization: divisor is a small multiple of 2
        # http://zsmith.co/intel_d.html#div or
        # http://zsmith.co/intel_n.html#neg and http://zsmith.co/intel_s.html#sar
        if dst.is_reg or dst.size == 8:
            limit = 80
        else:
            limit = 150

        if dst.is_reg:
            if src.value < 0:
                cost = 3 + 8 + 4 * (-src.value)
            else:
                cost = 8 + 4 * src.value

        if dst.is_mem:
            if src.value < 0:
                cost = 16 + 20 + 4 * (-src.value)
            else:
                cost = 20 + 4 * src.value

        # Now we know our limit, beyond which division is easier
        # and the cost per shift, so we know which values we can use
        if cost < limit:
            # The cost of negating and shifting is less, so we'll do that
            amount = limit // cost
            powers = [2**i for i in range(1, amount + 1)]
            if abs(src.value) in powers:
                # We have a power of two, so perform we can use bit shift
                if src.value < 0:
                    c.add_code(f'neg {dst}')
                    src.value = -src.value

                count = int(src.value ** 0.5)
                c.add_code(f'sar {dst}, {count}')
                return

    perform_divide(c, dst, src,
                   result_in8='al',
                   result_in16='ax')
示例#2
0
def multiply(c, m):
    """Multiplication statement. For instance:
        al *= 7
    """
    dst = Operand(c, m.group(1))
    src = Operand(c, m.group(2))

    if src.value == 0:
        # Zero optimization: second product is 0
        if dst.is_mem:
            c.add_code(f'and {dst}, 0')
        else:
            c.add_code(f'xor {dst}, {dst}')
        return

    if src.value and abs(src.value) == 1:
        # One optimization: do nothing or negate
        if src.value < 0:
            c.add_code(f'neg {dst}')
        return

    if src.value is not None:
        # Bit shift optimization: second product is a small multiple of 2
        # http://zsmith.co/intel_m.html#mul or
        # http://zsmith.co/intel_n.html#neg and http://zsmith.co/intel_s.html#sal
        if dst.is_reg or dst.size == 8:
            limit = 70
        else:
            limit = 124

        if dst.is_reg:
            if src.value < 0:
                cost = 3 + 8 + 4 * (-src.value)
            else:
                cost = 8 + 4 * src.value

        if dst.is_mem:
            if src.value < 0:
                cost = 16 + 20 + 4 * (-src.value)
            else:
                cost = 20 + 4 * src.value

        # Now we know our limit, beyond which multiplication is easier
        # and the cost per shift, so we know which values we can use
        if cost < limit:
            # The cost of negating and shifting is less, so we'll do that
            amount = limit // cost
            powers = [2**i for i in range(1, amount + 1)]
            if abs(src.value) in powers:
                # We have a power of two, so perform we can use bit shift
                if src.value < 0:
                    c.add_code(f'neg {dst}')
                    src.value = -src.value

                count = int(src.value**0.5)
                c.add_code(f'sal {dst}, {count}')
                return

    # We're likely going to need to save some temporary variables
    # No 'push' or 'pop' are used because 'ah *= 3', for instance, destroys 'al'
    # 'al' itself can be pushed to the stack, but a temporary variable can hold
    tmps = TmpVariables(c)

    # We will define 'dst, src, fa1, fa2' as:
    #   dst *= src
    #   fa1 *= fa2

    large_multiply = max(dst.size, src.size) > 8

    if large_multiply:
        # 16-bits mode, so we use 'ax' as the first factor
        fa1 = 'ax'

        # The second factor cannot be an inmediate value neither 'ax'
        if src.code[0] == 'a' and src.code[-1] in 'xhl' \
                or src.value is not None:
            fa2 = 'dx'
        else:
            fa2 = src.code

        # Determine which registers we need to save
        saves = []

        # al    *= 260
        # ^ dst
        #
        # ax    *= 260
        # ^ used
        #
        # dst ≠ used, used gets saved

        # Special case where we want to use al/ah as destination
        # We're using ax for multiplication, so we can't save the whole
        if dst[0] == 'a' and dst[-1] in 'hl':
            saves.append('al' if dst.code == 'ah' else 'ah')
        elif dst.code != fa1:
            # Save fa1 unless it's the destination
            saves.append(fa1)

        # Save fa2 unless it's the destination
        if dst.code != fa2:
            saves.append(fa2)

        # Save the used registers
        tmps.save_all(saves)

        # Load the factors into their correct location
        helperassign(c, [fa1, fa2], [dst, src])

        # Perform the multiplication
        c.add_code(f'mul {fa2}')

        # Move the result, 'ax', to wherever it should be
        helperassign(c, dst, 'ax')

        # Restore the used registers
        tmps.restore_all()

    else:
        # 8-bits mode, so we use 'al' as the first factor
        fa1 = 'al'

        # The second factor cannot be an inmediate value neither 'al'
        fa2 = src.code if src.value is None and src.code != 'al' else 'ah'

        # Determine which registers we need to save
        saves = []

        # Save fa1 unless it's the destination
        if dst.code != fa1:
            saves.append(fa1)

        # Save fa2 if we moved it (thus it's not the same)
        # unless it's be overrode if it is the destination
        if src.code != fa2 and dst.code != fa2:
            saves.append(fa2)

        # Save the used registers
        tmps.save_all(saves)

        # Load the factors into their correct location
        helperassign(c, [fa1, fa2], [dst, src])

        # Perform the multiplication
        c.add_code(f'mul {fa2}')

        # Move the result, 'al', to wherever it should be
        helperassign(c, dst, 'al')

        # Restore the used registers
        tmps.restore_all()