def run(self, blocks=None, equiv_set=None):
        """run array shape analysis on the given IR blocks, resulting in
        modified IR and finalized EquivSet for each block.
        """
        if blocks is None:
            blocks = self.func_ir.blocks

        self.func_ir._definitions = build_definitions(self.func_ir.blocks)

        if equiv_set is None:
            init_equiv_set = SymbolicEquivSet(self.typemap)
        else:
            init_equiv_set = equiv_set

        self.alias_map, self.arg_aliases = find_potential_aliases(
            blocks,
            self.func_ir.arg_names,
            self.typemap,
            self.func_ir
        )

        aa_count_save = ArrayAnalysis.aa_count
        ArrayAnalysis.aa_count += 1
        if config.DEBUG_ARRAY_OPT >= 1:
            print("Starting ArrayAnalysis:", aa_count_save)
        dprint_func_ir(self.func_ir, "before array analysis", blocks)

        if config.DEBUG_ARRAY_OPT >= 1:
            print(
                "ArrayAnalysis variable types: ", sorted(self.typemap.items())
            )
            print("ArrayAnalysis call types: ", self.calltypes)

        cfg = compute_cfg_from_blocks(blocks)
        topo_order = find_topo_order(blocks, cfg=cfg)
        # Traverse blocks in topological order
        self._run_on_blocks(topo_order, blocks, cfg, init_equiv_set)

        if config.DEBUG_ARRAY_OPT >= 1:
            self.dump()
            print(
                "ArrayAnalysis post variable types: ",
                sorted(self.typemap.items()),
            )
            print("ArrayAnalysis post call types: ", self.calltypes)

        dprint_func_ir(self.func_ir, "after array analysis", blocks)
        if config.DEBUG_ARRAY_OPT >= 1:
            print("Ending ArrayAnalysis:", aa_count_save)
run array shape analysis on the given IR blocks, resulting in
        modified IR and finalized EquivSet for each block.
        
numba/parfors/array_analysis.py