def _recover(self, pseudo_register):
        """ Handles the logic for reversing the spilling of a register.

        :param pseudo_register: The name of the pseudo-register whose spilled value is being recovered.
        :return: A tuple containing the physical register and a list of the MIPS code required to free the physical
                 register for use.
        """

        spill_offset = self.spilled_registers.pop(pseudo_register)
        physical_register = None
        code = [
            mi.COMMENT("recovering pseudo-register {}".format(pseudo_register))
        ]

        if self.available_registers:
            physical_register = self.available_registers.pop()
            self.lru_cache[pseudo_register] = physical_register

        else:
            self.lru_cache[pseudo_register] = None
            physical_register = self._freed_physical_register

            code.extend(self._spill_code)

            self.lru_cache[pseudo_register] = physical_register

            code.append(
                mi.LW(physical_register, mi.offset_label_immediate(self.spill_mem_base_label, spill_offset)))

        self.available_spill_memory_words.append(spill_offset)

        return physical_register, code
    def _spill(self, pseudo_register, physical_register):
        """ A private method for handling the logic for spilling the contents of a physical register into memory.

        Since this method is used as a callback, it cannot return things in the traditional sense. Because of this, the
        "returned" code and physical register are set to two private class variables, so the programmer must take care
        to manage these variables appropriately.

        :param pseudo_register: The name of the pseudo-register to be spilled. Aka 'key' for the purposes of the
                                pylru.lrucache callback.
        :physical_register: The name of the physical register to be freed/reused. Aka 'value' for the purposes of the
                                pylru.lrucache callback.
        :return: None.
        """

        self._spill_code = []
        self._freed_physical_register = physical_register

        if not self.available_spill_memory_words:
            print(self)
            raise OutOfSpillMemoryException()

        spill_offset = self.available_spill_memory_words.pop()

        self.spilled_registers[pseudo_register] = spill_offset

        self._spill_code = [
            mi.COMMENT("spilling psuedo-register {} to free {}".format(pseudo_register, physical_register)),
            mi.SW(physical_register, mi.offset_label_immediate(self.spill_mem_base_label, spill_offset)),
            mi.COMMENT("spill complete")
        ]
Ejemplo n.º 3
0
    def test_spill_recovery(self):
        pseudo_registers = [INT_REGISTER_TICKETS.get() for _ in range(0, 3)]

        self.register_use_table.acquire(pseudo_registers[0])
        self.register_use_table.acquire(pseudo_registers[1])

        self.assertSequenceEqual([], self.register_use_table.available_registers, 'the registers should be used up')

        expected_result = {'register': mips.T0, 'code': [assembler.SW(mips.T0, assembler.offset_label_immediate(SPILL_MEM_LABEL, 0))]}
        result = self.register_use_table.acquire(pseudo_registers[2])
        self.assertEqual(expected_result, result)

        # recovering a spilled register's value from memory should not only get the register, but also provide the
        # mips code that swaps the values for the temporaries using the spill memory
        expected_result = {'register': mips.T1, 'code': [assembler.SW(mips.T1, assembler.offset_label_immediate(SPILL_MEM_LABEL, 4)),
                                                         assembler.LW(mips.T1, assembler.offset_label_immediate(SPILL_MEM_LABEL, 0))]}
        result = self.register_use_table.acquire(pseudo_registers[0])
        self.assertEqual(expected_result, result)
Ejemplo n.º 4
0
    def test_multiple_recoveries(self):
        spilled_1 = 'ireg_1'
        spilled_2 = 'ireg_2'
        spiller_1 = 'ireg_3'
        spiller_2 = 'ireg_4'

        self.register_use_table.acquire(spilled_1)
        self.register_use_table.acquire(spilled_2)
        self.register_use_table.acquire(spiller_1)
        self.register_use_table.acquire(spiller_2)

        self.assertTrue(
            spilled_1 in self.register_use_table.spilled_registers.keys()
            and spilled_2 in self.register_use_table.spilled_registers.keys())

        result = self.register_use_table.acquire(spilled_2)
        expected_result = {
            'register':
            mips.T0,
            'code': [
                assembler.SW(
                    mips.T0,
                    assembler.offset_label_immediate(SPILL_MEM_LABEL, 8)),
                assembler.LW(
                    mips.T0,
                    assembler.offset_label_immediate(SPILL_MEM_LABEL, 4))
            ]
        }
        self.assertEqual(expected_result, result)

        result = self.register_use_table.acquire(spilled_1)
        expected_result = {
            'register':
            mips.T1,
            'code': [
                assembler.SW(
                    mips.T1,
                    assembler.offset_label_immediate(SPILL_MEM_LABEL, 4)),
                assembler.LW(
                    mips.T1,
                    assembler.offset_label_immediate(SPILL_MEM_LABEL, 0))
            ]
        }
        self.assertEqual(expected_result, result)
