Beispiel #1
0
    def _get_global_spotmap(self):
        """Generate global spotmap and free values.

        Returns a tuple. First element is a dictionary mapping ILValue to
        spot for spots which do not need register allocation, like static
        variables or literals. The second element is a list of the free
        values; the variables which were not mapped in the global spotmap.
        """

        global_spotmap = {}
        free_values = []
        all_values = self._all_il_values()

        string_literal_number = 0
        for value in all_values:
            if value in self.il_code.literals:
                # If literal, assign it a preassigned literal spot
                s = LiteralSpot(self.il_code.literals[value])
                global_spotmap[value] = s
            elif value in self.il_code.externs:
                # If extern, assign assign spot and add the extern to asm code
                s = MemSpot(self.il_code.externs[value])
                global_spotmap[value] = s

                self.asm_code.add_extern(self.il_code.externs[value])
            elif value in self.il_code.string_literals:
                # Add the string literal representation to the output ASM.
                name = f"__strlit{string_literal_number}"
                string_literal_number += 1

                self.asm_code.add_string_literal(
                    name, self.il_code.string_literals[value])
                global_spotmap[value] = MemSpot(name)
            elif (self.arguments.variables_on_stack
                  and value in self.il_code.variables):  # pragma: no cover
                # If all variables are allocated on the stack
                self.offset += value.ctype.size
                s = MemSpot(spots.RBP, -self.offset)
                global_spotmap[value] = s
            else:
                # Value is free and needs an assignment
                free_values.append(value)

        return global_spotmap, free_values
Beispiel #2
0
    def make_asm(self, spotmap, home_spots, get_reg, asm_code):  # noqa D102
        addr_spot = spotmap[self.addr]
        value_spot = spotmap[self.val]

        addr_r = get_reg([], [value_spot])
        self.move(addr_spot, addr_r, asm_code)

        indir_spot = MemSpot(addr_r)

        self.move(value_spot, indir_spot, asm_code)
Beispiel #3
0
    def make_asm(self, spotmap, home_spots, get_reg, asm_code):  # noqa D102
        addr_spot = spotmap[self.addr]
        output_spot = spotmap[self.output]

        addr_r = get_reg([], [output_spot])
        self.move(addr_spot, addr_r, asm_code)

        indir_spot = MemSpot(addr_r)

        self.move(indir_spot, output_spot, asm_code)
Beispiel #4
0
    def _get_nondynamic_spot(self, v, num):
        """Get a spot for non-dynamic values.

        In particular, assigns a spot to all literals, string literals,
        variables with no storage, and variables with static storage.

        v - value to get a spot for, or None if the value goes in a dynamic
        spot like a register
        nnum - positive integer guaranteed never to be the same for two
        distinct calls to this function
        """
        EXTERNAL = self.symbol_table.EXTERNAL
        INTERNAL = self.symbol_table.INTERNAL
        TENTATIVE = self.symbol_table.TENTATIVE

        if v in self.il_code.literals:
            return LiteralSpot(self.il_code.literals[v])

        elif v in self.il_code.string_literals:
            name = f"__strlit{num}"
            self.asm_code.add_string_literal(name,
                                             self.il_code.string_literals[v])
            return MemSpot(name)

        # Values with no storage can be referenced directly by name
        elif not self.symbol_table.storage.get(v, True):
            return MemSpot(self.symbol_table.names[v])

        elif self.symbol_table.storage.get(v) == self.symbol_table.STATIC:
            name = self.symbol_table.names[v]
            if self.symbol_table.linkage_type.get(v) != EXTERNAL:
                name = f"{name}.{num}"

            if self.symbol_table.def_state.get(v) == TENTATIVE:
                local = (self.symbol_table.linkage_type[v] == INTERNAL)
                self.asm_code.add_comm(name, v.ctype.size, local)
            else:
                init_val = self.il_code.static_inits.get(v, 0)
                self.asm_code.add_data(name, v.ctype.size, init_val)

            return MemSpot(name)
Beispiel #5
0
    def make_asm(self, spotmap, home_spots, get_reg, asm_code):  # noqa D102
        addr_spot = spotmap[self.addr]
        value_spot = spotmap[self.val]

        if isinstance(addr_spot, RegSpot):
            addr_r = addr_spot
        else:
            addr_r = get_reg([], [value_spot])
            asm_code.add(asm_cmds.Mov(addr_r, addr_spot, 8))

        indir_spot = MemSpot(addr_r)
        if isinstance(value_spot, RegSpot):
            temp_reg = value_spot
        else:
            temp_reg = get_reg([], [addr_r])

        self.move_data(indir_spot, value_spot, self.val.ctype.size, temp_reg,
                       asm_code)
