summaryrefslogtreecommitdiff
path: root/Registers.py
blob: 7fac4dd0e18a24b693fba7026b0c44904cfff620 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/env python
"""Compiles C into assembly for the practicum processor (PP2)

All rights reserved, you may not redistribute or use this program without prior
permission from Peter Wu or Xander Houtman. Use of this program is entirely
your own risk. In no circumstances can the authors of this program be held
responsible for any damage including, but not limited to, financial damage or
data loss. Modification of this program is not allowed without prior
permission. The generated output (assembly and messages) are not subject to
this license.
"""

import re

__author__ = "Peter Wu"
__copyright__ = "Copyright 2011, Peter Wu"
__credits__ = ["Peter Wu"]
__license__ = "Proprietary"
__version__ = "1.0"
__maintainer__ = "Peter Wu"
__email__ = "uwretep@gmail.com"

class Registers(object):
    """Register related functions
    
    Some functions were supposed to optimize things (alloc and free), but it's
    not implemented
    """
    def __init__(self):
        # which registers are in use?
        self.registers = {}
        for reg in range(0, 8):
            self.registers[str(reg)] = False
        # the "Base Pointer", local variables are relative to this address. The
        # value of this register points to a location in the stack
        self.BP = "R5"
    def alloc(self, register=None):
        """Retrieves a register which is marked unused, marks it as in use and
        return it"""
        # if a register was explicitly requested
        if register:
            if not self.is_register(register):
                raise RuntimeError("'{}' is not a register".format(register))
            register = register[1]
            if self.registers[register]:
                raise RuntimeError("Register 'R{}' is already in use".format(register))
        else:
            for register in range(0, 5):
                register = str(register)
                # find a free register
                if not self.registers[register]:
                    break
            else:
                raise RuntimeError("No free registers are available")
        self.registers[register] = True
        return "R" + register
    def free(self, register):
        """Marks a register as unused

        Keyword arguments:
        register -- a register in the format Rn where 0 <= n < 8

        """
        # remove leading R
        register = register[1:]
        if register in self.registers:
            if self.registers[register]:
                self.registers[register] = False;
            else:
                raise RuntimeError("free() of unused register")
        else:
            raise RuntimeError("free() of invalid register")
    def next_free(self, use_register=None):
        """Mark use_register as in use, find the next free register and then
        free both.
        """
        if use_register:
            self.alloc(use_register)
        next_avail = self.alloc()
        self.free(next_avail)
        if use_register:
            self.free(use_register)
        return next_avail
    def get_register(self, text):
        """Retrieve the first register from a binary instruction"""
        text = text.strip()
        # ignore commented lines
        if text.startswith(";"):
            return None
        # skip labels if any
        text = text.upper().split(":", 1)[-1]
        # exclude the first operand, e.g. LOAD
        text = text.lstrip(" ").split(" ", 1)[-1]
        # retieve the first element before the next space, e.g. R0
        text = text.lstrip(" ").split(" ", 1)[0]
        # is it a register?
        if self.is_register(text):
            return text
        # out of luck
        return None
    def is_register(self, text):
        """Returns True if text is a register, false otherwise"""
        return len(text) == 2 and text[0] == "R" and text[1] in self.registers
    def get_instruction(self, text):
        """Retrieve the instruction from text skipping labels and comments"""
        text = text.strip()
        # ignore commented lines
        if text.startswith(";"):
            return None
        # skip labels if any
        text = text.upper().split(":", 1)[-1]
        # the first element is assumed to be an instruction
        text = text.lstrip(" ").split(" ", 1)[0]
        # instructions have a length between 2 (OR) and 4 (LOAD)
        if len(text) >= 2 and len(text) <= 4:
            return text
        return None
    def find_register(self, instructions_list, fatal=False):
        """Finds the last modified register in a list of instructions"""
        for line in reversed(instructions_list):
            reg = self.get_register(line)
            if reg:
                return reg
            else:
                instruction = self.get_instruction(line)
                # convention: non-void functions store their result value in R0
                if instruction == "BRS":
                    return "R0"
        if fatal:
            raise RuntimeError("No register found in instructions list")
        return None
    def is_register_changed(self, line, register):
        """Returns True if the register is possibly modified in the line
        
        Keyword arguments:
        line -- The instruction line to be analyzed
        register -- The register to be looked for in the line
        """
        line = line.split(";", 1)[0].strip().upper()
        register = register.upper()
        if not self.is_register(register):
            raise RuntimeError("Invalid register argument")
        # split into at most 3 elements (instruction, register, operand)
        matches = re.split("\s+", line, 2)
        if len(matches) == 2:
            instruction, reg = matches
            if (instruction == "PULL" and
                self.is_register(reg) and reg == register):
                return True
            # Assume function calls to be malicious
            if instruction == "BRS":
                return True
        elif len(matches) == 3:
            instruction, reg, operand = matches
            # remove whitespace from the operand. LF and CR do not occur
            operand = operand.translate(None, "\t ")
            if (operand.startswith("[--" + register)
                or operand.endswith(register + "++]")):
                return True
            if instruction == "STOR" and operand == register:
                return True
            # MULL and DVMD modify two registers
            if (instruction in ("MULL", "DVMD") and self.is_register(reg) and
                int(reg[1]) + 1 == int(register[1])):
                return True
            if instruction not in ("CMP", "CHCK") and reg == register:
                return True
        return False