Ejemplo n.º 5
0
    def test_register_spill(self):
        pseudo_registers = [INT_REGISTER_TICKETS.get() for _ in range(0, 3)]

        self.register_use_table.acquire(pseudo_registers[0])
        self.register_use_table.acquire(pseudo_registers[1])

        self.assertSequenceEqual([], self.register_use_table.available_registers, 'the registers should be used up')

        expected_result = {'register': mips.T0, 'code': [assembler.SW(mips.T0, assembler.offset_label_immediate(SPILL_MEM_LABEL, 0))]}
        result = self.register_use_table.acquire(pseudo_registers[2])
        self.assertEqual(expected_result, result)
Ejemplo n.º 6
0
    def test_spill_recovery(self):
        pseudo_registers = [INT_REGISTER_TICKETS.get() for _ in range(0, 3)]

        self.register_use_table.acquire(pseudo_registers[0])
        self.register_use_table.acquire(pseudo_registers[1])

        self.assertSequenceEqual([],
                                 self.register_use_table.available_registers,
                                 'the registers should be used up')

        expected_result = {
            'register':
            mips.T0,
            'code': [
                assembler.SW(
                    mips.T0,
                    assembler.offset_label_immediate(SPILL_MEM_LABEL, 0))
            ]
        }
        result = self.register_use_table.acquire(pseudo_registers[2])
        self.assertEqual(expected_result, result)

        # recovering a spilled register's value from memory should not only get the register, but also provide the
        # mips code that swaps the values for the temporaries using the spill memory
        expected_result = {
            'register':
            mips.T1,
            'code': [
                assembler.SW(
                    mips.T1,
                    assembler.offset_label_immediate(SPILL_MEM_LABEL, 4)),
                assembler.LW(
                    mips.T1,
                    assembler.offset_label_immediate(SPILL_MEM_LABEL, 0))
            ]
        }
        result = self.register_use_table.acquire(pseudo_registers[0])
        self.assertEqual(expected_result, result)
Ejemplo n.º 7
0
    def test_multiple_recoveries(self):
        spilled_1 = 'ireg_1'
        spilled_2 = 'ireg_2'
        spiller_1 = 'ireg_3'
        spiller_2 = 'ireg_4'

        self.register_use_table.acquire(spilled_1)
        self.register_use_table.acquire(spilled_2)
        self.register_use_table.acquire(spiller_1)
        self.register_use_table.acquire(spiller_2)

        self.assertTrue(
            spilled_1 in self.register_use_table.spilled_registers.keys() and spilled_2 in
            self.register_use_table.spilled_registers.keys())

        result = self.register_use_table.acquire(spilled_2)
        expected_result = {'register': mips.T0, 'code': [assembler.SW(mips.T0, assembler.offset_label_immediate(SPILL_MEM_LABEL, 8)),
                                                         assembler.LW(mips.T0, assembler.offset_label_immediate(SPILL_MEM_LABEL, 4))]}
        self.assertEqual(expected_result, result)

        result = self.register_use_table.acquire(spilled_1)
        expected_result = {'register': mips.T1, 'code': [assembler.SW(mips.T1, assembler.offset_label_immediate(SPILL_MEM_LABEL, 4)),
                                                         assembler.LW(mips.T1, assembler.offset_label_immediate(SPILL_MEM_LABEL, 0))]}
        self.assertEqual(expected_result, result)
