예제 #1
0
 def test3(self):
     engine = Processor16Bits()
     fifo = ActionsFIFO()
     fifo.append(Register(1, False), Litteral(45), Register(5, False),
                 Operators.ADD)
     binaryCode = "\n".join(engine.getBinary([fifo]))
     self.assertEqual(binaryCode, "1000101001101101")
예제 #2
0
 def test1(self):
     engine = Processor12Bits()
     fifo = ActionsFIFO()
     fifo.append(Register(1, False), Register(2, False), Register(0, False),
                 Operators.ADD)
     binaryCode = "\n".join(engine.getBinary([fifo]))
     self.assertEqual(binaryCode, "111110000110")
예제 #3
0
    def test2(self):
        r0 = Register(0, False)
        r1 = Register(1, False)
        op = Operators.ADD
        engine = Processor16Bits()
        actions = ActionsFIFO()
        actions.append(r1, r0, r0, op)

        asm = engine.getAsm(actions)
        self.assertEqual(asm, "	ADD r0, r1, r0")
예제 #4
0
    def test3(self):
        r0 = Register(0, False)
        r1 = Register(1, False)
        l4 = Litteral(4)
        op = Operators.ADD
        engine = Processor16Bits()
        actions = ActionsFIFO()
        actions.append(r1, l4, r0, op, r1, r0, r0, op)

        asm = engine.getAsm(actions)
        good = "\n".join(["	ADD r0, r1, #4", "	ADD r0, r1, r0"])
        self.assertEqual(asm, good)
예제 #5
0
    def compile(self, fifo:ActionsFIFO) -> ActionsFIFO:
        """compile la pile de calcul

        :param fifo: file de l'expression à compiler
        :type fifo: List[Union[Operator, Variable, Litteral]]
        :return: file des actions à affectuer en tenant compte des mouvements de registres
        :rtype: ActionsFIFO
        """
        self._actions = ActionsFIFO()
        while not fifo.empty:
            self._compileNext(fifo)
        return self._actions
예제 #6
0
    def _actionToBinary(
            self, fifo: ActionsFIFO, addressList: Dict[Union[Label, Variable],
                                                       int]) -> List[str]:
        """
        :param fifo: file des actions produite par la compilation
        :type fifo: ActionsFIFO
        :param addressList: adresses de variables et labels
        :type addressList: Dict[Union[Label,Variable],int]
        :return: code binaire
        :rtyp: List[str]
        """

        lines: List[str] = []
        currentActionItems: List[ActionType] = []
        while not fifo.empty:
            lastItem = fifo.pop()
            if (not isinstance(lastItem, Operator)) or lastItem.isComparaison:
                currentActionItems.append(lastItem)
                continue
            lines.extend(
                self._operatorToBinary(lastItem, currentActionItems,
                                       addressList))
            currentActionItems = []

        if len(currentActionItems) > 0:
            raise CompilationError(
                "La séquence devrait terminer par un opérateur.",
                {"lineNumber": fifo.lineNumber})

        return lines
예제 #7
0
    def _actionToAsm(self, fifo: ActionsFIFO) -> List[str]:
        """
        :param fifo: file des actions produite par la compilation
        :type fifo: ActionsFIFO
        :return: code asm
        :rtyp: List[str]
        :raises: CompilationError
        """

        lines: List[str] = []
        currentActionItems: List[ActionType] = []
        while not fifo.empty:
            lastItem = fifo.pop()
            if (not isinstance(lastItem, Operator)) or lastItem.isComparaison:
                currentActionItems.append(lastItem)
                continue
            lines.extend(self._operatorToAsm(lastItem, currentActionItems))
            currentActionItems = []

        if len(currentActionItems) > 0:
            raise CompilationError(
                "La séquence devrait terminer par un opérateur.",
                {"lineNumber": fifo.lineNumber})

        return lines
예제 #8
0
    def getFIFO(self, litteralDomain: Tuple[int, int]) -> ActionsFIFO:
        """Produit une file de type polonaise inversée de façon à donner
        l'ordre de calcul le plus efficace

        :param litteralDomain: bornes inf et max des littéraux acceptés dans les opérations
        :type litteralDomain: Tuple[int,int]
        :return: file de tokens, opérandes ou opérateurs
        :rtype: ActionsFIFO
        """
        return ActionsFIFO()
