Exemplo n.º 1
0
 def __init__(self, name):
     Instruction.count = 0
     self._listIns = []
     self._nbtmp = -1
     self._nblabel = -1
     self._dec = 0
     self._pool = TemporaryPool()
     self._stacksize = 0
     self._name = name
     # CFG Stuff - Lab 5 Only
     self._start = None
     self._end = None
Exemplo n.º 2
0
 def __init__(self):
     self._listIns = []
     self._nbtmp = -1
     self._nblabel = -1
     self._dec = -1
     self._pool = TemporaryPool()
     # CFG Stuff - Lab 5 Only
     self._start = None
     self._end = None
     self._mapin = {}  # will be map block -> set of variables
     self._mapout = {}
     self._mapdef = {}  # block : defined = killed vars in the block
     self._igraph = None  # interference graph
Exemplo n.º 3
0
class SARUMANProg:
    """Representation of a SARUMAN program (i.e. list of
    instructions)."""
    def __init__(self):
        Instruction.count = 0
        self._listIns = []
        self._nbtmp = -1
        self._nblabel = -1
        self._dec = -1
        self._pool = TemporaryPool()
        # CFG Stuff - Lab 5 Only
        self._start = None
        self._end = None
        self._mapin = {}  # will be map block -> set of variables
        self._mapout = {}
        self._mapdef = {}  # block : defined = killed vars in the block
        self._igraph = None  # interference graph

    def add_edge(self, src, dest):
        dest._in.append(src)
        src._out.append(dest)

    def add_instruction(self, i, linkwithsucc=True):
        """Utility function to add an instruction in the program.
        in Lab 4, only add at the end of the instruction list (_listIns)
        in Lab 5, will be used to also add in the CFG structure.
        """
        if not self._listIns:  # empty list: empty prg
            i._isnotStart = False
            self._start = i
        else:
            if self._end is not None:
                self.add_edge(self._end, i)
        self._end = i
        if not linkwithsucc:
            self._end = None
        self._listIns.append(i)
        return i

    def iter_instructions(self, f):
        """Iterate over instructions.

        For each real instruction (not label or comment), call f,
        which must return either None or a list of instruction. If it
        returns None, nothing happens. If it returns a list, then the
        instruction is replaced by this list.

        """
        i = 0
        while i < len(self._listIns):
            old_i = self._listIns[i]
            if not old_i.is_instruction():
                i += 1
                continue
            new_i_list = f(old_i)
            if new_i_list is None:
                i += 1
                continue
            del self._listIns[i]
            self._listIns.insert(i, Comment(str(old_i)))
            i += 1
            for new_i in new_i_list:
                self._listIns.insert(i, new_i)
                i += 1
            self._listIns.insert(i, Comment("end " + str(old_i)))
            i += 1

    def get_instructions(self):
        return self._listIns

    def new_location(self, three_addr):
        if three_addr:
            return self.new_tmp()
        else:
            return self.new_offset()

    def new_tmp(self):
        """
        Return a new fresh temporary (temp)
        + add in list
        """
        return self._pool.new_tmp()

    def printTempList(self):
        print(self._pool._all_temps)

    def new_offset(self, base):
        """
        Return a new offset in the memory stack
        """
        self._dec = self._dec + 1
        return Offset(base, self._dec)

    def new_label(self, name):
        """
        Return a new label
        """
        self._nblabel = self._nblabel + 1
        return Label(name + "_" + str(self._nblabel))

    def new_label_while(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_while_begin_" + str(self._nblabel)),
                Label("l_while_end_" + str(self._nblabel)))

    def new_label_cond(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_cond_neg_" + str(self._nblabel)),
                Label("l_cond_end_" + str(self._nblabel)))

    def new_label_if(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_if_false_" + str(self._nblabel)),
                Label("l_if_end_" + str(self._nblabel)))

    # each instruction has its own "add in list" version
    def addLabel(self, s):
        return self.add_instruction(s)

    def addComment(self, s):
        self.add_instruction(Comment(s))

    def addInstructionPRINT(self, expr):
        """Print integer value, followed by newline."""
        # a print instruction generates the temp it prints.
        ins = Instru3A("print signed", expr)
        if isinstance(expr, Temporary):
            # tests if the temp prints a temporary
            ins._gen.add(expr)
        self.add_instruction(ins)
        self.add_instruction(Instru3A("print char", Char("'\\n'")))

    # test and cond jumps.
    def addInstructionJUMP(self, label):
        assert isinstance(label, Label)
        i = Instru3A("jump", label)
        # add in list but do not link with the following node
        self.add_instruction(i, linkwithsucc=False)
        self.add_edge(i, label)
        return i

    # Useful meta instruction for conditional jump
    def addInstructionCondJUMP(self, label, op1, c, op2):
        assert isinstance(label, Label)
        assert isinstance(c, Condition)
        ins = Instru3A("cond_jump", args=[label, op1, c, op2])
        if isinstance(op1, Temporary):
            ins._gen.add(op1)
        if isinstance(op2, Temporary):
            ins._gen.add(op2)
        self.add_instruction(ins)
        self.add_edge(ins, label)
        return ins

    def addInstructionADD(self, dr, sr1, sr2orimm7):
        if isinstance(sr2orimm7, Immediate):
            ins = Instru3A("add3i", dr, sr1, sr2orimm7)
        else:
            ins = Instru3A("add3", dr, sr1, sr2orimm7)
        self.addGenKillSr2orimm(ins, dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    def addInstructionSUB(self, dr, sr1, sr2orimm7):
        if isinstance(sr2orimm7, Immediate):
            ins = Instru3A("sub3i", dr, sr1, sr2orimm7)
        else:
            ins = Instru3A("sub3", dr, sr1, sr2orimm7)
        self.addGenKillSr2orimm(ins, dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    def addInstructionAND(self, dr, sr1, sr2orimm7):
        ins = Instru3A("and3", dr, sr1, sr2orimm7)
        self.addGenKillSr2orimm(ins, dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    def addInstructionOR(self, dr, sr1, sr2orimm7):
        ins = Instru3A("or3", dr, sr1, sr2orimm7)
        self.addGenKillSr2orimm(ins, dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    # Copy values (immediate or in register)
    def addInstructionLETI(self, dr, imm7):
        ins = Instru3A("leti", dr, imm7)
        ins._kill.add(dr)
        self.add_instruction(ins)

    def addInstructionLET(self, dr, sr):
        ins = Instru3A("let", dr, sr)
        self.addGenKillSr2orimm(ins, dr, sr)
        self.add_instruction(ins)

    def addInstructionRMEM(self, dr, sr):
        if isinstance(sr, Register):
            # Accept plain register where we expect an indirect
            # addressing mode.
            sr = Indirect(sr)
        ins = Instru3A("rmem", dr, sr)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    def addInstructionWMEM(self, dr, sr):
        if isinstance(sr, Register):
            # Accept plain register where we expect an indirect
            # addressing mode.
            sr = Indirect(sr)
        ins = Instru3A("wmem", dr, sr)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    def addGenKillSr2orimm(self, ins, dr, sr, sr2orimm7=None):
        ins._kill.add(dr)
        ins._gen.add(sr)
        if isinstance(sr2orimm7, Temporary):
            ins._gen.add(sr2orimm7)
        #Remove occurence of kill that we found in gen
        ins._kill = ins._kill.difference(ins._gen)

    # Allocation functions
    def naive_alloc(self):
        """ Allocate all temporaries to registers.
        Fail if there are too many temporaries."""
        regs = list(GP_REGS)  # Get a writable copy
        reg_allocation = dict()
        for tmp in self._pool._all_temps:
            try:
                reg = regs.pop()
            except IndexError:
                raise AllocationError(
                    "Too many temporaries ({}) for the naive allocation, sorry"
                    .format(len(self._pool._all_temps)))
            reg_allocation[tmp] = reg
        self._pool.set_reg_allocation(reg_allocation)
        self.iter_instructions(replace_reg)

    def alloc_to_mem(self):
        """Allocate all temporaries to memory. Hypothesis:
        - Expanded instructions can use r0 (to compute addresses), r1 and
        r2 (to store the values of temporaries before the actual
        instruction).
        """
        self._pool.set_reg_allocation(
            {temp: self.new_offset(SP)
             for temp in self._pool._all_temps})
        self.iter_instructions(replace_mem)

    def do_smart_alloc(self, basename, debug=False):
        """Perform all steps related to smart register allocation:

        - Dataflow analysis to compute liveness range of each
          temporary.

        - Interference graph construction

        - Graph coloring

        - Substitution of temporaries by actual locations in the
          3-address code.
        """
        # prints the CFG as a dot file
        if debug:
            self.printDot(basename + ".dot")
            print("CFG generated in " + basename + ".dot.pdf")

        # TODO: Move the print&return statements below down as you progress
        # TODO: in the lab. They must be removed from the final version.
        #print("do_smart_alloc: stopping here for now")
        #return

        # dataflow
        if debug:
            self.printGenKill()

        mapin, mapout = self.doDataflow()
        if debug:
            self.printMapInOut()

        # conflict graph
        igraph = self.doInterfGraph()

        if debug:
            print("printing the conflict graph")
            igraph.print_dot(basename + "_conflicts.dot")

        # Smart Alloc via graph coloring
        self.smart_alloc(debug, basename + "_colored.dot")

    def smart_alloc(self, debug, outputname):
        """Allocate all temporaries with graph coloring
        also prints the colored graph if debug.

        Precondition: the interference graph (_igraph) must have been
        initialized.
        """
        if not self._igraph:
            raise Exception("hum, the interference graph seems to be empty")
        # Temporary -> Operand (register or offset) dictionary,
        # specifying where a given Temporary should be allocated:
        alloc_dict = {temp: None for temp in self._pool._all_temps}

        def replaceValueDictOfTemporary(key_str, value):
            for key in alloc_dict.keys():
                if str(key) == key_str:
                    alloc_dict[key] = value

        # TODO :  color the graph with appropriate nb of colors,
        # and get back the (partial) coloring (see Libgraphes.py)
        # if appropriate, relaunch the coloring for spilled variables.
        # Then, construct a dict register -> Register or Offset.
        # My version is 27 lines including debug log.
        # Be careful, the registers names in the graph are now strings,
        # at some point there should be an explicit
        # str_temp = str(temp) conversion before accessing the associated color.

        coloring, is_total, colored_nodes = self._igraph.color(6, [])

        for node in colored_nodes:
            #alloc_dict[node] = GP_REGS[int(coloring[node])]
            replaceValueDictOfTemporary(node, GP_REGS[int(coloring[node])])

        if not is_total:
            nbNotColored = len(self._igraph.vertices()) - len(colored_nodes)
            # We recolor only not colored nodes
            coloring, is_total, colored_nodes = self._igraph.color(
                nbNotColored, colored_nodes)
            assert is_total is True
            assert nbNotColored == len(colored_nodes)
            # We add them as offset
            for node in colored_nodes:
                #alloc_dict[node] = Offset(SP, int(coloring[node]))
                replaceValueDictOfTemporary(node,
                                            Offset(SP, int(coloring[node])))

        self._pool.set_reg_allocation(alloc_dict)
        self.iter_instructions(replace_smart)

    def printGenKill(self):
        print("Dataflow Analysis, Initialisation")
        i = 0
        # this should be an iterator
        while i < len(self._listIns):
            self._listIns[i].printGenKill()
            i += 1

    def printMapInOut(self):  # Prints in/out sets, useful for debug!
        print("In: {" + ", ".join(
            str(x) + ": " + regset_to_string(self._mapin[x])
            for x in self._mapin.keys()) + "}")
        print("Out: {" + ", ".join(
            str(x) + ": " + regset_to_string(self._mapout[x])
            for x in self._mapout.keys()) + "}")

    def doDataflow(self):
        print("Dataflow Analysis")
        countit = 0
        # initialisation of all mapout,mapin sets, and def = kill
        for i in range(len(self._listIns)):
            self._mapin[i] = set()
            self._mapout[i] = set()
        stable = False
        while not stable:
            # Iterate until fixpoint :
            # make calls to self._start.do_dataflow_onestep (in Instruction3A.py)

            countit = countit + 1
            saveoldout = copy.copy(self._mapout)
            saveoldin = copy.copy(self._mapin)  # structure copy
            self._start.do_dataflow_onestep(self._mapin, self._mapout, set())
            stable = True
            for i in range(len(self._listIns)):
                # print("considering node number "+str(i))
                # sets are growing.
                # print (self._mapout[i])
                if (self._mapout[i] > saveoldout[i]
                        or self._mapin[i] > saveoldin[i]):
                    stable = False
        print("finished in " + str(countit) + " iterations")
        # print(self._mapin)
        # print(self._mapout)

        return (self._mapin, self._mapout)

    def doInterfGraph(self):
        self._start.update_defmap(self._mapdef, set())
        self._igraph = Graph()
        # self.printTempList()
        if not self._mapout and not self._mapin:
            raise Exception("hum, dataflow sets need to be initialised")
        else:
            t = self._pool._all_temps
            for v in t:
                # print("add vertex "+str(v))
                self._igraph.add_vertex(str(v))
            tpairs = [(p1, p2) for p1 in t for p2 in t]
            # print(tpairs)
            for (t1, t2) in tpairs:
                if interfere(t1, t2, self._mapout, self._mapdef):
                    # print("add edge "+str(t1)+" - "+str(t2))
                    self._igraph.add_edge((str(t1), str(t2)))

        return (self._igraph)

    # Dump code
    def printCode(self, filename, comment=None):
        # dump generated code on stdout or file.
        output = open(filename, 'w') if filename else sys.stdout
        output.write(
            ";;Automatically generated TARGET code, MIF08 & CAP 2018\n")
        if comment is not None:
            output.write(";;{} version\n".format(comment))
        # Stack in SARUMAN is managed with SP
        for i in self._listIns:
            i.printIns(output)
        output.write("\n\n;;postlude\n")
        output.write("end:\n	jump end\n")
        if output is not sys.stdout:
            output.close()

    def printDot(self, filename, view=False):
        # Only used in Lab 5
        graph = nx.DiGraph()
        self._start.printDot(graph, set())
        graph.graph['graph'] = dict()
        graph.graph['graph']['overlap'] = 'false'
        nx.drawing.nx_agraph.write_dot(graph, filename)
        gz.render('dot', 'pdf', filename)
        if view:
            gz.view(filename + '.pdf')
Exemplo n.º 4
0
class TARGET18Prog:
    """Representation of a TARGET18 program (i.e. list of
    instructions)."""
    def __init__(self):
        Instruction.count = 0
        self._listIns = []
        self._nbtmp = -1
        self._nblabel = -1
        self._dec = -1
        self._pool = TemporaryPool()
        # CFG Stuff - Lab 5 Only
        self._start = None
        self._end = None
        self._mapin = {}  # will be map block -> set of variables
        self._mapout = {}
        self._mapdef = {}  # block : defined = killed vars in the block
        self._igraph = None  # interference graph

    def add_edge(self, src, dest):
        dest._in.append(src)
        src._out.append(dest)

    def add_instruction(self, i, linkwithsucc=True):
        """Utility function to add an instruction in the program.
        in Lab 4, only add at the end of the instruction list (_listIns)
        in Lab 5, will be used to also add in the CFG structure.
        """
        if not self._listIns:  # empty list: empty prg
            i._isnotStart = False
            self._start = i
        else:
            if self._end is not None:
                self.add_edge(self._end, i)
        self._end = i
        if not linkwithsucc:
            self._end = None
        self._listIns.append(i)
        return i

    def iter_instructions(self, f):
        """Iterate over instructions.

        For each real instruction (not label or comment), call f,
        which must return either None or a list of instruction. If it
        returns None, nothing happens. If it returns a list, then the
        instruction is replaced by this list.

        """
        i = 0
        while i < len(self._listIns):
            old_i = self._listIns[i]
            if not old_i.is_instruction():
                i += 1
                continue
            new_i_list = f(old_i)
            if new_i_list is None:
                i += 1
                continue
            del self._listIns[i]
            self._listIns.insert(i, Comment(str(old_i)))
            i += 1
            for new_i in new_i_list:
                self._listIns.insert(i, new_i)
                i += 1
            self._listIns.insert(i, Comment("end " + str(old_i)))
            i += 1

    def get_instructions(self):
        return self._listIns

    def new_location(self, three_addr):
        if three_addr:
            return self.new_tmp()
        else:
            return self.new_offset()

    def new_tmp(self):
        """
        Return a new fresh temporary (temp)
        + add in list
        """
        return self._pool.new_tmp()

    def printTempList(self):
        print(self._pool._all_temps)

    def new_offset(self, base):
        """
        Return a new offset in the memory stack
        """
        self._dec = self._dec + 1
        return Offset(base, self._dec)

    def new_label(self, name):
        """
        Return a new label
        """
        self._nblabel = self._nblabel + 1
        return Label(name + "_" + str(self._nblabel))

    def new_label_while(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_while_begin_" + str(self._nblabel)),
                Label("l_while_end_" + str(self._nblabel)))

    def new_label_cond(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_cond_neg_" + str(self._nblabel)),
                Label("l_cond_end_" + str(self._nblabel)))

    def new_label_if(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_if_false_" + str(self._nblabel)),
                Label("l_if_end_" + str(self._nblabel)))

    # each instruction has its own "add in list" version
    def addLabel(self, s):
        return self.add_instruction(s)

    def addComment(self, s):
        self.add_instruction(Comment(s))

    def addInstructionPRINT(self, expr):
        # a print instruction generates the temp it prints.
        ins = Instru3A("print signed", expr)
        if isinstance(expr, Temporary):
            # tests if the temp prints a temporary
            ins._gen.add(expr)
        self.add_instruction(ins)
        self.add_instruction(Instru3A("print char", Char("'\\n'")))

    # test and cond jumps.
    def addInstructionJUMP(self, label):
        assert isinstance(label, Label)
        i = Instru3A("jump", label)
        # TODO: properly build the CFG: don't chain with next
        # TODO: instruction to add, but with the target of the jump.
        self.add_instruction(i)
        return i

    # Useful meta instruction for conditional jump
    def addInstructionCondJUMP(self, label, op1, c, op2):
        assert isinstance(label, Label)
        assert isinstance(c, Condition)
        ins = Instru3A("cond_jump", args=[label, op1, c, op2])
        # TODO ADD GEN KILL INIT IF REQUIRED
        # TODO: properly build the CFG: chain with both the next
        # TODO: instruction to be added and with the target of the jump.
        self.add_instruction(ins)
        return ins

    def addInstructionADD(self, dr, sr1, sr2orimm7):
        if isinstance(sr2orimm7, Immediate):
            ins = Instru3A("add3i", dr, sr1, sr2orimm7)
        else:
            ins = Instru3A("add3", dr, sr1, sr2orimm7)
        # Tip : at some point you should use isinstance(..., Temporary)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    def addInstructionSUB(self, dr, sr1, sr2orimm7):
        if isinstance(sr2orimm7, Immediate):
            ins = Instru3A("sub3i", dr, sr1, sr2orimm7)
        else:
            ins = Instru3A("sub3", dr, sr1, sr2orimm7)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    def addInstructionAND(self, dr, sr1, sr2orimm7):
        ins = Instru3A("and3", dr, sr1, sr2orimm7)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    def addInstructionOR(self, dr, sr1, sr2orimm7):
        ins = Instru3A("or3", dr, sr1, sr2orimm7)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    # Copy values (immediate or in register)
    def addInstructionLETI(self, dr, imm7):
        ins = Instru3A("leti", dr, imm7)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    def addInstructionLET(self, dr, sr):
        ins = Instru3A("let", dr, sr)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    def addInstructionRMEM(self, dr, sr):
        if isinstance(sr, Register):
            # Accept plain register where we expect an indirect
            # addressing mode.
            sr = Indirect(sr)
        ins = Instru3A("rmem", dr, sr)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    def addInstructionWMEM(self, dr, sr):
        if isinstance(sr, Register):
            # Accept plain register where we expect an indirect
            # addressing mode.
            sr = Indirect(sr)
        ins = Instru3A("wmem", dr, sr)
        # TODO ADD GEN KILL INIT IF REQUIRED
        self.add_instruction(ins)

    # Allocation functions
    def naive_alloc(self):
        """ Allocate all temporaries to registers.
        Fail if there are too many temporaries."""
        regs = list(GP_REGS)  # Get a writable copy
        reg_allocation = dict()
        for tmp in self._pool._all_temps:
            try:
                reg = regs.pop()
            except IndexError:
                raise AllocationError(
                    "Too many temporaries ({}) for the naive allocation, sorry"
                    .format(len(self._pool._all_temps)))
            reg_allocation[tmp] = reg
        self._pool.set_reg_allocation(reg_allocation)
        self.iter_instructions(replace_reg)

    def alloc_to_mem(self):
        """Allocate all temporaries to memory. Hypothesis:
        - Expanded instructions can use r0 (to compute addresses), r1 and
        r2 (to store the values of temporaries before the actual
        instruction).
        """
        self._pool.set_reg_allocation(
            {temp: self.new_offset(SP)
             for temp in self._pool._all_temps})
        self.iter_instructions(replace_mem)

    def smart_alloc(self, debug, outputname):
        """ Allocate all temporaries with graph coloring
        also prints the colored graph if debug
        """
        if not self._igraph:
            raise Exception("hum, the interference graph seems to be empty")
        # Temporary -> Operand (register or offset) dictionary,
        # specifying where a given Temporary should be allocated:
        alloc_dict = {}
        # TODO :  color the graph with appropriate nb of colors,
        # and get back the (partial) coloring (see Libgraphes.py)
        # if appropriate, relaunch the coloring for spilled variables.
        # Then, construct a dict register -> Register or Offset.
        # My version is 27 lines including debug log.
        # Be careful, the registers names in the graph are now strings,
        # at some point there should be an explicit
        # str_temp = str(temp) conversion before accessing the associated color.
        # TODO !
        self._pool.set_reg_allocation(alloc_dict)
        self.iter_instructions(replace_smart)

    def printGenKill(self):
        print("Dataflow Analysis, Initialisation")
        i = 0
        # this should be an iterator
        while i < len(self._listIns):
            self._listIns[i].printGenKill()
            i += 1

    def printMapInOut(self):  # Prints in/out sets, useful for debug!
        print("In: {" + ", ".join(
            str(x) + ": " + regset_to_string(self._mapin[x])
            for x in self._mapin.keys()) + "}")
        print("Out: {" + ", ".join(
            str(x) + ": " + regset_to_string(self._mapout[x])
            for x in self._mapout.keys()) + "}")

    def doDataflow(self):
        print("Dataflow Analysis")
        countit = 0
        # initialisation of all mapout,mapin sets, and def = kill
        for i in range(len(self._listIns)):
            self._mapin[i] = set()
            self._mapout[i] = set()
        stable = False
        while not stable:
            # Iterate until fixpoint :
            # make calls to self._start.do_dataflow_onestep (in Instruction3A.py)
            stable = True  # CHANGE
        # TODO ! (perform iterations until fixpoint).
        return (self._mapin, self._mapout)

    def doInterfGraph(self):
        self._start.update_defmap(self._mapdef, set())
        self._igraph = Graph()
        # self.printTempList()
        if not self._mapout and not self._mapin:
            raise Exception("hum, dataflow sets need to be initialised")
        else:
            t = self._pool._all_temps
            # TODO !
        return (self._igraph)

    # Dump code
    def printCode(self, filename, comment=None):
        # dump generated code on stdout or file.
        output = open(filename, 'w') if filename else sys.stdout
        output.write(
            ";;Automatically generated TARGET code, MIF08 & CAP 2018\n")
        if comment is not None:
            output.write(";;{} version\n".format(comment))
        # Stack in TARGET18 is managed with SP
        for i in self._listIns:
            i.printIns(output)
        output.write("\n\n;;postlude\n")
        output.write("end:\n	jump end\n")
        if output is not sys.stdout:
            output.close()

    def printDot(self, filename, view=False):
        # Only used in Lab 5
        graph = nx.DiGraph()
        self._start.printDot(graph, set())
        graph.graph['graph'] = dict()
        graph.graph['graph']['overlap'] = 'false'
        nx.drawing.nx_agraph.write_dot(graph, filename)
        gz.render('dot', 'pdf', filename)
        if view:
            gz.view(filename + '.pdf')
Exemplo n.º 5
0
class RiscVFunction:
    """Representation of a RiscV program (i.e. list of
    instructions)."""
    def __init__(self, name):
        Instruction.count = 0
        self._listIns = []
        self._nbtmp = -1
        self._nblabel = -1
        self._dec = 0
        self._pool = TemporaryPool()
        self._stacksize = 0
        self._name = name
        # CFG Stuff - Lab 5 Only
        self._start = None
        self._end = None

    def add_edge(self, src, dest):
        dest._in.append(src)
        src._out.append(dest)

    def add_instruction(self, i, linkwithsucc=True):
        """Utility function to add an instruction in the program.
        in Lab 4, only add at the end of the instruction list (_listIns)
        in Lab 5, will be used to also add in the CFG structure.
        """
        if not self._listIns:  # empty list: empty prg
            self._start = i
        else:
            if self._end is not None:
                self.add_edge(self._end, i)
        self._end = i
        if not linkwithsucc:
            self._end = None
        self._listIns.append(i)
        return i

    def iter_instructions(self, f):
        """Iterate over instructions.

        For each real instruction (not label or comment), call f,
        which must return either None or a list of instruction. If it
        returns None, nothing happens. If it returns a list, then the
        instruction is replaced by this list.

        """
        i = 0
        while i < len(self._listIns):
            old_i = self._listIns[i]
            if not old_i.is_instruction():
                i += 1
                continue
            new_i_list = f(old_i)
            if new_i_list is None:
                i += 1
                continue
            del self._listIns[i]
            self._listIns.insert(i, Comment(str(old_i)))
            i += 1
            for new_i in new_i_list:
                self._listIns.insert(i, new_i)
                i += 1
            self._listIns.insert(i, Comment("end " + str(old_i)))
            i += 1

    def get_instructions(self):
        return self._listIns

    def new_tmp(self):
        """
        Return a new fresh temporary (temp)
        + add in list
        """
        return self._pool.new_tmp()

    def new_offset(self, base):
        """
        Return a new offset in the memory stack
        """
        self._dec = self._dec + 1
        return Offset(base, -8 * self._dec)

    def get_offset(self):
        return self._dec

    def get_stacksize(self):
        return self._stacksize

    def new_label(self, name):
        """
        Return a new label
        """
        self._nblabel = self._nblabel + 1
        return Label(name + "_" + str(self._nblabel))

    def new_label_while(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_while_begin_" + str(self._nblabel)),
                Label("l_while_end_" + str(self._nblabel)))

    def new_label_cond(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_cond_neg_" + str(self._nblabel)),
                Label("l_cond_end_" + str(self._nblabel)))

    # each instruction has its own "add in list" version
    def addLabel(self, s):
        return self.add_instruction(s)

    def addComment(self, s):
        self.add_instruction(Comment(s))

    def addInstructionPRINTINT(self, reg):
        """Print integer value, with newline. (see Expand)"""
        # a print instruction generates the temp it prints.
        ins = Instru3A("mv", A0, reg)
        self.add_instruction(ins)
        self.addInstructionCALL('println_int')

    # Other printing instructions are not implemented.

    def addInstructionCALL(self, function):
        if isinstance(function, str):
            function = Function(function)
        assert isinstance(function, Function)
        self.add_instruction(Instru3A('call', function))

    # Unconditional jump to label.
    def addInstructionJUMP(self, label):
        assert isinstance(label, Label)
        i = Instru3A("j", label)
        # add in list but do not link with the following node
        self.add_instruction(i, linkwithsucc=False)
        self.add_edge(i, label)
        return i

    # Conditional jump
    def addInstructionCondJUMP(self, label, op1, c, op2):
        assert isinstance(label, Label)
        assert isinstance(c, Condition)
        if op2 != 0:
            ins = Instru3A(c.__str__(), op1, op2, label)
        else:
            ins = Instru3A(c.__str__(), op1, ZERO, label)
        self.add_instruction(ins)
        self.add_edge(ins, label)
        return ins

    def addInstructionADD(self, dr, sr1, sr2orimm7):
        if isinstance(sr2orimm7, Immediate):
            ins = Instru3A("addi", dr, sr1, sr2orimm7)
        else:
            ins = Instru3A("add", dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    def addInstructionMUL(self, dr, sr1, sr2orimm7):
        if isinstance(sr2orimm7, Immediate):
            raise Exception("Cant multiply by an immediate")
        else:
            ins = Instru3A("mul", dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    def addInstructionDIV(self, dr, sr1, sr2orimm7):
        if isinstance(sr2orimm7, Immediate):
            raise Exception("Cant divide by an immediate")
        else:
            ins = Instru3A("div", dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    def addInstructionREM(self, dr, sr1, sr2orimm7):
        if isinstance(sr2orimm7, Immediate):
            raise Exception("Cant divide by an immediate")
        else:
            ins = Instru3A("rem", dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    def addInstructionNOT(self, dr, sr):
        ins = Instru3A("not", dr, sr)
        self.add_instruction(ins)

    def addInstructionSUB(self, dr, sr1, sr2orimm7):
        assert not isinstance(sr2orimm7, Immediate)
        ins = Instru3A("sub", dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    def addInstructionAND(self, dr, sr1, sr2orimm7):
        ins = Instru3A("and", dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    def addInstructionOR(self, dr, sr1, sr2orimm7):
        ins = Instru3A("or", dr, sr1, sr2orimm7)
        self.add_instruction(ins)

    # Copy values (immediate or in register)
    def addInstructionLI(self, dr, imm7):
        ins = Instru3A("li", dr, imm7)
        self.add_instruction(ins)

    def addInstructionMV(self, dr, sr):
        ins = Instru3A("mv", dr, sr)
        self.add_instruction(ins)

    def addInstructionLD(self, dr, mem):
        ins = Instru3A("ld", dr, mem)
        self.add_instruction(ins)

    def addInstructionSD(self, sr, mem):
        ins = Instru3A("sd", sr, mem)
        self.add_instruction(ins)

    # Dump code
    def printCode(self, output, comment=None):
        # compute size for the local stack - do not forget to align by 16
        fo = self.get_stacksize()  # allocate enough memory for stack
        cardoffset = 8 * (fo + (0 if fo % 2 == 0 else 1)) + 16
        output.write(
            "##Automatically generated RISCV code, MIF08 & CAP 2019\n")
        if comment is not None:
            output.write("##{} version\n".format(comment))
        output.write("\n\n##prelude\n")
        output.write("""
        .text
        .globl {0}
{0}:
        addi sp, sp, -{1}
        sd ra, 0(sp)
        sd fp, 8(sp)
        addi fp, sp, {1}
        """.format(self._name, cardoffset))
        # Stack in RiscV is managed with SP
        output.write("\n\n##Generated Code\n")
        for i in self._listIns:
            i.printIns(output)
        output.write("\n\n##postlude\n")
        output.write("""
        ld ra, 0(sp)
        ld fp, 8(sp)
        addi sp, sp, {}
        ret
""".format(cardoffset))

    def printDot(self, filename, view=False):
        # Only used in Lab 5
        graph = nx.DiGraph()
        names = dict()
        for i, instruction in enumerate(self._listIns):
            names[instruction] = str(i) + "_" + str(instruction)
        # nodes
        for instruction in self._listIns:
            graph.add_node(names[instruction])
        # edges
        for instruction in self._listIns:
            for child in instruction._out:
                graph.add_edge(names[instruction], names[child])
        # Add a fake "START" node to mark the entry of the CFG
        if i == 0:
            graph.add_node("START")
            graph.add_edge("START", names[self._start])
        graph.graph['graph'] = dict()
        graph.graph['graph']['overlap'] = 'false'
        nx.drawing.nx_agraph.write_dot(graph, filename)
        gz.render('dot', 'pdf', filename)
        if view:
            gz.view(filename + '.pdf')
Exemplo n.º 6
0
class TARGET18Prog:
    """Representation of a TARGET18 program (i.e. list of
    instructions)."""
    def __init__(self):
        self._listIns = []
        self._nbtmp = -1
        self._nblabel = -1
        self._dec = -1
        self._pool = TemporaryPool()
        # CFG Stuff - Lab 5 Only
        self._start = None
        self._end = None
        self._mapin = {}  # will be map block -> set of variables
        self._mapout = {}
        self._mapdef = {}  # block : defined = killed vars in the block
        self._igraph = None  # interference graph

    def add_edge(self, src, dest):
        dest._in.append(src)
        src._out.append(dest)

    def add_instruction(self, i, linkwithsucc=True):
        """Utility function to add an instruction in the program.
        in Lab 4, only add at the end of the instruction list (_listIns)
        in Lab 5, will be used to also add in the CFG structure.
        """
        if not self._listIns:  # empty list: empty prg
            i._isnotStart = False
            self._start = i
            self._end = i
        else:
            if self._end is not None:
                self._end._out.append(i)
                i._in.append(self._end)
            self._end = i
            if not linkwithsucc:
                self._end = None
        self._listIns.append(i)
        return i

    def iter_instructions(self, f):
        """Iterate over instructions.

        For each real instruction (not label or comment), call f,
        which must return either None or a list of instruction. If it
        returns None, nothing happens. If it returns a list, then the
        instruction is replaced by this list.

        """
        i = 0
        while i < len(self._listIns):
            old_i = self._listIns[i]
            if not old_i.is_instruction():
                i += 1
                continue
            new_i_list = f(old_i)
            if new_i_list is None:
                i += 1
                continue
            del self._listIns[i]
            self._listIns.insert(i, Comment(str(old_i)))
            i += 1
            for new_i in new_i_list:
                self._listIns.insert(i, new_i)
                i += 1
            self._listIns.insert(i, Comment("end " + str(old_i)))
            i += 1

    def get_instructions(self):
        return self._listIns

    def new_location(self, three_addr):
        if three_addr:
            return self.new_tmp()
        else:
            return self.new_offset()

    def new_tmp(self):
        """
        Return a new fresh temporary (temp)
        """
        return self._pool.new_tmp()

    def new_offset(self, base):
        """
        Return a new offset in the memory stack
        """
        self._dec = self._dec + 1
        return Offset(base, self._dec)

    def new_label(self, name):
        """
        Return a new label
        """
        self._nblabel = self._nblabel + 1
        return Label(name + "_" + str(self._nblabel))

    def new_label_while(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_while_begin_" + str(self._nblabel)),
                Label("l_while_end_" + str(self._nblabel)))

    def new_label_cond(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_cond_neg_" + str(self._nblabel)),
                Label("l_cond_end_" + str(self._nblabel)))

    def new_label_if(self):
        self._nblabel = self._nblabel + 1
        return (Label("l_if_false_" + str(self._nblabel)),
                Label("l_if_end_" + str(self._nblabel)))

    # each instruction has its own "add in list" version
    def addLabel(self, s):
        return self.add_instruction(s)

    def addComment(self, s):
        self.add_instruction(Comment(s))

    # print instruction  TODO attention ça ne marche que pour des INT
    def addInstructionPRINT(self, expr):
        self.add_instruction(Instru3A("print signed", expr))
        self.add_instruction(Instru3A("print char", Char("'\\n'")))

    # test and cond jumps.
    def addInstructionJUMP(self, label):
        assert isinstance(label, Label)
        i = Instru3A("jump", label)
        self.add_instruction(i, False)
        # add in list but do not link with the following node
        self.add_edge(i, label)
        return i

    # TODO : regarder ce qu'on fait avec le JUMPI

    # Useful meta instruction for conditional jump
    def addInstructionCondJUMP(self, label, op1, c, op2):
        assert isinstance(label, Label)
        assert isinstance(c, Condition)
        i = Instru3A("cond_jump", args=[label, op1, c, op2])
        self.add_instruction(i)
        self.add_edge(i, label)  # useful in next lab
        return i

    # Arithmetic instructions #TODO complete
    def addInstructionADD(self, dr, sr1, sr2orimm7):  # add avec 2 ops.
        if isinstance(sr2orimm7, Immediate):
            self.add_instruction(
                Instru3A("add3i", dr, sr1, sr2orimm7))
        else:
            self.add_instruction(
                Instru3A("add3", dr, sr1, sr2orimm7))

    def addInstructionSUB(self, dr, sr1, sr2orimm7):
        if isinstance(sr2orimm7, Immediate):
            self.add_instruction(
                Instru3A("sub3i", dr, sr1, sr2orimm7))
        else:
            self.add_instruction(
                Instru3A("sub3", dr, sr1, sr2orimm7))

    def addInstructionAND(self, dr, sr1, sr2orimm7):
        self.add_instruction(
            Instru3A("and3", dr, sr1, sr2orimm7))

    def addInstructionOR(self, dr, sr1, sr2orimm7):
        self.add_instruction(
            Instru3A("or3", dr, sr1, sr2orimm7))

    def addInstructionXOR(self, dr, sr1, sr2orimm7):  # TODO verifier si il existe
        self.add_instruction(
            Instru3A("xor3", dr, sr1, sr2orimm7))

    # Copy values (immediate or in register)
    def addInstructionLETI(self, dr, imm7):
        self.add_instruction(Instru3A("leti", dr, imm7))

    def addInstructionLET(self, dr, sr):
        self.add_instruction(Instru3A("let", dr, sr))

    # Memory instructions - Meta Instructions

    def addInstructionRMEM(self, dr, sr):
        if isinstance(sr, Register):
            # Accept plain register where we expect an indirect
            # addressing mode.
            sr = Indirect(sr)
        self.add_instruction(Instru3A("rmem", dr, sr))

    def addInstructionWMEM(self, dr, sr):
        if isinstance(sr, Register):
            # Accept plain register where we expect an indirect
            # addressing mode.
            sr = Indirect(sr)
        self.add_instruction(Instru3A("wmem", dr, sr))

    # Allocation functions
    def naive_alloc(self):
        """ Allocate all temporaries to registers.
        Fail if there are too many temporaries."""
        regs = list(GP_REGS)  # Get a writable copy
        reg_allocation = dict()
        for tmp in self._pool._all_temps:
            try:
                reg = regs.pop()
            except IndexError:
                raise AllocationError(
                    "Too many temporaries ({}) for the naive allocation, sorry"
                    .format(len(self._pool._all_temps)))
            reg_allocation[tmp] = reg
        self._pool.set_reg_allocation(reg_allocation)
        self.iter_instructions(replace_reg)

    def alloc_to_mem(self):
        """Allocate all temporaries to memory. Hypothesis:
        - Expanded instructions can use r0 (to compute addresses), r1 and
        r2 (to store the values of temporaries before the actual
        instruction).
        """
        self._pool.set_reg_allocation(
            {vreg: self.new_offset(SP) for vreg in self._pool._all_temps})
        self.iter_instructions(replace_mem)

    # Dump code
    def printCode(self, filename, comment=None):
        # dump generated code on stdout or file.
        output = open(filename, 'w') if filename else sys.stdout
        output.write(
            ";;Automatically generated TARGET code, MIF08 & CAP 2018\n")
        if comment is not None:
            output.write(";;{} version\n".format(comment))
        # Stack in TARGET18 is managed with SP
        for i in self._listIns:
            i.printIns(output)
        output.write("\n\n;;postlude\n")
        output.write("end:\n	jump end\n")
        if output is not sys.stdout:
            output.close()