Ejemplo n.º 8
0
    def test_register_spill(self):
        pseudo_registers = [INT_REGISTER_TICKETS.get() for _ in range(0, 3)]

        self.register_use_table.acquire(pseudo_registers[0])
        self.register_use_table.acquire(pseudo_registers[1])

        self.assertSequenceEqual([],
                                 self.register_use_table.available_registers,
                                 'the registers should be used up')

        expected_result = {
            'register':
            mips.T0,
            'code': [
                assembler.SW(
                    mips.T0,
                    assembler.offset_label_immediate(SPILL_MEM_LABEL, 0))
            ]
        }
        result = self.register_use_table.acquire(pseudo_registers[2])
        self.assertEqual(expected_result, result)
Ejemplo n.º 9
0

__restore_register_macro_instructions = [mi.COMMENT("brace yourself for a long, unrolled loop...")]
for __temp_register in reversed(__T_REGISTERS):
    __restore_register_macro_instructions.append(mi.ADDIU(mr.SP, mr.SP, mr.WORD_SIZE))
    __restore_register_macro_instructions.append(mi.LW(__temp_register, mi.offset_from_register_with_immediate(mr.SP)))
if config.NOT_TESTING_FUNCTIONS:
    __restore_register_macro_instructions = []
RESTORE_REGISTER_MACRO = Macro(name='RESTORE_T_REGISTERS', args=None, body=__restore_register_macro_instructions)



__save_spill_mem_macro_body = [mi.COMMENT("brace yourself for a long, unrolled loop...")]
for i in range(0, config.SPILL_MEM_SIZE, mr.WORD_SIZE):
    __save_spill_mem_macro_body.extend([
        mi.LW(mr.A3, mi.offset_label_immediate('SPILL_MEMORY', i)),
        mi.SW(mr.A3, mi.offset_from_register_with_immediate(mr.SP)),
        mi.SUBIU(mr.SP, mr.SP, mr.WORD_SIZE)
    ])
if config.NOT_TESTING_FUNCTIONS:
    __save_spill_mem_macro_body = []
SAVE_SPILL_MEM_MACRO = Macro(name='SAVE_SPILL_MEM', args=None, body=__save_spill_mem_macro_body)


__restore_spill_mem_macro_body = [mi.COMMENT("brace yourself for a long, unrolled loop...")]
for i in range(config.SPILL_MEM_SIZE - mr.WORD_SIZE, -mr.WORD_SIZE, -mr.WORD_SIZE):
    __restore_spill_mem_macro_body.extend([
        mi.ADDIU(mr.SP, mr.SP, mr.WORD_SIZE),
        mi.LW(mr.A3, mi.offset_from_register_with_immediate(mr.SP)),
        mi.SW(mr.A3, mi.offset_label_immediate('SPILL_MEMORY', i))
    ])
Ejemplo n.º 10
0
    __restore_register_macro_instructions.append(
        mi.ADDIU(mr.SP, mr.SP, mr.WORD_SIZE))
    __restore_register_macro_instructions.append(
        mi.LW(__temp_register, mi.offset_from_register_with_immediate(mr.SP)))
if config.NOT_TESTING_FUNCTIONS:
    __restore_register_macro_instructions = []
RESTORE_REGISTER_MACRO = Macro(name='RESTORE_T_REGISTERS',
                               args=None,
                               body=__restore_register_macro_instructions)

__save_spill_mem_macro_body = [
    mi.COMMENT("brace yourself for a long, unrolled loop...")
]
for i in range(0, config.SPILL_MEM_SIZE, mr.WORD_SIZE):
    __save_spill_mem_macro_body.extend([
        mi.LW(mr.A3, mi.offset_label_immediate('SPILL_MEMORY', i)),
        mi.SW(mr.A3, mi.offset_from_register_with_immediate(mr.SP)),
        mi.SUBIU(mr.SP, mr.SP, mr.WORD_SIZE)
    ])
if config.NOT_TESTING_FUNCTIONS:
    __save_spill_mem_macro_body = []
SAVE_SPILL_MEM_MACRO = Macro(name='SAVE_SPILL_MEM',
                             args=None,
                             body=__save_spill_mem_macro_body)

__restore_spill_mem_macro_body = [
    mi.COMMENT("brace yourself for a long, unrolled loop...")
]
for i in range(config.SPILL_MEM_SIZE - mr.WORD_SIZE, -mr.WORD_SIZE,
               -mr.WORD_SIZE):
    __restore_spill_mem_macro_body.extend([