[pypy-svn] r37331 - pypy/dist/pypy/jit/codegen/i386
arigo at codespeak.net
arigo at codespeak.net
Thu Jan 25 15:11:43 CET 2007
Author: arigo
Date: Thu Jan 25 15:11:41 2007
New Revision: 37331
Added:
pypy/dist/pypy/jit/codegen/i386/operation.py (contents, props changed)
pypy/dist/pypy/jit/codegen/i386/regalloc.py (contents, props changed)
Modified:
pypy/dist/pypy/jit/codegen/i386/rgenop.py
Log:
Split rgenop in three pieces. For now they are linked with "import *",
but that could be eventually fixed.
Added: pypy/dist/pypy/jit/codegen/i386/operation.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/jit/codegen/i386/operation.py Thu Jan 25 15:11:41 2007
@@ -0,0 +1,857 @@
+"""Operations.
+
+In the spirit of LLVM, operations are also variables themselves: they
+are their own result variable.
+"""
+
+import sys
+from pypy.rlib.objectmodel import specialize
+from pypy.objspace.std.multimethod import FailedToImplement
+from pypy.jit.codegen.i386.ri386 import *
+from pypy.jit.codegen.i386.ri386setup import Conditions
+from pypy.jit.codegen.model import GenVar
+
+
+WORD = 4 # bytes
+if sys.platform == 'darwin':
+ CALL_ALIGN = 4
+else:
+ CALL_ALIGN = 1
+
+PROLOGUE_FIXED_WORDS = 5
+
+RK_NO_RESULT = 0
+RK_WORD = 1
+RK_CC = 2
+
+
+class Operation(GenVar):
+ clobbers_cc = True
+ result_kind = RK_WORD
+ cc_result = -1
+
+ def allocate(self, allocator):
+ pass
+ def generate(self, allocator):
+ raise NotImplementedError
+
+class Op1(Operation):
+ def __init__(self, x):
+ self.x = x
+ def allocate(self, allocator):
+ allocator.using(self.x)
+ def generate(self, allocator):
+ try:
+ dstop = allocator.get_operand(self)
+ except KeyError:
+ return # result not used
+ srcop = allocator.get_operand(self.x)
+ return self.generate2(allocator.mc, dstop, srcop)
+ def generate2(self, mc, dstop, srcop):
+ raise NotImplementedError
+
+class UnaryOp(Op1):
+ def generate(self, allocator):
+ try:
+ loc = allocator.var2loc[self]
+ except KeyError:
+ return # simple operation whose result is not used anyway
+ op = allocator.load_location_with(loc, self.x)
+ self.emit(allocator.mc, op)
+
+class OpIntNeg(UnaryOp):
+ opname = 'int_neg'
+ emit = staticmethod(I386CodeBuilder.NEG)
+
+class OpIntInvert(UnaryOp):
+ opname = 'int_invert', 'uint_invert'
+ emit = staticmethod(I386CodeBuilder.NOT)
+
+class OpIntAbs(Op1):
+ opname = 'int_abs'
+ def generate2(self, mc, dstop, srcop):
+ # ABS-computing code from Psyco, found by exhaustive search
+ # on *all* short sequences of operations :-)
+ inplace = (dstop == srcop)
+ if inplace or not (isinstance(srcop, REG) or isinstance(dstop, REG)):
+ mc.MOV(ecx, srcop)
+ srcop = ecx
+ if not inplace:
+ mc.MOV(dstop, srcop)
+ mc.SHL(dstop, imm8(1))
+ mc.SBB(dstop, srcop)
+ mc.SBB(ecx, ecx)
+ mc.XOR(dstop, ecx)
+
+class OpSameAs(Op1):
+ clobbers_cc = False
+ def generate2(self, mc, dstop, srcop):
+ if srcop != dstop:
+ try:
+ mc.MOV(dstop, srcop)
+ except FailedToImplement:
+ mc.MOV(ecx, srcop)
+ mc.MOV(dstop, ecx)
+
+class OpCompare1(Op1):
+ result_kind = RK_CC
+ def generate(self, allocator):
+ srcop = allocator.get_operand(self.x)
+ mc = allocator.mc
+ self.emit(mc, srcop)
+
+class OpIntIsTrue(OpCompare1):
+ opname = 'int_is_true', 'ptr_nonzero', 'uint_is_true'
+ cc_result = Conditions['NE']
+ @staticmethod
+ def emit(mc, x):
+ mc.CMP(x, imm8(0))
+
+class OpIntIsZero(OpIntIsTrue):
+ opname = 'ptr_iszero', 'bool_not'
+ cc_result = Conditions['E']
+
+class Op2(Operation):
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+ def allocate(self, allocator):
+ allocator.using(self.x)
+ allocator.using(self.y)
+ def generate(self, allocator):
+ try:
+ dstop = allocator.get_operand(self)
+ except KeyError:
+ return # simple operation whose result is not used anyway
+ op1 = allocator.get_operand(self.x)
+ op2 = allocator.get_operand(self.y)
+ self.generate3(allocator.mc, dstop, op1, op2)
+ def generate3(self, mc, dstop, op1, op2):
+ raise NotImplementedError
+
+class BinaryOp(Op2):
+ commutative = False
+ def generate3(self, mc, dstop, op1, op2):
+ # now all of dstop, op1 and op2 may alias each other and be in
+ # a register, in the stack or an immediate... finding a correct
+ # and encodable combination of instructions is loads of fun
+ if dstop == op1:
+ case = 1 # optimize for this common case
+ elif self.commutative and dstop == op2:
+ op1, op2 = op2, op1
+ case = 1
+ elif isinstance(dstop, REG):
+ if dstop != op2:
+ # REG = OPERATION(op1, op2) with op2 != REG
+ case = 2
+ else:
+ # REG = OPERATION(op1, REG)
+ case = 3
+ elif isinstance(op1, REG) and isinstance(op2, REG):
+ # STACK = OPERATION(REG, REG)
+ case = 2
+ else:
+ case = 3
+ # generate instructions according to the 'case' determined above
+ if case == 1:
+ # dstop == op1
+ try:
+ self.emit(mc, op1, op2)
+ except FailedToImplement: # emit(STACK, STACK) combination
+ mc.MOV(ecx, op2)
+ self.emit(mc, op1, ecx)
+ elif case == 2:
+ # this case works for:
+ # * REG = OPERATION(op1, op2) with op2 != REG
+ # * STACK = OPERATION(REG, REG)
+ mc.MOV(dstop, op1)
+ self.emit(mc, dstop, op2)
+ else:
+ # most general case
+ mc.MOV(ecx, op1)
+ self.emit(mc, ecx, op2)
+ mc.MOV(dstop, ecx)
+
+class OpIntAdd(BinaryOp):
+ opname = 'int_add', 'uint_add'
+ emit = staticmethod(I386CodeBuilder.ADD)
+ commutative = True
+
+class OpIntSub(BinaryOp):
+ opname = 'int_sub', 'uint_sub'
+ emit = staticmethod(I386CodeBuilder.SUB)
+
+class OpIntAnd(BinaryOp):
+ opname = 'int_and', 'uint_and'
+ emit = staticmethod(I386CodeBuilder.AND)
+
+class OpIntOr(BinaryOp):
+ opname = 'int_or', 'uint_or'
+ emit = staticmethod(I386CodeBuilder.OR)
+
+class OpIntXor(BinaryOp):
+ opname = 'int_xor', 'uint_xor'
+ emit = staticmethod(I386CodeBuilder.XOR)
+
+class OpIntMul(Op2):
+ opname = 'int_mul'
+ def generate3(self, mc, dstop, op1, op2):
+ if isinstance(dstop, REG):
+ tmpop = dstop
+ else:
+ tmpop = ecx
+ if tmpop == op1:
+ mc.IMUL(tmpop, op2)
+ elif isinstance(op2, IMM32):
+ mc.IMUL(tmpop, op1, op2)
+ elif isinstance(op1, IMM32):
+ mc.IMUL(tmpop, op2, op1)
+ else:
+ if tmpop != op2:
+ mc.MOV(tmpop, op2)
+ mc.IMUL(tmpop, op1)
+ if dstop != tmpop:
+ mc.MOV(dstop, tmpop)
+
+class MulOrDivOp(Op2):
+ def generate3(self, mc, dstop, op1, op2):
+ # not very efficient but not very common operations either
+ if dstop != eax:
+ mc.PUSH(eax)
+ if dstop != edx:
+ mc.PUSH(edx)
+ if op1 != eax:
+ mc.MOV(eax, op1)
+ if self.input_is_64bits:
+ if self.unsigned:
+ mc.XOR(edx, edx)
+ else:
+ mc.CDQ()
+ try:
+ self.emit(mc, op2)
+ except FailedToImplement:
+ mc.MOV(ecx, op2)
+ self.emit(mc, ecx)
+ if dstop != self.reg_containing_result:
+ mc.MOV(dstop, self.reg_containing_result)
+ if dstop != edx:
+ mc.POP(edx)
+ if dstop != eax:
+ mc.POP(eax)
+
+class OpIntFloorDiv(MulOrDivOp):
+ opname = 'int_floordiv'
+ input_is_64bits = True
+ reg_containing_result = eax
+ unsigned = False
+ @staticmethod
+ def emit(mc, op2):
+ # from the PPC backend which has the same problem:
+ #
+ # grumble, the powerpc handles division when the signs of x
+ # and y differ the other way to how cpython wants it. this
+ # crawling horror is a branch-free way of computing the right
+ # remainder in all cases. it's probably not optimal.
+ #
+ # we need to adjust the result iff the remainder is non-zero
+ # and the signs of x and y differ. in the standard-ish PPC
+ # way, we compute boolean values as either all-bits-0 or
+ # all-bits-1 and "and" them together, resulting in either
+ # adding 0 or -1 as needed in the final step.
+ #
+ # Python i386
+ # 20/3 = 6, 2 6, 2
+ # (-20)/3 = -7, 1 -6,-2 # operand signs differ
+ # 20/(-3) = -7,-1 -6, 2 # operand signs differ
+ # (-20)/(-3) = 6,-2 6,-2
+ #
+ if isinstance(op2, IMM32):
+ # if op2 is an immediate, we do an initial adjustment of operand 1
+ # so that we get directly the correct answer
+ if op2.value >= 0:
+ # if op1 is negative, subtract (op2-1)
+ mc.MOV(ecx, edx) # -1 if op1 is negative, 0 otherwise
+ mc.AND(ecx, imm(op2.value-1))
+ mc.SUB(eax, ecx)
+ mc.SBB(edx, imm8(0))
+ else:
+ # if op1 is positive (or null), add (op2-1)
+ mc.MOV(ecx, edx)
+ mc.NEG(ecx) # -1 if op1 is positive, 0 otherwise
+ mc.AND(ecx, imm(op2.value-1))
+ mc.ADD(eax, ecx)
+ mc.ADC(edx, imm8(0))
+ mc.MOV(ecx, op2)
+ mc.IDIV(ecx)
+ else:
+ # subtract 1 to the result if the operand signs differ and
+ # the remainder is not zero
+ mc.MOV(ecx, eax)
+ mc.IDIV(op2)
+ mc.XOR(ecx, op2)
+ mc.SAR(ecx, imm8(31)) # -1 if signs differ, 0 otherwise
+ mc.AND(ecx, edx) # nonnull if signs differ and edx != 0
+ mc.CMP(ecx, imm8(1)) # no carry flag iff signs differ and edx != 0
+ mc.ADC(eax, imm8(-1)) # subtract 1 iff no carry flag
+
+class OpIntMod(MulOrDivOp):
+ opname = 'int_mod'
+ input_is_64bits = True
+ reg_containing_result = edx
+ unsigned = False
+ @staticmethod
+ def emit(mc, op2):
+ # Python i386
+ # 20/3 = 6, 2 6, 2
+ # (-20)/3 = -7, 1 -6,-2 # operand signs differ
+ # 20/(-3) = -7,-1 -6, 2 # operand signs differ
+ # (-20)/(-3) = 6,-2 6,-2
+ #
+ if isinstance(op2, IMM32):
+ mc.MOV(ecx, op2)
+ mc.IDIV(ecx)
+ if op2.value >= 0:
+ # if the result is negative, add op2 to it
+ mc.MOV(ecx, edx)
+ mc.SAR(ecx, imm8(31))
+ mc.AND(ecx, imm(op2.value))
+ mc.ADD(edx, ecx)
+ else:
+ # if the result is > 0, subtract op2 from it
+ mc.MOV(ecx, edx)
+ mc.NEG(ecx)
+ mc.SAR(ecx, imm8(31))
+ mc.AND(ecx, imm(op2.value))
+ mc.SUB(edx, ecx)
+ else:
+ # if the operand signs differ and the remainder is not zero,
+ # add operand2 to the result
+ mc.MOV(ecx, eax)
+ mc.IDIV(op2)
+ mc.XOR(ecx, op2)
+ mc.SAR(ecx, imm8(31)) # -1 if signs differ, 0 otherwise
+ mc.AND(ecx, edx) # nonnull if signs differ and edx != 0
+ mc.CMOVNZ(ecx, op2) # == op2 if signs differ and edx != 0
+ mc.ADD(edx, ecx)
+
+class OpUIntMul(MulOrDivOp):
+ opname = 'uint_mul'
+ input_is_64bits = False
+ reg_containing_result = eax
+ unsigned = True
+ emit = staticmethod(I386CodeBuilder.MUL)
+
+class OpUIntFloorDiv(MulOrDivOp):
+ opname = 'uint_floordiv'
+ input_is_64bits = True
+ reg_containing_result = eax
+ unsigned = True
+ emit = staticmethod(I386CodeBuilder.DIV)
+
+class OpUIntMod(MulOrDivOp):
+ opname = 'uint_mod'
+ input_is_64bits = True
+ reg_containing_result = edx
+ unsigned = True
+ emit = staticmethod(I386CodeBuilder.DIV)
+
+class OpIntLShift(Op2):
+ opname = 'int_lshift', 'uint_lshift'
+ def generate3(self, mc, dstop, op1, op2):
+ # XXX not optimized
+ mc.MOV(ecx, op2)
+ if dstop != op1:
+ try:
+ mc.MOV(dstop, op1)
+ except FailedToImplement:
+ mc.PUSH(op1)
+ mc.POP(dstop)
+ mc.SHL(dstop, cl)
+ mc.CMP(ecx, imm8(32))
+ mc.SBB(ecx, ecx)
+ mc.AND(dstop, ecx)
+
+class OpIntRShift(Op2):
+ opname = 'int_rshift'
+ def generate3(self, mc, dstop, op1, op2):
+ # XXX not optimized
+ mc.MOV(ecx, imm(31))
+ mc.CMP(op2, ecx)
+ mc.CMOVBE(ecx, op2)
+ if dstop != op1:
+ try:
+ mc.MOV(dstop, op1)
+ except FailedToImplement:
+ mc.PUSH(op1)
+ mc.POP(dstop)
+ mc.SAR(dstop, cl)
+
+class OpUIntRShift(Op2):
+ opname = 'uint_rshift'
+ def generate3(self, mc, dstop, op1, op2):
+ # XXX not optimized
+ mc.MOV(ecx, op2)
+ if dstop != op1:
+ try:
+ mc.MOV(dstop, op1)
+ except FailedToImplement:
+ mc.PUSH(op1)
+ mc.POP(dstop)
+ mc.SHR(dstop, cl)
+ mc.CMP(ecx, imm8(32))
+ mc.SBB(ecx, ecx)
+ mc.AND(dstop, ecx)
+
+class OpCompare2(Op2):
+ result_kind = RK_CC
+ def generate(self, allocator):
+ srcop = allocator.get_operand(self.x)
+ dstop = allocator.get_operand(self.y)
+ mc = allocator.mc
+ # XXX optimize the case CMP(immed, reg-or-modrm)
+ try:
+ mc.CMP(srcop, dstop)
+ except FailedToImplement:
+ mc.MOV(ecx, srcop)
+ mc.CMP(ecx, dstop)
+
+class OpIntLt(OpCompare2):
+ opname = 'int_lt', 'char_lt'
+ cc_result = Conditions['L']
+
+class OpIntLe(OpCompare2):
+ opname = 'int_le', 'char_le'
+ cc_result = Conditions['LE']
+
+class OpIntEq(OpCompare2):
+ opname = 'int_eq', 'char_eq', 'unichar_eq', 'ptr_eq', 'uint_eq'
+ cc_result = Conditions['E']
+
+class OpIntNe(OpCompare2):
+ opname = 'int_ne', 'char_ne', 'unichar_ne', 'ptr_ne', 'uint_ne'
+ cc_result = Conditions['NE']
+
+class OpIntGt(OpCompare2):
+ opname = 'int_gt', 'char_gt'
+ cc_result = Conditions['G']
+
+class OpIntGe(OpCompare2):
+ opname = 'int_ge', 'char_ge'
+ cc_result = Conditions['GE']
+
+class OpUIntLt(OpCompare2):
+ opname = 'uint_lt'
+ cc_result = Conditions['B']
+
+class OpUIntLe(OpCompare2):
+ opname = 'uint_le'
+ cc_result = Conditions['BE']
+
+class OpUIntGt(OpCompare2):
+ opname = 'uint_gt'
+ cc_result = Conditions['A']
+
+class OpUIntGe(OpCompare2):
+ opname = 'uint_ge'
+ cc_result = Conditions['AE']
+
+class JumpIf(Operation):
+ clobbers_cc = False
+ result_kind = RK_NO_RESULT
+ def __init__(self, gv_condition, targetbuilder, negate):
+ self.gv_condition = gv_condition
+ self.targetbuilder = targetbuilder
+ self.negate = negate
+ def allocate(self, allocator):
+ allocator.using_cc(self.gv_condition)
+ for gv in self.targetbuilder.inputargs_gv:
+ allocator.using(gv)
+ def generate(self, allocator):
+ cc = self.gv_condition.cc_result
+ if self.negate:
+ cc = cond_negate(cc)
+ mc = allocator.mc
+ targetbuilder = self.targetbuilder
+ targetbuilder.set_coming_from(mc, insncond=cc)
+ targetbuilder.inputoperands = [allocator.get_operand(gv)
+ for gv in targetbuilder.inputargs_gv]
+
+class OpLabel(Operation):
+ clobbers_cc = False
+ result_kind = RK_NO_RESULT
+ def __init__(self, lbl, args_gv):
+ self.lbl = lbl
+ self.args_gv = args_gv
+ def allocate(self, allocator):
+ for v in self.args_gv:
+ allocator.using(v)
+ def generate(self, allocator):
+ lbl = self.lbl
+ lbl.targetaddr = allocator.mc.tell()
+ lbl.targetstackdepth = allocator.required_frame_depth
+ lbl.inputoperands = [allocator.get_operand(v) for v in self.args_gv]
+
+class OpCall(Operation):
+ def __init__(self, sigtoken, gv_fnptr, args_gv):
+ self.sigtoken = sigtoken
+ self.gv_fnptr = gv_fnptr
+ self.args_gv = args_gv
+ def allocate(self, allocator):
+ # XXX try to use eax for the result
+ allocator.using(self.gv_fnptr)
+ for v in self.args_gv:
+ allocator.using(v)
+ def generate(self, allocator):
+ try:
+ dstop = allocator.get_operand(self)
+ except KeyError:
+ dstop = None
+ mc = allocator.mc
+ stack_align_words = PROLOGUE_FIXED_WORDS
+ if dstop != eax:
+ mc.PUSH(eax)
+ if CALL_ALIGN > 1: stack_align_words += 1
+ if dstop != edx:
+ mc.PUSH(edx)
+ if CALL_ALIGN > 1: stack_align_words += 1
+ args_gv = self.args_gv
+ num_placeholders = 0
+ if CALL_ALIGN > 1:
+ stack_align_words += len(args_gv)
+ stack_align_words &= CALL_ALIGN-1
+ if stack_align_words > 0:
+ num_placeholders = CALL_ALIGN - stack_align_words
+ mc.SUB(esp, imm(WORD * num_placeholders))
+ for i in range(len(args_gv)-1, -1, -1):
+ srcop = allocator.get_operand(args_gv[i])
+ mc.PUSH(srcop)
+ fnop = allocator.get_operand(self.gv_fnptr)
+ if isinstance(fnop, IMM32):
+ mc.CALL(rel32(fnop.value))
+ else:
+ mc.CALL(fnop)
+ mc.ADD(esp, imm(WORD * (len(args_gv) + num_placeholders)))
+ if dstop != edx:
+ mc.POP(edx)
+ if dstop != eax:
+ if dstop is not None:
+ mc.MOV(dstop, eax)
+ mc.POP(eax)
+
+def field_operand(mc, base, fieldtoken):
+ # may use ecx
+ fieldoffset, fieldsize = fieldtoken
+
+ if isinstance(base, MODRM):
+ mc.MOV(ecx, base)
+ base = ecx
+ elif isinstance(base, IMM32):
+ fieldoffset += base.value
+ base = None
+
+ if fieldsize == 1:
+ return mem8(base, fieldoffset)
+ else:
+ return mem (base, fieldoffset)
+
+def array_item_operand(mc, base, arraytoken, opindex):
+ # may use ecx
+ _, startoffset, itemoffset = arraytoken
+
+ if isinstance(opindex, IMM32):
+ startoffset += itemoffset * opindex.value
+ opindex = None
+ indexshift = 0
+ elif itemoffset in SIZE2SHIFT:
+ if not isinstance(opindex, REG):
+ mc.MOV(ecx, opindex)
+ opindex = ecx
+ indexshift = SIZE2SHIFT[itemoffset]
+ else:
+ mc.IMUL(ecx, opindex, imm(itemoffset))
+ opindex = ecx
+ indexshift = 0
+
+ assert base is not ecx
+ if isinstance(base, MODRM):
+ if opindex != ecx:
+ mc.MOV(ecx, base)
+ else: # waaaa
+ opindex = None
+ if indexshift > 0:
+ mc.SHL(ecx, imm8(indexshift))
+ mc.ADD(ecx, base)
+ base = ecx
+ elif isinstance(base, IMM32):
+ startoffset += base.value
+ base = None
+
+ if itemoffset == 1:
+ return memSIB8(base, opindex, indexshift, startoffset)
+ else:
+ return memSIB (base, opindex, indexshift, startoffset)
+
+class OpComputeSize(Operation):
+ def __init__(self, varsizealloctoken, gv_length):
+ self.varsizealloctoken = varsizealloctoken
+ self.gv_length = gv_length
+ def allocate(self, allocator):
+ allocator.using(self.gv_length)
+ def generate(self, allocator):
+ dstop = allocator.get_operand(self)
+ srcop = allocator.get_operand(self.gv_length)
+ mc = allocator.mc
+ op_size = array_item_operand(mc, None, self.varsizealloctoken, srcop)
+ try:
+ mc.LEA(dstop, op_size)
+ except FailedToImplement:
+ mc.LEA(ecx, op_size)
+ mc.MOV(dstop, ecx)
+
+def hard_store(mc, opmemtarget, opvalue, itemsize):
+ # For the possibly hard cases of stores
+ # Generates a store to 'opmemtarget' of size 'itemsize' == 1, 2 or 4.
+ # If it is 1, opmemtarget must be a MODRM8; otherwise, it must be a MODRM.
+ if itemsize == WORD:
+ try:
+ mc.MOV(opmemtarget, opvalue)
+ except FailedToImplement:
+ if opmemtarget.involves_ecx():
+ mc.PUSH(opvalue)
+ mc.POP(opmemtarget)
+ else:
+ mc.MOV(ecx, opvalue)
+ mc.MOV(opmemtarget, ecx)
+ else:
+ must_pop_eax = False
+ if itemsize == 1:
+ if isinstance(opvalue, REG) and opvalue.lowest8bits:
+ # a register whose lower 8 bits are directly readable
+ opvalue = opvalue.lowest8bits
+ elif isinstance(opvalue, IMM8):
+ pass
+ else:
+ if opmemtarget.involves_ecx(): # grumble!
+ mc.PUSH(eax)
+ must_pop_eax = True
+ scratch = eax
+ else:
+ scratch = ecx
+ if opvalue.width == 1:
+ mc.MOV(scratch.lowest8bits, opvalue)
+ else:
+ mc.MOV(scratch, opvalue)
+ opvalue = scratch.lowest8bits
+ else:
+ assert itemsize == 2
+ if isinstance(opvalue, MODRM) or type(opvalue) is IMM32:
+ # no support for now to encode 16-bit immediates,
+ # so we use a scratch register for this case too
+ if opmemtarget.involves_ecx(): # grumble!
+ mc.PUSH(eax)
+ must_pop_eax = True
+ scratch = eax
+ else:
+ scratch = ecx
+ mc.MOV(scratch, opvalue)
+ opvalue = scratch
+ mc.o16() # prefix for the MOV below
+ # and eventually, the real store:
+ mc.MOV(opmemtarget, opvalue)
+ if must_pop_eax:
+ mc.POP(eax)
+
+def hard_load(mc, opdst, opmemsource, itemsize):
+ # For the possibly hard cases of stores
+ # Generates a load from 'opmemsource' of size 'itemsize' == 1, 2 or 4.
+ # If it is 1, opmemtarget must be a MODRM8; otherwise, it must be a MODRM.
+ if itemsize == WORD:
+ try:
+ mc.MOV(opdst, opmemsource)
+ except FailedToImplement: # opdst is a MODRM
+ if opmemsource.involves_ecx():
+ mc.PUSH(opmemsource)
+ mc.POP(opdst)
+ else:
+ mc.MOV(ecx, opmemsource)
+ mc.MOV(opdst, ecx)
+ else:
+ try:
+ mc.MOVZX(opdst, opmemsource)
+ except FailedToImplement: # opdst is a MODRM
+ if opmemsource.involves_ecx():
+ mc.PUSH(eax)
+ mc.MOVZX(eax, opmemsource)
+ mc.MOV(opdst, eax)
+ mc.POP(eax)
+ else:
+ mc.MOVZX(ecx, opmemsource)
+ mc.MOV(opdst, ecx)
+
+class OpGetField(Operation):
+ def __init__(self, fieldtoken, gv_ptr):
+ self.fieldtoken = fieldtoken
+ self.gv_ptr = gv_ptr
+ def allocate(self, allocator):
+ allocator.using(self.gv_ptr)
+ def generate(self, allocator):
+ try:
+ dstop = allocator.get_operand(self)
+ except KeyError:
+ return # result not used
+ opptr = allocator.get_operand(self.gv_ptr)
+ mc = allocator.mc
+ opsource = field_operand(mc, opptr, self.fieldtoken)
+ _, fieldsize = self.fieldtoken
+ hard_load(mc, dstop, opsource, fieldsize)
+
+class OpSetField(Operation):
+ result_kind = RK_NO_RESULT
+ def __init__(self, fieldtoken, gv_ptr, gv_value):
+ self.fieldtoken = fieldtoken
+ self.gv_ptr = gv_ptr
+ self.gv_value = gv_value
+ def allocate(self, allocator):
+ allocator.using(self.gv_ptr)
+ allocator.using(self.gv_value)
+ def generate(self, allocator):
+ opptr = allocator.get_operand(self.gv_ptr)
+ opvalue = allocator.get_operand(self.gv_value)
+ mc = allocator.mc
+ optarget = field_operand(mc, opptr, self.fieldtoken)
+ _, fieldsize = self.fieldtoken
+ hard_store(mc, optarget, opvalue, fieldsize)
+
+class OpGetArrayItem(Operation):
+ def __init__(self, arraytoken, gv_array, gv_index):
+ self.arraytoken = arraytoken
+ self.gv_array = gv_array
+ self.gv_index = gv_index
+ def allocate(self, allocator):
+ allocator.using(self.gv_array)
+ allocator.using(self.gv_index)
+ def generate(self, allocator):
+ try:
+ dstop = allocator.get_operand(self)
+ except KeyError:
+ return # result not used
+ oparray = allocator.get_operand(self.gv_array)
+ opindex = allocator.get_operand(self.gv_index)
+ mc = allocator.mc
+ opsource = array_item_operand(mc, oparray, self.arraytoken, opindex)
+ _, _, itemsize = self.arraytoken
+ hard_load(mc, dstop, opsource, itemsize)
+
+class OpSetArrayItem(Operation):
+ result_kind = RK_NO_RESULT
+ def __init__(self, arraytoken, gv_array, gv_index, gv_value):
+ self.arraytoken = arraytoken
+ self.gv_array = gv_array
+ self.gv_index = gv_index
+ self.gv_value = gv_value
+ def allocate(self, allocator):
+ allocator.using(self.gv_array)
+ allocator.using(self.gv_index)
+ allocator.using(self.gv_value)
+ def generate(self, allocator):
+ oparray = allocator.get_operand(self.gv_array)
+ opindex = allocator.get_operand(self.gv_index)
+ opvalue = allocator.get_operand(self.gv_value)
+ mc = allocator.mc
+ optarget = array_item_operand(mc, oparray, self.arraytoken, opindex)
+ _, _, itemsize = self.arraytoken
+ hard_store(mc, optarget, opvalue, itemsize)
+
+class OpGetArraySubstruct(Operation):
+ def __init__(self, arraytoken, gv_array, gv_index):
+ self.arraytoken = arraytoken
+ self.gv_array = gv_array
+ self.gv_index = gv_index
+ def allocate(self, allocator):
+ allocator.using(self.gv_array)
+ allocator.using(self.gv_index)
+ def generate(self, allocator):
+ try:
+ dstop = allocator.get_operand(self)
+ except KeyError:
+ return # result not used
+ oparray = allocator.get_operand(self.gv_array)
+ opindex = allocator.get_operand(self.gv_index)
+ mc = allocator.mc
+ opsource = array_item_operand(mc, oparray, self.arraytoken, opindex)
+ try:
+ mc.LEA(dstop, opsource)
+ except FailedToImplement:
+ mc.LEA(ecx, opsource)
+ mc.MOV(dstop, ecx)
+
+# ____________________________________________________________
+
+def setup_opclasses(base):
+ d = {}
+ for name, value in globals().items():
+ if type(value) is type(base) and issubclass(value, base):
+ opnames = getattr(value, 'opname', ())
+ if isinstance(opnames, str):
+ opnames = (opnames,)
+ for opname in opnames:
+ assert opname not in d
+ d[opname] = value
+ return d
+OPCLASSES1 = setup_opclasses(Op1)
+OPCLASSES2 = setup_opclasses(Op2)
+del setup_opclasses
+
+# identity operations
+OPCLASSES1['cast_bool_to_int'] = None
+OPCLASSES1['cast_char_to_int'] = None
+OPCLASSES1['cast_unichar_to_int'] = None
+OPCLASSES1['cast_int_to_char'] = None
+OPCLASSES1['cast_int_to_unichar'] = None
+OPCLASSES1['cast_ptr_to_int'] = None
+OPCLASSES1['cast_int_to_ptr'] = None
+OPCLASSES1['cast_uint_to_int'] = None
+OPCLASSES1['cast_bool_to_uint'] = None
+OPCLASSES1['cast_int_to_uint'] = None
+
+# special cases
+#OPCLASSES1['bool_not'] = genop_bool_not XXX do something about it
+
+ at specialize.memo()
+def getopclass1(opname):
+ try:
+ return OPCLASSES1[opname]
+ except KeyError:
+ raise MissingBackendOperation(opname)
+
+ at specialize.memo()
+def getopclass2(opname):
+ try:
+ return OPCLASSES2[opname]
+ except KeyError:
+ raise MissingBackendOperation(opname)
+
+class MissingBackendOperation(Exception):
+ pass
+
+
+def setup_conditions():
+ result1 = [None] * 16
+ result2 = [None] * 16
+ for key, value in Conditions.items():
+ result1[value] = getattr(I386CodeBuilder, 'J'+key)
+ result2[value] = getattr(I386CodeBuilder, 'SET'+key)
+ return result1, result2
+EMIT_JCOND, EMIT_SETCOND = setup_conditions()
+INSN_JMP = len(EMIT_JCOND)
+EMIT_JCOND.append(I386CodeBuilder.JMP) # not really a conditional jump
+del setup_conditions
+
+def cond_negate(cond):
+ assert 0 <= cond < INSN_JMP
+ return cond ^ 1
+
+SIZE2SHIFT = {1: 0,
+ 2: 1,
+ 4: 2,
+ 8: 3}
Added: pypy/dist/pypy/jit/codegen/i386/regalloc.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/jit/codegen/i386/regalloc.py Thu Jan 25 15:11:41 2007
@@ -0,0 +1,231 @@
+"""Register allocation.
+
+"""
+
+from pypy.rlib.objectmodel import we_are_translated
+from pypy.rpython.lltypesystem import lltype
+from pypy.jit.codegen.i386.operation import *
+
+
+class StackOpCache:
+ INITIAL_STACK_EBP_OFS = -4
+stack_op_cache = StackOpCache()
+stack_op_cache.lst = []
+
+def stack_op(n):
+ "Return the mem operand that designates the nth stack-spilled location"
+ assert n >= 0
+ lst = stack_op_cache.lst
+ while len(lst) <= n:
+ ofs = WORD * (StackOpCache.INITIAL_STACK_EBP_OFS - len(lst))
+ lst.append(mem(ebp, ofs))
+ return lst[n]
+
+def stack_n_from_op(op):
+ ofs = op.ofs_relative_to_ebp()
+ return StackOpCache.INITIAL_STACK_EBP_OFS - ofs / WORD
+
+
+class RegAllocator(object):
+ AVAILABLE_REGS = [eax, edx, ebx, esi, edi] # XXX ecx reserved for stuff
+
+ # 'gv' -- GenVars, used as arguments and results of operations
+ #
+ # 'loc' -- location, a small integer that represents an abstract
+ # register number
+ #
+ # 'operand' -- a concrete machine code operand, which can be a
+ # register (ri386.eax, etc.) or a stack memory operand
+
+ def __init__(self):
+ self.nextloc = 0
+ self.var2loc = {}
+ self.available_locs = []
+ self.force_loc2operand = {}
+ self.force_operand2loc = {}
+ self.initial_moves = []
+
+ def set_final(self, final_vars_gv):
+ for v in final_vars_gv:
+ if not v.is_const and v not in self.var2loc:
+ self.var2loc[v] = self.nextloc
+ self.nextloc += 1
+
+ def creating(self, v):
+ try:
+ loc = self.var2loc[v]
+ except KeyError:
+ pass
+ else:
+ self.available_locs.append(loc) # now available again for reuse
+
+ def using(self, v):
+ if not v.is_const and v not in self.var2loc:
+ try:
+ loc = self.available_locs.pop()
+ except IndexError:
+ loc = self.nextloc
+ self.nextloc += 1
+ self.var2loc[v] = loc
+
+ def creating_cc(self, v):
+ if self.need_var_in_cc is v:
+ # common case: v is a compare operation whose result is precisely
+ # what we need to be in the CC
+ self.need_var_in_cc = None
+ self.creating(v)
+
+ def save_cc(self):
+ # we need a value to be in the CC, but we see a clobbering
+ # operation, so we copy the original CC-creating operation down
+ # past the clobbering operation
+ v = self.need_var_in_cc
+ if not we_are_translated():
+ assert v in self.operations[:self.operationindex]
+ self.operations.insert(self.operationindex, v)
+ self.need_var_in_cc = None
+
+ def using_cc(self, v):
+ assert isinstance(v, Operation)
+ assert 0 <= v.cc_result < INSN_JMP
+ if self.need_var_in_cc is not None:
+ self.save_cc()
+ self.need_var_in_cc = v
+
+ def allocate_locations(self, operations):
+ # assign locations to gvars
+ self.operations = operations
+ self.need_var_in_cc = None
+ self.operationindex = len(operations)
+ for i in range(len(operations)-1, -1, -1):
+ v = operations[i]
+ kind = v.result_kind
+ if kind == RK_WORD:
+ self.creating(v)
+ elif kind == RK_CC:
+ self.creating_cc(v)
+ if self.need_var_in_cc is not None and v.clobbers_cc:
+ self.save_cc()
+ v.allocate(self)
+ self.operationindex = i
+ if self.need_var_in_cc is not None:
+ self.save_cc()
+
+ def force_var_operands(self, force_vars, force_operands, at_start):
+ force_loc2operand = self.force_loc2operand
+ force_operand2loc = self.force_operand2loc
+ for i in range(len(force_vars)):
+ v = force_vars[i]
+ operand = force_operands[i]
+ try:
+ loc = self.var2loc[v]
+ except KeyError:
+ if at_start:
+ pass # input variable not used anyway
+ else:
+ self.add_final_move(v, operand, make_copy=v.is_const)
+ else:
+ if loc in force_loc2operand or operand in force_operand2loc:
+ if at_start:
+ self.initial_moves.append((loc, operand))
+ else:
+ self.add_final_move(v, operand, make_copy=True)
+ else:
+ force_loc2operand[loc] = operand
+ force_operand2loc[operand] = loc
+
+ def add_final_move(self, v, targetoperand, make_copy):
+ if make_copy:
+ v = OpSameAs(v)
+ self.operations.append(v)
+ loc = self.nextloc
+ self.nextloc += 1
+ self.var2loc[v] = loc
+ self.force_loc2operand[loc] = targetoperand
+
+ def allocate_registers(self):
+ # assign registers to locations that don't have one already
+ force_loc2operand = self.force_loc2operand
+ operands = []
+ seen_regs = 0
+ seen_stackn = {}
+ for op in force_loc2operand.values():
+ if isinstance(op, REG):
+ seen_regs |= 1 << op.op
+ elif isinstance(op, MODRM):
+ seen_stackn[stack_n_from_op(op)] = None
+ i = 0
+ stackn = 0
+ for loc in range(self.nextloc):
+ try:
+ operand = force_loc2operand[loc]
+ except KeyError:
+ # grab the next free register
+ try:
+ while True:
+ operand = RegAllocator.AVAILABLE_REGS[i]
+ i += 1
+ if not (seen_regs & (1 << operand.op)):
+ break
+ except IndexError:
+ while stackn in seen_stackn:
+ stackn += 1
+ operand = stack_op(stackn)
+ stackn += 1
+ operands.append(operand)
+ self.operands = operands
+ self.required_frame_depth = stackn
+
+ def get_operand(self, gv_source):
+ if gv_source.is_const:
+ return imm(gv_source.revealconst(lltype.Signed))
+ else:
+ loc = self.var2loc[gv_source]
+ return self.operands[loc]
+
+ def load_location_with(self, loc, gv_source):
+ dstop = self.operands[loc]
+ srcop = self.get_operand(gv_source)
+ if srcop != dstop:
+ self.mc.MOV(dstop, srcop)
+ return dstop
+
+ def generate_initial_moves(self):
+ initial_moves = self.initial_moves
+ # first make sure that the reserved stack frame is big enough
+ last_n = self.required_frame_depth - 1
+ for loc, srcoperand in initial_moves:
+ if isinstance(srcoperand, MODRM):
+ n = stack_n_from_op(srcoperand)
+ if last_n < n:
+ last_n = n
+ if last_n >= 0:
+ if CALL_ALIGN > 1:
+ last_n = (last_n & ~(CALL_ALIGN-1)) + (CALL_ALIGN-1)
+ self.required_frame_depth = last_n + 1
+ self.mc.LEA(esp, stack_op(last_n))
+ # XXX naive algo for now
+ for loc, srcoperand in initial_moves:
+ if self.operands[loc] != srcoperand:
+ self.mc.PUSH(srcoperand)
+ initial_moves.reverse()
+ for loc, srcoperand in initial_moves:
+ if self.operands[loc] != srcoperand:
+ self.mc.POP(self.operands[loc])
+
+ def generate_operations(self):
+ for v in self.operations:
+ v.generate(self)
+ cc = v.cc_result
+ if cc >= 0 and v in self.var2loc:
+ # force a comparison instruction's result into a
+ # regular location
+ dstop = self.get_operand(v)
+ mc = self.mc
+ insn = EMIT_SETCOND[cc]
+ insn(mc, cl)
+ try:
+ mc.MOVZX(dstop, cl)
+ except FailedToImplement:
+ mc.MOVZX(ecx, cl)
+ mc.MOV(dstop, ecx)
Modified: pypy/dist/pypy/jit/codegen/i386/rgenop.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/i386/rgenop.py (original)
+++ pypy/dist/pypy/jit/codegen/i386/rgenop.py Thu Jan 25 15:11:41 2007
@@ -1,796 +1,16 @@
-import sys, py
+import py
from pypy.rlib.objectmodel import specialize, we_are_translated
from pypy.rpython.lltypesystem import lltype, llmemory
from pypy.jit.codegen.model import AbstractRGenOp, GenLabel, GenBuilder
from pypy.jit.codegen.model import GenVar, GenConst, CodeGenSwitch
-from pypy.objspace.std.multimethod import FailedToImplement
-from pypy.jit.codegen.i386.ri386 import *
-from pypy.jit.codegen.i386.ri386setup import Conditions
from pypy.jit.codegen.i386.codebuf import CodeBlockOverflow
+from pypy.jit.codegen.i386.operation import *
+from pypy.jit.codegen.i386.regalloc import RegAllocator
from pypy.jit.codegen import conftest
from pypy.rpython.annlowlevel import llhelper
-
-WORD = 4 # bytes
-if sys.platform == 'darwin':
- CALL_ALIGN = 4
-else:
- CALL_ALIGN = 1
-
-PROLOGUE_FIXED_WORDS = 5
-
-RK_NO_RESULT = 0
-RK_WORD = 1
-RK_CC = 2
-
DEBUG_TRAP = conftest.option.trap
-
-class Operation(GenVar):
- clobbers_cc = True
- result_kind = RK_WORD
- cc_result = -1
-
- def allocate(self, allocator):
- pass
- def generate(self, allocator):
- raise NotImplementedError
-
-class Op1(Operation):
- def __init__(self, x):
- self.x = x
- def allocate(self, allocator):
- allocator.using(self.x)
- def generate(self, allocator):
- try:
- dstop = allocator.get_operand(self)
- except KeyError:
- return # result not used
- srcop = allocator.get_operand(self.x)
- return self.generate2(allocator.mc, dstop, srcop)
- def generate2(self, mc, dstop, srcop):
- raise NotImplementedError
-
-class UnaryOp(Op1):
- def generate(self, allocator):
- try:
- loc = allocator.var2loc[self]
- except KeyError:
- return # simple operation whose result is not used anyway
- op = allocator.load_location_with(loc, self.x)
- self.emit(allocator.mc, op)
-
-class OpIntNeg(UnaryOp):
- opname = 'int_neg'
- emit = staticmethod(I386CodeBuilder.NEG)
-
-class OpIntInvert(UnaryOp):
- opname = 'int_invert', 'uint_invert'
- emit = staticmethod(I386CodeBuilder.NOT)
-
-class OpIntAbs(Op1):
- opname = 'int_abs'
- def generate2(self, mc, dstop, srcop):
- # ABS-computing code from Psyco, found by exhaustive search
- # on *all* short sequences of operations :-)
- inplace = (dstop == srcop)
- if inplace or not (isinstance(srcop, REG) or isinstance(dstop, REG)):
- mc.MOV(ecx, srcop)
- srcop = ecx
- if not inplace:
- mc.MOV(dstop, srcop)
- mc.SHL(dstop, imm8(1))
- mc.SBB(dstop, srcop)
- mc.SBB(ecx, ecx)
- mc.XOR(dstop, ecx)
-
-class OpSameAs(Op1):
- clobbers_cc = False
- def generate2(self, mc, dstop, srcop):
- if srcop != dstop:
- try:
- mc.MOV(dstop, srcop)
- except FailedToImplement:
- mc.MOV(ecx, srcop)
- mc.MOV(dstop, ecx)
-
-class OpCompare1(Op1):
- result_kind = RK_CC
- def generate(self, allocator):
- srcop = allocator.get_operand(self.x)
- mc = allocator.mc
- self.emit(mc, srcop)
-
-class OpIntIsTrue(OpCompare1):
- opname = 'int_is_true', 'ptr_nonzero', 'uint_is_true'
- cc_result = Conditions['NE']
- @staticmethod
- def emit(mc, x):
- mc.CMP(x, imm8(0))
-
-class OpIntIsZero(OpIntIsTrue):
- opname = 'ptr_iszero', 'bool_not'
- cc_result = Conditions['E']
-
-class Op2(Operation):
- def __init__(self, x, y):
- self.x = x
- self.y = y
- def allocate(self, allocator):
- allocator.using(self.x)
- allocator.using(self.y)
- def generate(self, allocator):
- try:
- dstop = allocator.get_operand(self)
- except KeyError:
- return # simple operation whose result is not used anyway
- op1 = allocator.get_operand(self.x)
- op2 = allocator.get_operand(self.y)
- self.generate3(allocator.mc, dstop, op1, op2)
- def generate3(self, mc, dstop, op1, op2):
- raise NotImplementedError
-
-class BinaryOp(Op2):
- commutative = False
- def generate3(self, mc, dstop, op1, op2):
- # now all of dstop, op1 and op2 may alias each other and be in
- # a register, in the stack or an immediate... finding a correct
- # and encodable combination of instructions is loads of fun
- if dstop == op1:
- case = 1 # optimize for this common case
- elif self.commutative and dstop == op2:
- op1, op2 = op2, op1
- case = 1
- elif isinstance(dstop, REG):
- if dstop != op2:
- # REG = OPERATION(op1, op2) with op2 != REG
- case = 2
- else:
- # REG = OPERATION(op1, REG)
- case = 3
- elif isinstance(op1, REG) and isinstance(op2, REG):
- # STACK = OPERATION(REG, REG)
- case = 2
- else:
- case = 3
- # generate instructions according to the 'case' determined above
- if case == 1:
- # dstop == op1
- try:
- self.emit(mc, op1, op2)
- except FailedToImplement: # emit(STACK, STACK) combination
- mc.MOV(ecx, op2)
- self.emit(mc, op1, ecx)
- elif case == 2:
- # this case works for:
- # * REG = OPERATION(op1, op2) with op2 != REG
- # * STACK = OPERATION(REG, REG)
- mc.MOV(dstop, op1)
- self.emit(mc, dstop, op2)
- else:
- # most general case
- mc.MOV(ecx, op1)
- self.emit(mc, ecx, op2)
- mc.MOV(dstop, ecx)
-
-class OpIntAdd(BinaryOp):
- opname = 'int_add', 'uint_add'
- emit = staticmethod(I386CodeBuilder.ADD)
- commutative = True
-
-class OpIntSub(BinaryOp):
- opname = 'int_sub', 'uint_sub'
- emit = staticmethod(I386CodeBuilder.SUB)
-
-class OpIntAnd(BinaryOp):
- opname = 'int_and', 'uint_and'
- emit = staticmethod(I386CodeBuilder.AND)
-
-class OpIntOr(BinaryOp):
- opname = 'int_or', 'uint_or'
- emit = staticmethod(I386CodeBuilder.OR)
-
-class OpIntXor(BinaryOp):
- opname = 'int_xor', 'uint_xor'
- emit = staticmethod(I386CodeBuilder.XOR)
-
-class OpIntMul(Op2):
- opname = 'int_mul'
- def generate3(self, mc, dstop, op1, op2):
- if isinstance(dstop, REG):
- tmpop = dstop
- else:
- tmpop = ecx
- if tmpop == op1:
- mc.IMUL(tmpop, op2)
- elif isinstance(op2, IMM32):
- mc.IMUL(tmpop, op1, op2)
- elif isinstance(op1, IMM32):
- mc.IMUL(tmpop, op2, op1)
- else:
- if tmpop != op2:
- mc.MOV(tmpop, op2)
- mc.IMUL(tmpop, op1)
- if dstop != tmpop:
- mc.MOV(dstop, tmpop)
-
-class MulOrDivOp(Op2):
- def generate3(self, mc, dstop, op1, op2):
- # not very efficient but not very common operations either
- if dstop != eax:
- mc.PUSH(eax)
- if dstop != edx:
- mc.PUSH(edx)
- if op1 != eax:
- mc.MOV(eax, op1)
- if self.input_is_64bits:
- if self.unsigned:
- mc.XOR(edx, edx)
- else:
- mc.CDQ()
- try:
- self.emit(mc, op2)
- except FailedToImplement:
- mc.MOV(ecx, op2)
- self.emit(mc, ecx)
- if dstop != self.reg_containing_result:
- mc.MOV(dstop, self.reg_containing_result)
- if dstop != edx:
- mc.POP(edx)
- if dstop != eax:
- mc.POP(eax)
-
-class OpIntFloorDiv(MulOrDivOp):
- opname = 'int_floordiv'
- input_is_64bits = True
- reg_containing_result = eax
- unsigned = False
- @staticmethod
- def emit(mc, op2):
- # from the PPC backend which has the same problem:
- #
- # grumble, the powerpc handles division when the signs of x
- # and y differ the other way to how cpython wants it. this
- # crawling horror is a branch-free way of computing the right
- # remainder in all cases. it's probably not optimal.
- #
- # we need to adjust the result iff the remainder is non-zero
- # and the signs of x and y differ. in the standard-ish PPC
- # way, we compute boolean values as either all-bits-0 or
- # all-bits-1 and "and" them together, resulting in either
- # adding 0 or -1 as needed in the final step.
- #
- # Python i386
- # 20/3 = 6, 2 6, 2
- # (-20)/3 = -7, 1 -6,-2 # operand signs differ
- # 20/(-3) = -7,-1 -6, 2 # operand signs differ
- # (-20)/(-3) = 6,-2 6,-2
- #
- if isinstance(op2, IMM32):
- # if op2 is an immediate, we do an initial adjustment of operand 1
- # so that we get directly the correct answer
- if op2.value >= 0:
- # if op1 is negative, subtract (op2-1)
- mc.MOV(ecx, edx) # -1 if op1 is negative, 0 otherwise
- mc.AND(ecx, imm(op2.value-1))
- mc.SUB(eax, ecx)
- mc.SBB(edx, imm8(0))
- else:
- # if op1 is positive (or null), add (op2-1)
- mc.MOV(ecx, edx)
- mc.NEG(ecx) # -1 if op1 is positive, 0 otherwise
- mc.AND(ecx, imm(op2.value-1))
- mc.ADD(eax, ecx)
- mc.ADC(edx, imm8(0))
- mc.MOV(ecx, op2)
- mc.IDIV(ecx)
- else:
- # subtract 1 to the result if the operand signs differ and
- # the remainder is not zero
- mc.MOV(ecx, eax)
- mc.IDIV(op2)
- mc.XOR(ecx, op2)
- mc.SAR(ecx, imm8(31)) # -1 if signs differ, 0 otherwise
- mc.AND(ecx, edx) # nonnull if signs differ and edx != 0
- mc.CMP(ecx, imm8(1)) # no carry flag iff signs differ and edx != 0
- mc.ADC(eax, imm8(-1)) # subtract 1 iff no carry flag
-
-class OpIntMod(MulOrDivOp):
- opname = 'int_mod'
- input_is_64bits = True
- reg_containing_result = edx
- unsigned = False
- @staticmethod
- def emit(mc, op2):
- # Python i386
- # 20/3 = 6, 2 6, 2
- # (-20)/3 = -7, 1 -6,-2 # operand signs differ
- # 20/(-3) = -7,-1 -6, 2 # operand signs differ
- # (-20)/(-3) = 6,-2 6,-2
- #
- if isinstance(op2, IMM32):
- mc.MOV(ecx, op2)
- mc.IDIV(ecx)
- if op2.value >= 0:
- # if the result is negative, add op2 to it
- mc.MOV(ecx, edx)
- mc.SAR(ecx, imm8(31))
- mc.AND(ecx, imm(op2.value))
- mc.ADD(edx, ecx)
- else:
- # if the result is > 0, subtract op2 from it
- mc.MOV(ecx, edx)
- mc.NEG(ecx)
- mc.SAR(ecx, imm8(31))
- mc.AND(ecx, imm(op2.value))
- mc.SUB(edx, ecx)
- else:
- # if the operand signs differ and the remainder is not zero,
- # add operand2 to the result
- mc.MOV(ecx, eax)
- mc.IDIV(op2)
- mc.XOR(ecx, op2)
- mc.SAR(ecx, imm8(31)) # -1 if signs differ, 0 otherwise
- mc.AND(ecx, edx) # nonnull if signs differ and edx != 0
- mc.CMOVNZ(ecx, op2) # == op2 if signs differ and edx != 0
- mc.ADD(edx, ecx)
-
-class OpUIntMul(MulOrDivOp):
- opname = 'uint_mul'
- input_is_64bits = False
- reg_containing_result = eax
- unsigned = True
- emit = staticmethod(I386CodeBuilder.MUL)
-
-class OpUIntFloorDiv(MulOrDivOp):
- opname = 'uint_floordiv'
- input_is_64bits = True
- reg_containing_result = eax
- unsigned = True
- emit = staticmethod(I386CodeBuilder.DIV)
-
-class OpUIntMod(MulOrDivOp):
- opname = 'uint_mod'
- input_is_64bits = True
- reg_containing_result = edx
- unsigned = True
- emit = staticmethod(I386CodeBuilder.DIV)
-
-class OpIntLShift(Op2):
- opname = 'int_lshift', 'uint_lshift'
- def generate3(self, mc, dstop, op1, op2):
- # XXX not optimized
- mc.MOV(ecx, op2)
- if dstop != op1:
- try:
- mc.MOV(dstop, op1)
- except FailedToImplement:
- mc.PUSH(op1)
- mc.POP(dstop)
- mc.SHL(dstop, cl)
- mc.CMP(ecx, imm8(32))
- mc.SBB(ecx, ecx)
- mc.AND(dstop, ecx)
-
-class OpIntRShift(Op2):
- opname = 'int_rshift'
- def generate3(self, mc, dstop, op1, op2):
- # XXX not optimized
- mc.MOV(ecx, imm(31))
- mc.CMP(op2, ecx)
- mc.CMOVBE(ecx, op2)
- if dstop != op1:
- try:
- mc.MOV(dstop, op1)
- except FailedToImplement:
- mc.PUSH(op1)
- mc.POP(dstop)
- mc.SAR(dstop, cl)
-
-class OpUIntRShift(Op2):
- opname = 'uint_rshift'
- def generate3(self, mc, dstop, op1, op2):
- # XXX not optimized
- mc.MOV(ecx, op2)
- if dstop != op1:
- try:
- mc.MOV(dstop, op1)
- except FailedToImplement:
- mc.PUSH(op1)
- mc.POP(dstop)
- mc.SHR(dstop, cl)
- mc.CMP(ecx, imm8(32))
- mc.SBB(ecx, ecx)
- mc.AND(dstop, ecx)
-
-class OpCompare2(Op2):
- result_kind = RK_CC
- def generate(self, allocator):
- srcop = allocator.get_operand(self.x)
- dstop = allocator.get_operand(self.y)
- mc = allocator.mc
- # XXX optimize the case CMP(immed, reg-or-modrm)
- try:
- mc.CMP(srcop, dstop)
- except FailedToImplement:
- mc.MOV(ecx, srcop)
- mc.CMP(ecx, dstop)
-
-class OpIntLt(OpCompare2):
- opname = 'int_lt', 'char_lt'
- cc_result = Conditions['L']
-
-class OpIntLe(OpCompare2):
- opname = 'int_le', 'char_le'
- cc_result = Conditions['LE']
-
-class OpIntEq(OpCompare2):
- opname = 'int_eq', 'char_eq', 'unichar_eq', 'ptr_eq', 'uint_eq'
- cc_result = Conditions['E']
-
-class OpIntNe(OpCompare2):
- opname = 'int_ne', 'char_ne', 'unichar_ne', 'ptr_ne', 'uint_ne'
- cc_result = Conditions['NE']
-
-class OpIntGt(OpCompare2):
- opname = 'int_gt', 'char_gt'
- cc_result = Conditions['G']
-
-class OpIntGe(OpCompare2):
- opname = 'int_ge', 'char_ge'
- cc_result = Conditions['GE']
-
-class OpUIntLt(OpCompare2):
- opname = 'uint_lt'
- cc_result = Conditions['B']
-
-class OpUIntLe(OpCompare2):
- opname = 'uint_le'
- cc_result = Conditions['BE']
-
-class OpUIntGt(OpCompare2):
- opname = 'uint_gt'
- cc_result = Conditions['A']
-
-class OpUIntGe(OpCompare2):
- opname = 'uint_ge'
- cc_result = Conditions['AE']
-
-class JumpIf(Operation):
- clobbers_cc = False
- result_kind = RK_NO_RESULT
- def __init__(self, gv_condition, targetbuilder, negate):
- self.gv_condition = gv_condition
- self.targetbuilder = targetbuilder
- self.negate = negate
- def allocate(self, allocator):
- allocator.using_cc(self.gv_condition)
- for gv in self.targetbuilder.inputargs_gv:
- allocator.using(gv)
- def generate(self, allocator):
- cc = self.gv_condition.cc_result
- if self.negate:
- cc = cond_negate(cc)
- mc = allocator.mc
- targetbuilder = self.targetbuilder
- targetbuilder.set_coming_from(mc, insncond=cc)
- targetbuilder.inputoperands = [allocator.get_operand(gv)
- for gv in targetbuilder.inputargs_gv]
-
-class OpLabel(Operation):
- clobbers_cc = False
- result_kind = RK_NO_RESULT
- def __init__(self, lbl, args_gv):
- self.lbl = lbl
- self.args_gv = args_gv
- def allocate(self, allocator):
- for v in self.args_gv:
- allocator.using(v)
- def generate(self, allocator):
- lbl = self.lbl
- lbl.targetaddr = allocator.mc.tell()
- lbl.targetstackdepth = allocator.required_frame_depth
- lbl.inputoperands = [allocator.get_operand(v) for v in self.args_gv]
-
-class Label(GenLabel):
- targetaddr = 0
- targetstackdepth = 0
- inputoperands = None
-
-class OpCall(Operation):
- def __init__(self, sigtoken, gv_fnptr, args_gv):
- self.sigtoken = sigtoken
- self.gv_fnptr = gv_fnptr
- self.args_gv = args_gv
- def allocate(self, allocator):
- # XXX try to use eax for the result
- allocator.using(self.gv_fnptr)
- for v in self.args_gv:
- allocator.using(v)
- def generate(self, allocator):
- try:
- dstop = allocator.get_operand(self)
- except KeyError:
- dstop = None
- mc = allocator.mc
- stack_align_words = PROLOGUE_FIXED_WORDS
- if dstop != eax:
- mc.PUSH(eax)
- if CALL_ALIGN > 1: stack_align_words += 1
- if dstop != edx:
- mc.PUSH(edx)
- if CALL_ALIGN > 1: stack_align_words += 1
- args_gv = self.args_gv
- num_placeholders = 0
- if CALL_ALIGN > 1:
- stack_align_words += len(args_gv)
- stack_align_words &= CALL_ALIGN-1
- if stack_align_words > 0:
- num_placeholders = CALL_ALIGN - stack_align_words
- mc.SUB(esp, imm(WORD * num_placeholders))
- for i in range(len(args_gv)-1, -1, -1):
- srcop = allocator.get_operand(args_gv[i])
- mc.PUSH(srcop)
- fnop = allocator.get_operand(self.gv_fnptr)
- if isinstance(fnop, IMM32):
- mc.CALL(rel32(fnop.value))
- else:
- mc.CALL(fnop)
- mc.ADD(esp, imm(WORD * (len(args_gv) + num_placeholders)))
- if dstop != edx:
- mc.POP(edx)
- if dstop != eax:
- if dstop is not None:
- mc.MOV(dstop, eax)
- mc.POP(eax)
-
-def field_operand(mc, base, fieldtoken):
- # may use ecx
- fieldoffset, fieldsize = fieldtoken
-
- if isinstance(base, MODRM):
- mc.MOV(ecx, base)
- base = ecx
- elif isinstance(base, IMM32):
- fieldoffset += base.value
- base = None
-
- if fieldsize == 1:
- return mem8(base, fieldoffset)
- else:
- return mem (base, fieldoffset)
-
-def array_item_operand(mc, base, arraytoken, opindex):
- # may use ecx
- _, startoffset, itemoffset = arraytoken
-
- if isinstance(opindex, IMM32):
- startoffset += itemoffset * opindex.value
- opindex = None
- indexshift = 0
- elif itemoffset in SIZE2SHIFT:
- if not isinstance(opindex, REG):
- mc.MOV(ecx, opindex)
- opindex = ecx
- indexshift = SIZE2SHIFT[itemoffset]
- else:
- mc.IMUL(ecx, opindex, imm(itemoffset))
- opindex = ecx
- indexshift = 0
-
- assert base is not ecx
- if isinstance(base, MODRM):
- if opindex != ecx:
- mc.MOV(ecx, base)
- else: # waaaa
- opindex = None
- if indexshift > 0:
- mc.SHL(ecx, imm8(indexshift))
- mc.ADD(ecx, base)
- base = ecx
- elif isinstance(base, IMM32):
- startoffset += base.value
- base = None
-
- if itemoffset == 1:
- return memSIB8(base, opindex, indexshift, startoffset)
- else:
- return memSIB (base, opindex, indexshift, startoffset)
-
-class OpComputeSize(Operation):
- def __init__(self, varsizealloctoken, gv_length):
- self.varsizealloctoken = varsizealloctoken
- self.gv_length = gv_length
- def allocate(self, allocator):
- allocator.using(self.gv_length)
- def generate(self, allocator):
- dstop = allocator.get_operand(self)
- srcop = allocator.get_operand(self.gv_length)
- mc = allocator.mc
- op_size = array_item_operand(mc, None, self.varsizealloctoken, srcop)
- try:
- mc.LEA(dstop, op_size)
- except FailedToImplement:
- mc.LEA(ecx, op_size)
- mc.MOV(dstop, ecx)
-
-def hard_store(mc, opmemtarget, opvalue, itemsize):
- # For the possibly hard cases of stores
- # Generates a store to 'opmemtarget' of size 'itemsize' == 1, 2 or 4.
- # If it is 1, opmemtarget must be a MODRM8; otherwise, it must be a MODRM.
- if itemsize == WORD:
- try:
- mc.MOV(opmemtarget, opvalue)
- except FailedToImplement:
- if opmemtarget.involves_ecx():
- mc.PUSH(opvalue)
- mc.POP(opmemtarget)
- else:
- mc.MOV(ecx, opvalue)
- mc.MOV(opmemtarget, ecx)
- else:
- must_pop_eax = False
- if itemsize == 1:
- if isinstance(opvalue, REG) and opvalue.lowest8bits:
- # a register whose lower 8 bits are directly readable
- opvalue = opvalue.lowest8bits
- elif isinstance(opvalue, IMM8):
- pass
- else:
- if opmemtarget.involves_ecx(): # grumble!
- mc.PUSH(eax)
- must_pop_eax = True
- scratch = eax
- else:
- scratch = ecx
- if opvalue.width == 1:
- mc.MOV(scratch.lowest8bits, opvalue)
- else:
- mc.MOV(scratch, opvalue)
- opvalue = scratch.lowest8bits
- else:
- assert itemsize == 2
- if isinstance(opvalue, MODRM) or type(opvalue) is IMM32:
- # no support for now to encode 16-bit immediates,
- # so we use a scratch register for this case too
- if opmemtarget.involves_ecx(): # grumble!
- mc.PUSH(eax)
- must_pop_eax = True
- scratch = eax
- else:
- scratch = ecx
- mc.MOV(scratch, opvalue)
- opvalue = scratch
- mc.o16() # prefix for the MOV below
- # and eventually, the real store:
- mc.MOV(opmemtarget, opvalue)
- if must_pop_eax:
- mc.POP(eax)
-
-def hard_load(mc, opdst, opmemsource, itemsize):
- # For the possibly hard cases of stores
- # Generates a load from 'opmemsource' of size 'itemsize' == 1, 2 or 4.
- # If it is 1, opmemtarget must be a MODRM8; otherwise, it must be a MODRM.
- if itemsize == WORD:
- try:
- mc.MOV(opdst, opmemsource)
- except FailedToImplement: # opdst is a MODRM
- if opmemsource.involves_ecx():
- mc.PUSH(opmemsource)
- mc.POP(opdst)
- else:
- mc.MOV(ecx, opmemsource)
- mc.MOV(opdst, ecx)
- else:
- try:
- mc.MOVZX(opdst, opmemsource)
- except FailedToImplement: # opdst is a MODRM
- if opmemsource.involves_ecx():
- mc.PUSH(eax)
- mc.MOVZX(eax, opmemsource)
- mc.MOV(opdst, eax)
- mc.POP(eax)
- else:
- mc.MOVZX(ecx, opmemsource)
- mc.MOV(opdst, ecx)
-
-class OpGetField(Operation):
- def __init__(self, fieldtoken, gv_ptr):
- self.fieldtoken = fieldtoken
- self.gv_ptr = gv_ptr
- def allocate(self, allocator):
- allocator.using(self.gv_ptr)
- def generate(self, allocator):
- try:
- dstop = allocator.get_operand(self)
- except KeyError:
- return # result not used
- opptr = allocator.get_operand(self.gv_ptr)
- mc = allocator.mc
- opsource = field_operand(mc, opptr, self.fieldtoken)
- _, fieldsize = self.fieldtoken
- hard_load(mc, dstop, opsource, fieldsize)
-
-class OpSetField(Operation):
- result_kind = RK_NO_RESULT
- def __init__(self, fieldtoken, gv_ptr, gv_value):
- self.fieldtoken = fieldtoken
- self.gv_ptr = gv_ptr
- self.gv_value = gv_value
- def allocate(self, allocator):
- allocator.using(self.gv_ptr)
- allocator.using(self.gv_value)
- def generate(self, allocator):
- opptr = allocator.get_operand(self.gv_ptr)
- opvalue = allocator.get_operand(self.gv_value)
- mc = allocator.mc
- optarget = field_operand(mc, opptr, self.fieldtoken)
- _, fieldsize = self.fieldtoken
- hard_store(mc, optarget, opvalue, fieldsize)
-
-class OpGetArrayItem(Operation):
- def __init__(self, arraytoken, gv_array, gv_index):
- self.arraytoken = arraytoken
- self.gv_array = gv_array
- self.gv_index = gv_index
- def allocate(self, allocator):
- allocator.using(self.gv_array)
- allocator.using(self.gv_index)
- def generate(self, allocator):
- try:
- dstop = allocator.get_operand(self)
- except KeyError:
- return # result not used
- oparray = allocator.get_operand(self.gv_array)
- opindex = allocator.get_operand(self.gv_index)
- mc = allocator.mc
- opsource = array_item_operand(mc, oparray, self.arraytoken, opindex)
- _, _, itemsize = self.arraytoken
- hard_load(mc, dstop, opsource, itemsize)
-
-class OpSetArrayItem(Operation):
- result_kind = RK_NO_RESULT
- def __init__(self, arraytoken, gv_array, gv_index, gv_value):
- self.arraytoken = arraytoken
- self.gv_array = gv_array
- self.gv_index = gv_index
- self.gv_value = gv_value
- def allocate(self, allocator):
- allocator.using(self.gv_array)
- allocator.using(self.gv_index)
- allocator.using(self.gv_value)
- def generate(self, allocator):
- oparray = allocator.get_operand(self.gv_array)
- opindex = allocator.get_operand(self.gv_index)
- opvalue = allocator.get_operand(self.gv_value)
- mc = allocator.mc
- optarget = array_item_operand(mc, oparray, self.arraytoken, opindex)
- _, _, itemsize = self.arraytoken
- hard_store(mc, optarget, opvalue, itemsize)
-
-class OpGetArraySubstruct(Operation):
- def __init__(self, arraytoken, gv_array, gv_index):
- self.arraytoken = arraytoken
- self.gv_array = gv_array
- self.gv_index = gv_index
- def allocate(self, allocator):
- allocator.using(self.gv_array)
- allocator.using(self.gv_index)
- def generate(self, allocator):
- try:
- dstop = allocator.get_operand(self)
- except KeyError:
- return # result not used
- oparray = allocator.get_operand(self.gv_array)
- opindex = allocator.get_operand(self.gv_index)
- mc = allocator.mc
- opsource = array_item_operand(mc, oparray, self.arraytoken, opindex)
- try:
- mc.LEA(dstop, opsource)
- except FailedToImplement:
- mc.LEA(ecx, opsource)
- mc.MOV(dstop, ecx)
-
# ____________________________________________________________
class IntConst(GenConst):
@@ -909,77 +129,6 @@
# ____________________________________________________________
-def setup_opclasses(base):
- d = {}
- for name, value in globals().items():
- if type(value) is type(base) and issubclass(value, base):
- opnames = getattr(value, 'opname', ())
- if isinstance(opnames, str):
- opnames = (opnames,)
- for opname in opnames:
- assert opname not in d
- d[opname] = value
- return d
-OPCLASSES1 = setup_opclasses(Op1)
-OPCLASSES2 = setup_opclasses(Op2)
-del setup_opclasses
-
-# identity operations
-OPCLASSES1['cast_bool_to_int'] = None
-OPCLASSES1['cast_char_to_int'] = None
-OPCLASSES1['cast_unichar_to_int'] = None
-OPCLASSES1['cast_int_to_char'] = None
-OPCLASSES1['cast_int_to_unichar'] = None
-OPCLASSES1['cast_ptr_to_int'] = None
-OPCLASSES1['cast_int_to_ptr'] = None
-OPCLASSES1['cast_uint_to_int'] = None
-OPCLASSES1['cast_bool_to_uint'] = None
-OPCLASSES1['cast_int_to_uint'] = None
-
-# special cases
-#OPCLASSES1['bool_not'] = genop_bool_not XXX do something about it
-
- at specialize.memo()
-def getopclass1(opname):
- try:
- return OPCLASSES1[opname]
- except KeyError:
- raise MissingBackendOperation(opname)
-
- at specialize.memo()
-def getopclass2(opname):
- try:
- return OPCLASSES2[opname]
- except KeyError:
- raise MissingBackendOperation(opname)
-
-class MissingBackendOperation(Exception):
- pass
-
-
-def setup_conditions():
- result1 = [None] * 16
- result2 = [None] * 16
- for key, value in Conditions.items():
- result1[value] = getattr(I386CodeBuilder, 'J'+key)
- result2[value] = getattr(I386CodeBuilder, 'SET'+key)
- return result1, result2
-EMIT_JCOND, EMIT_SETCOND = setup_conditions()
-INSN_JMP = len(EMIT_JCOND)
-EMIT_JCOND.append(I386CodeBuilder.JMP) # not really a conditional jump
-del setup_conditions
-
-def cond_negate(cond):
- assert 0 <= cond < INSN_JMP
- return cond ^ 1
-
-SIZE2SHIFT = {1: 0,
- 2: 1,
- 4: 2,
- 8: 3}
-
-# ____________________________________________________________
-
GC_MALLOC = lltype.Ptr(lltype.FuncType([lltype.Signed], llmemory.Address))
def gc_malloc(size):
@@ -1013,230 +162,6 @@
# ____________________________________________________________
-class StackOpCache:
- INITIAL_STACK_EBP_OFS = -4
-stack_op_cache = StackOpCache()
-stack_op_cache.lst = []
-
-def stack_op(n):
- "Return the mem operand that designates the nth stack-spilled location"
- assert n >= 0
- lst = stack_op_cache.lst
- while len(lst) <= n:
- ofs = WORD * (StackOpCache.INITIAL_STACK_EBP_OFS - len(lst))
- lst.append(mem(ebp, ofs))
- return lst[n]
-
-def stack_n_from_op(op):
- ofs = op.ofs_relative_to_ebp()
- return StackOpCache.INITIAL_STACK_EBP_OFS - ofs / WORD
-
-
-class RegAllocator(object):
- AVAILABLE_REGS = [eax, edx, ebx, esi, edi] # XXX ecx reserved for stuff
-
- # 'gv' -- GenVars, used as arguments and results of operations
- #
- # 'loc' -- location, a small integer that represents an abstract
- # register number
- #
- # 'operand' -- a concrete machine code operand, which can be a
- # register (ri386.eax, etc.) or a stack memory operand
-
- def __init__(self):
- self.nextloc = 0
- self.var2loc = {}
- self.available_locs = []
- self.force_loc2operand = {}
- self.force_operand2loc = {}
- self.initial_moves = []
-
- def set_final(self, final_vars_gv):
- for v in final_vars_gv:
- if not v.is_const and v not in self.var2loc:
- self.var2loc[v] = self.nextloc
- self.nextloc += 1
-
- def creating(self, v):
- try:
- loc = self.var2loc[v]
- except KeyError:
- pass
- else:
- self.available_locs.append(loc) # now available again for reuse
-
- def using(self, v):
- if not v.is_const and v not in self.var2loc:
- try:
- loc = self.available_locs.pop()
- except IndexError:
- loc = self.nextloc
- self.nextloc += 1
- self.var2loc[v] = loc
-
- def creating_cc(self, v):
- if self.need_var_in_cc is v:
- # common case: v is a compare operation whose result is precisely
- # what we need to be in the CC
- self.need_var_in_cc = None
- self.creating(v)
-
- def save_cc(self):
- # we need a value to be in the CC, but we see a clobbering
- # operation, so we copy the original CC-creating operation down
- # past the clobbering operation
- v = self.need_var_in_cc
- if not we_are_translated():
- assert v in self.operations[:self.operationindex]
- self.operations.insert(self.operationindex, v)
- self.need_var_in_cc = None
-
- def using_cc(self, v):
- assert isinstance(v, Operation)
- assert 0 <= v.cc_result < INSN_JMP
- if self.need_var_in_cc is not None:
- self.save_cc()
- self.need_var_in_cc = v
-
- def allocate_locations(self, operations):
- # assign locations to gvars
- self.operations = operations
- self.need_var_in_cc = None
- self.operationindex = len(operations)
- for i in range(len(operations)-1, -1, -1):
- v = operations[i]
- kind = v.result_kind
- if kind == RK_WORD:
- self.creating(v)
- elif kind == RK_CC:
- self.creating_cc(v)
- if self.need_var_in_cc is not None and v.clobbers_cc:
- self.save_cc()
- v.allocate(self)
- self.operationindex = i
- if self.need_var_in_cc is not None:
- self.save_cc()
-
- def force_var_operands(self, force_vars, force_operands, at_start):
- force_loc2operand = self.force_loc2operand
- force_operand2loc = self.force_operand2loc
- for i in range(len(force_vars)):
- v = force_vars[i]
- operand = force_operands[i]
- try:
- loc = self.var2loc[v]
- except KeyError:
- if at_start:
- pass # input variable not used anyway
- else:
- self.add_final_move(v, operand, make_copy=v.is_const)
- else:
- if loc in force_loc2operand or operand in force_operand2loc:
- if at_start:
- self.initial_moves.append((loc, operand))
- else:
- self.add_final_move(v, operand, make_copy=True)
- else:
- force_loc2operand[loc] = operand
- force_operand2loc[operand] = loc
-
- def add_final_move(self, v, targetoperand, make_copy):
- if make_copy:
- v = OpSameAs(v)
- self.operations.append(v)
- loc = self.nextloc
- self.nextloc += 1
- self.var2loc[v] = loc
- self.force_loc2operand[loc] = targetoperand
-
- def allocate_registers(self):
- # assign registers to locations that don't have one already
- force_loc2operand = self.force_loc2operand
- operands = []
- seen_regs = 0
- seen_stackn = {}
- for op in force_loc2operand.values():
- if isinstance(op, REG):
- seen_regs |= 1 << op.op
- elif isinstance(op, MODRM):
- seen_stackn[stack_n_from_op(op)] = None
- i = 0
- stackn = 0
- for loc in range(self.nextloc):
- try:
- operand = force_loc2operand[loc]
- except KeyError:
- # grab the next free register
- try:
- while True:
- operand = RegAllocator.AVAILABLE_REGS[i]
- i += 1
- if not (seen_regs & (1 << operand.op)):
- break
- except IndexError:
- while stackn in seen_stackn:
- stackn += 1
- operand = stack_op(stackn)
- stackn += 1
- operands.append(operand)
- self.operands = operands
- self.required_frame_depth = stackn
-
- def get_operand(self, gv_source):
- if gv_source.is_const:
- return imm(gv_source.revealconst(lltype.Signed))
- else:
- loc = self.var2loc[gv_source]
- return self.operands[loc]
-
- def load_location_with(self, loc, gv_source):
- dstop = self.operands[loc]
- srcop = self.get_operand(gv_source)
- if srcop != dstop:
- self.mc.MOV(dstop, srcop)
- return dstop
-
- def generate_initial_moves(self):
- initial_moves = self.initial_moves
- # first make sure that the reserved stack frame is big enough
- last_n = self.required_frame_depth - 1
- for loc, srcoperand in initial_moves:
- if isinstance(srcoperand, MODRM):
- n = stack_n_from_op(srcoperand)
- if last_n < n:
- last_n = n
- if last_n >= 0:
- if CALL_ALIGN > 1:
- last_n = (last_n & ~(CALL_ALIGN-1)) + (CALL_ALIGN-1)
- self.required_frame_depth = last_n + 1
- self.mc.LEA(esp, stack_op(last_n))
- # XXX naive algo for now
- for loc, srcoperand in initial_moves:
- if self.operands[loc] != srcoperand:
- self.mc.PUSH(srcoperand)
- initial_moves.reverse()
- for loc, srcoperand in initial_moves:
- if self.operands[loc] != srcoperand:
- self.mc.POP(self.operands[loc])
-
- def generate_operations(self):
- for v in self.operations:
- v.generate(self)
- cc = v.cc_result
- if cc >= 0 and v in self.var2loc:
- # force a comparison instruction's result into a
- # regular location
- dstop = self.get_operand(v)
- mc = self.mc
- insn = EMIT_SETCOND[cc]
- insn(mc, cl)
- try:
- mc.MOVZX(dstop, cl)
- except FailedToImplement:
- mc.MOVZX(ecx, cl)
- mc.MOV(dstop, ecx)
-
-
class Builder(GenBuilder):
coming_from = 0
operations = None
@@ -1484,7 +409,13 @@
pass # self.mc.log(msg)
# XXX re-do this somehow...
-#
+
+class Label(GenLabel):
+ targetaddr = 0
+ targetstackdepth = 0
+ inputoperands = None
+
+# ____________________________________________________________
dummy_var = GenVar()
@@ -1570,6 +501,7 @@
def show_incremental_progress(self):
pass
+# ____________________________________________________________
class RI386GenOp(AbstractRGenOp):
from pypy.jit.codegen.i386.codebuf import MachineCodeBlock
More information about the Pypy-commit
mailing list