예제 #9
0
    def _compileNext(self, fifo:ActionsFIFO):
        """
        gère le prochaine item de la pile

        :param fifo: file de calcul en cours
        :type fifo: ActionsFIFO
        """
        item = fifo.pop()
        if isinstance(item, Variable):
            self._getNeededRegisterSpace(True)
            self._pushValue(item)
            return

        if item == Operators.SWAP:
            self._swapStackRegister()
            return

        if isinstance(item, Operator) and item.arity == 2:
            self._pushBinaryOperator(item)
            return

        if isinstance(item, Operator) and item.arity == 1:
            self._pushUnaryOperator(item)
            return

        # à ce stade c'est un littéral
        assert isinstance(item,Litteral)
        if fifo.empty:
            self._getNeededRegisterSpace(True)
            self._pushValue(item)
            return

        suivant = fifo.readNext()
        if isinstance(suivant, Operator) and suivant.isArithmetic and self._engine.litteralOperatorAvailable(suivant, item):
            if suivant.arity == 2:
                self._pushBinaryOperatorWithLitteral(suivant, item)
            else:
                self._pushUnaryOperatorWithLitteral(suivant, item)
            fifo.pop()
            return

        self._getNeededRegisterSpace(True)
        self._pushValue(item)
예제 #10
0
    def _compileNode(self, node: StructureNode,
                     registers: RegistersManager) -> ActionsFIFO:
        """Exécute la compilation pour un noeud. Le résultat est ajouté à l'objet assembleur.

        :param node: noeud à compiler
        :type node: StructureNode
        :param registers: gestionnaire de registres
        :type registers: RegostersManager
        :return: le maillon numéro de ligne, label, action fifo
        :rtype: ActionsFIFO
        """

        if isinstance(node, TransfertNode):
            expression = node.expression
            if not expression is None:
                fifo = expression.getFIFO(self._engine.litteralDomain)
                cem = CompileExpressionManager(self._engine, registers)
                actionsItem = cem.compile(fifo)
                resultRegister = registers.pop()
                cible = node.cible
                if cible is None:
                    actionsItem.append(resultRegister, Operators.PRINT)
                else:
                    actionsItem.append(resultRegister, node.cible,
                                       Operators.STORE)
            else:
                cible = node.cible
                assert not cible is None
                actionsItem = ActionsFIFO()
                actionsItem.append(node.cible, Operators.INPUT)

        elif isinstance(node, JumpNode):
            labelCible = node.cible.assignLabel()
            condition = node.getCondition()
            if condition is None:
                actionsItem = ActionsFIFO()
            else:
                fifo = condition.getFIFO(self._engine.litteralDomain)
                cem = CompileExpressionManager(self._engine, registers)
                actionsItem = cem.compile(fifo)
            actionsItem.append(labelCible, Operators.GOTO)

        elif isinstance(node, SimpleNode) and not node.operator is None:
            actionsItem = ActionsFIFO()
            actionsItem.append(node.operator)

        else:
            raise CompilationError("Noeud non reconnu",
                                   {"lineNumber": node.lineNumber})

        actionsItem.setLabel(node.label)
        actionsItem.setLineNumber(node.lineNumber)
        return actionsItem