Beispiel #6
0
    def _make_asm(self, commands, global_spotmap):
        """Generate ASM code for given command list."""

        # Get free values
        free_values = self._get_free_values(commands, global_spotmap)
        #print("\nfree values", free_values)

        #pprint.pprint(commands)

        # If any variable may have its address referenced, assign it a
        # permanent memory spot if it doesn't yet have one.
        move_to_mem = []

        moveAllToMem = False

        if moveAllToMem:
            # test where we move all variables to memory
            for v in free_values:
                move_to_mem.append(v)
                #print(v, " was moved to mem because of test")

        else:
            for command in commands:
                refs = command.references().values()
                for line in refs:
                    for v in line:
                        if v not in refs:
                            move_to_mem.append(v)
                            #print(v, " was moved to mem")

            # In addition, move all IL values of strange size to memory because
            # they won't fit in a register.
            for v in free_values:
                if v.ctype.size > 4:
                    move_to_mem.append(v)
                    #print(v, " was moved to mem because of size")

        # (Not really relevant for B322)
        # Shivy-todo: All non-free IL values are automatically assigned distinct
        # memory spots. However, this is very inoptimal for structs.
        # Consider the following C code, where S is already declared:
        #
        #   struct S array[10];
        #   s = array[1];
        #
        # This code compiles to the following IL:
        #
        #   READAT(array, 1) -> X
        #   SET(X) -> s
        #
        # However, X is an unnecessary copy of `s` in memory. Ideally,
        # the register allocator will recognize that X is just a temporary
        # and assign X to the same memory location as s to avoid additional
        # copy operations and memory usage. This also requires that the
        # relevant IL commands check whether the two arguments are in the
        # same spot before trying to do a copy.
        for v in move_to_mem:
            if v in free_values:
                self.offset += v.ctype.size
                global_spotmap[v] = MemSpot(spots.RBP, -self.offset)
                free_values.remove(v)

        # pprint.pprint(free_values)

        # Perform liveliness analysis
        live_vars = self._get_live_vars(commands, free_values)

        #print(live_vars)

        # Generate conflict and preference graph
        g_bak = self._generate_graph(commands, free_values, live_vars)

        spilled_nodes = []

        while True:
            g = g_bak.copy()

            # Remove all nodes that have been spilled for this iteration
            for n in spilled_nodes:
                g.pop(n)

            removed_nodes = []
            merged_nodes = {}

            # Repeat simplification, coalescing, and freeze until freeze
            # does not work.
            while True:
                # Repeat simplification and coalescing until nothing
                # happens.
                while True:
                    simplified = self._simplify_all(removed_nodes, g)
                    merged = self._coalesce_all(merged_nodes, g)

                    if not simplified and not merged: break

                if not self._freeze(g):
                    break

            # If no nodes remain, we are done
            if not g.nodes():
                break
            # If nodes do remain, spill one of them and retry
            else:
                # Spill node with highest number of conflicts. This node
                # will never be a merged node because we merge nodes
                # conservatively, so any recently merged node can be
                # simplified immediately.
                n = max(g.nodes(), key=lambda n: len(g.confs(n)))
                spilled_nodes.append(n)

        # Move any remaining nodes from graph into removed_nodes
        # This accounts for pseudonodes which cannot be removed in the
        # simplify phase.
        while g.all_nodes():
            removed_nodes.append(g.pop(g.all_nodes()[0]))

        #print(removed_nodes)
        # Pop values off the stack to generate spot assignments.
        spotmap = self._generate_spotmap(removed_nodes, merged_nodes, g_bak)

        #print(spotmap)

        # Assign stack values to the spilled nodes
        for v in spilled_nodes:
            self.offset += v.ctype.size
            spotmap[v] = MemSpot(spots.RBP, -self.offset)

        # Merge global spotmap into this spotmap
        for v in global_spotmap:
            spotmap[v] = global_spotmap[v]

        if self.arguments.show_reg_alloc_perf:  # pragma: no cover
            total_prefs = 0
            matched_prefs = 0

            for n1, n2 in itertools.combinations(g_bak.all_nodes(), 2):
                if n2 in g_bak.prefs(n1):
                    total_prefs += 1
                    if spotmap[n1] == spotmap[n2]:
                        matched_prefs += 1

            print("total prefs", total_prefs)
            print("matched prefs", matched_prefs)

            print("total ILValues", len(g_bak.nodes()))
            print("register ILValues", len(g_bak.nodes()) - len(spilled_nodes))

        # Generate assembly code
        self._generate_asm(commands, live_vars, spotmap)