예제 #11
0
class CompileExpressionManager:
    _registers : RegistersManager
    _actions       : ActionsFIFO

    def __init__(self, engine:ProcessorEngine, registers:RegistersManager):
        """Constructeur

        :param engine: modèle de processeur utilisé
        :type engine: ProcessorEngine
        :param registers: gestionnaire de registres
        :type registers: RegistersManager
        """
        self._engine = engine
        self._registers = registers
        self._registers.purgeStack()


    def compile(self, fifo:ActionsFIFO) -> ActionsFIFO:
        """compile la pile de calcul

        :param fifo: file de l'expression à compiler
        :type fifo: List[Union[Operator, Variable, Litteral]]
        :return: file des actions à affectuer en tenant compte des mouvements de registres
        :rtype: ActionsFIFO
        """
        self._actions = ActionsFIFO()
        while not fifo.empty:
            self._compileNext(fifo)
        return self._actions

    ### private
    def _compileNext(self, fifo:ActionsFIFO):
        """
        gère le prochaine item de la pile

        :param fifo: file de calcul en cours
        :type fifo: ActionsFIFO
        """
        item = fifo.pop()
        if isinstance(item, Variable):
            self._getNeededRegisterSpace(True)
            self._pushValue(item)
            return

        if item == Operators.SWAP:
            self._swapStackRegister()
            return

        if isinstance(item, Operator) and item.arity == 2:
            self._pushBinaryOperator(item)
            return

        if isinstance(item, Operator) and item.arity == 1:
            self._pushUnaryOperator(item)
            return

        # à ce stade c'est un littéral
        assert isinstance(item,Litteral)
        if fifo.empty:
            self._getNeededRegisterSpace(True)
            self._pushValue(item)
            return

        suivant = fifo.readNext()
        if isinstance(suivant, Operator) and suivant.isArithmetic and self._engine.litteralOperatorAvailable(suivant, item):
            if suivant.arity == 2:
                self._pushBinaryOperatorWithLitteral(suivant, item)
            else:
                self._pushUnaryOperatorWithLitteral(suivant, item)
            fifo.pop()
            return

        self._getNeededRegisterSpace(True)
        self._pushValue(item)

    def _freeRegister(self) -> Register:
        """Libère le dernier registre de la pile des registres.

        :return: le registre libéré
        :rtype: Register
        :raises: CompilationError s'il n'y a aucun registre à libérer

        .. warning:: ne doit pas tomber sur une mémoire temporaire
        """
        register: Optional[Register] = self._registers.pop()
        if register is None:
            raise CompilationError("Aucun registre à libérer.")
        assert not register.isTemp
        return register

    def _getTopStackRegister(self) -> Register:
        """
        :return: registre au sommet de la pile, sans le libérer.
        :rtype: Register
        :raises: CompilationError s'il n'y a pas d'opérande dans la pile
        .. note:: si le numéro est ``< 0``, provoque le dépilage d'un item de mémoire et retour du numéro de registre ayant accueilli le retour.
        """
        register:Optional[Register] = self._registers.readTopStack()
        if register is None:
            raise CompilationError("Pas assez d'opérande dans la pile.")
        if not register.isTemp:
            return register
        self._registers.pop()
        newRegister = self._copyMemoryToFreeRegister(register)
        self._registers.push(newRegister)
        return newRegister

    def _copyMemoryToFreeRegister(self, memoryRegister:Register) -> Register:
        """Libère la mémoire temporaire et la déplace dans un registre libre

        :param memoryRegister: registre mémoire temp à libérer
        :type memoryRegister: Register
        :return: registre destination
        :rtype: Register
        :raises: CompilationError si aucun registre destination
        """
        destinationRegister = self._registers.getFreeRegister()
        if destinationRegister is None:
            raise CompilationError("Pas de registre disponible")
        self._actions.append(memoryRegister, destinationRegister, Operators.LOAD)
        return destinationRegister

    def _swapStackRegister(self):
        """Intervertit les contenus des deux derniers étages de la pile des registres

        :raises: CompilationError s'il n'y a pas assez d'opérandes pour le swap
        """
        self._registers.swap()

    def _getAvailableRegister(self) -> Register:
        """Retourne un registre disponible.

        :return: prochain registre disponible
        :rtype: int
        :raises: CompilationError si aucune registre n'est disponible
        """
        register = self._registers.getFreeRegister()
        if register is None:
            raise CompilationError("Pas de registre disponible")
        self._registers.push(register)
        return register

    def _UALoutputIsAvailable(self) -> bool:
        """
        :return: vrai si la sortie de l'UAL est libre
        :rtype: bool
        """
        return self._registers.isFree(0) or self._engine.ualOutputIsFree() and self._registers.hasAvailables()

    def _moveBottomRegisterToMemory(self):
        """Libère le registre en bas de la pile des registres utilisés.
        Copie le contenu dans une mémoire temporaire.
        Place -1 dans la pile des registres utilisés pour indiquer le placement en mémoire temporaire.
        :raises: CompilationError si aucun registre à déplacer
        """
        indexRegisterToCopyInTemp = self._registers.getLastRegisterIndexInStack()
        if indexRegisterToCopyInTemp == -1:
            raise CompilationError("Pas de registre disponible")
        registerToCopyInTemp = self._registers.extractFromStack(indexRegisterToCopyInTemp)
        memoryRegister = self._copyRegisterToMemory(registerToCopyInTemp)
        self._registers.insertInStack(indexRegisterToCopyInTemp, memoryRegister)

    def _copyRegisterToMemory(self, sourceRegister:Register) -> Register:
        """Ajoute au code assembleur l'opération consistant à déplacer un registre vers la mémoire.
        **Ne libère pas le registre**.

        :param sourceRegister: registre à copier
        :type sourceRegister: Register
        :return: Mémoire cible
        :rtype: Registrer
        """
        memoryRegister = self._registers.getFreeTempRegister()
        self._actions.append(sourceRegister, memoryRegister, Operators.STORE)
        return memoryRegister

    def _freeZeroRegister(self):
        """Libère spécifiquement le registre 0, même s'il n'est pas au sommet de la pile.
        Aucune opération s'il est libre.
        """
        r0 = self._registers.getZeroRegister()
        index0 = self._registers.indexInStack(r0)
        if index0 < 0:
            return

        freeRegister = self._registers.getFreeRegister()
        if freeRegister is None:
            self._registers.extractFromStack(index0)
            memory = self._copyRegisterToMemory(r0)
            self._registers.insertInStack(index0, memory)
            return
        self._registers.extractFromStack(index0)
        self._registers.insertInStack(index0, freeRegister)
        self._actions.append(r0, freeRegister, Operators.MOVE)

    def _pushBinaryOperator(self, operator:Operator):
        """Ajoute une opération binaire.
        Libère les 2 registres au sommet de la pile, ajoute l'opération,
        Occupe le premier registre libre pour le résultat

        :param operator: opération parmi ``+``, ``-``, ``*``, ``/``, ``%``, ``&``, ``|``, ``^``
        :type operator: Operator
        """
        assert operator.arity == 2 and (operator.isArithmetic or operator.isComparaison)
        # Attention ici : ne pas libérer le premier registre tant que les 2e n'a pas été traité
        # -> il faut les libérer en même temps
        rLastCalc = self._getTopStackRegister()
        self._swapStackRegister()
        rFirstCalc = self._getTopStackRegister()
        self._swapStackRegister()
        self._freeRegister()
        self._freeRegister()

        if operator.isComparaison:
            self._actions.append(rFirstCalc, rLastCalc, operator)
            return
        registreDestination = self._getAvailableRegister()
        self._actions.append(rFirstCalc, rLastCalc, registreDestination, operator)

    def _pushBinaryOperatorWithLitteral(self, operator:Operator, litteral:Litteral):
        """Ajoute une opération binaire dont le 2e opérand est un littéral
        Libère le registre au sommet de la pile comme 1er opérande.
        Occupe le premier registre libre pour le résultat.

        :param operator: opération parmi ``+``, ``-``, ``*``, ``/``, ``%``, ``&``, ``|``, ``^``
        :type operator: Operator
        :param litteral: littéral
        :type litteral: Litteral
        """
        registreOperand = self._getTopStackRegister()
        self._freeRegister()
        registreDestination = self._getAvailableRegister()
        self._actions.append(registreOperand, litteral, registreDestination, operator)

    def _pushUnaryOperator(self, operator:Operator):
        """Ajoute une opération unaire
        Libère le registre au sommet de la pile comme opérande.
        Occupe le premier registre libre pour le résultat.

        :param operator: opération parmi ``~``, ``-``
        :type operator: Operator
        """
        registreOperand = self._getTopStackRegister()
        self._freeRegister()
        registreDestination = self._getAvailableRegister()
        self._actions.append(registreOperand, registreDestination, operator)

    def _pushUnaryOperatorWithLitteral(self, operator:Operator, litteral:Litteral):
        """Ajoute une opération unaire dont l'opérande est un littéral
        Occupe le premier registre libre pour le résultat

        :param operator: opération parmi ``~``, ``-``
        :type operator: Operator
        :param litteral: littéral
        :type litteral: Litteral
        """
        registreDestination = self._getAvailableRegister()
        self._actions.append(litteral, registreDestination, operator)

    def _pushValue(self, value:Union[Litteral,Variable]):
        """Charge une valeur dans le premier registre disponible

        :param value: valeur à charger
        :type value: Union[Litteral,Variable]
        """
        registreDestination = self._getAvailableRegister()
        if isinstance(value, Litteral):
            if self._engine.litteralOperatorAvailable(Operators.MOVE, value):
                self._actions.append(value, registreDestination, Operators.MOVE)
            else:
                variableFromLitteral = Variable.fromInt(value.value)
                self._actions.append(variableFromLitteral, registreDestination, Operators.LOAD)
        else:
            self._actions.append(value, registreDestination, Operators.LOAD)


    def _getNeededRegisterSpace(self, needUAL:bool):
        """Déplace des registres au besoin
        * Déplace le registre 0 s'il est nécessaire pour l'UAL.
        * Déplace le dernier registre vers la mémoire autant que possible ou nécessaire

        :param needUAL: le noeud nécessitera-t-il l'utilisation de l'UAL ?
        :type needUAL: bool
        """
        if not self._registers.hasAvailables():
            self._moveBottomRegisterToMemory()
        if needUAL and not self._UALoutputIsAvailable():
            self._freeZeroRegister()