Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • 3d2278499816ce8336a2eb695a9f2d218983303f
  • master par défaut
  • 1-baka-export
  • meson
  • assdraw
  • old-master
  • v3.2.2
  • v3.2.1
  • v3.2.0
  • v3.1.3
  • v3.1.2
  • v3.1.1
  • v3.1.0
  • v3.0.4
  • v3.0.3
  • v3.0.2
  • v3.0.1
  • v3.0.0
  • v2.1.3
  • v2.1.4
  • v2.1.5
  • v2.1.6
  • v2.1.0
  • v2.1.1
  • v2.1.2
  • v2.1.7
26 résultats

auto4_lua.h

Blame
  • plugin.cpp 21,99 Kio
    #include <gcc-plugin.h>
    #include <plugin-version.h>
    #include <tree.h>
    #include <basic-block.h>
    #include <diagnostic.h>
    #include <gimple.h>
    #include <tree-pass.h>
    #include <context.h>
    #include <function.h>
    #include <gimple-iterator.h>
    #include <c-family/c-pragma.h>
    #include <vec.h>
    #include <bitmap.h>
    #include <queue>
    
    // ========== VARIABLES GLOBALES ========== //
    
    int plugin_is_GPL_compatible;
    
    // Mode debug (affichage des informations de débogage)
    bool debug_mode = true;
    
    /* Enumération des codes des opérations collectives MPI potentielles à vérifier */
    enum mpi_collective_code
    {
    #define DEFMPICOLLECTIVES(CODE, NAME) CODE,
    #include "MPI_collectives.def"
        LAST_AND_UNUSED_MPI_COLLECTIVE_CODE
    #undef DEFMPICOLLECTIVES
    };
    
    /* Tableau de noms des opérations collectives MPI potentielles à vérifier */
    #define DEFMPICOLLECTIVES(CODE, NAME) NAME,
    const char *const mpi_collective_name[] = {
    #include "MPI_collectives.def"
    };
    #undef DEFMPICOLLECTIVES
    
    // Vecteur contenant la liste des fonctions à vérifier issue de la directive #pragma ProjetCA mpicoll_check
    vec<tree> *fun_vec;
    
    // ========== //
    
    // ========== PRAGMA ========== //
    
    // Affichage du vecteur (variable globale) des fonctions à vérifier
    static void print_fun_vec()
    {
        tree x;
        printf("fun_vec contains %i element(s):", fun_vec->length());
        for (int i = 0; fun_vec->iterate(i, &x); i++)
            printf(" %s,", IDENTIFIER_POINTER(x));
        printf("\n");
    }
    
    // Traitement de la directive #pragma ProjetCA mpicoll_check
    static void handle_pragma_mpicoll_check(cpp_reader *)
    {
        location_t loc;
        enum cpp_ttype token;
        tree x;
        bool close_paren_needed_p = false;
    
        // Erreur si la directive est placée au sein d'une fonction
        if (cfun)
        {
            error_at(cfun->function_start_locus, "%<#pragma ProjetCA mpicoll_check%> is not allowed inside functions");
            return;
        }
    
        token = pragma_lex(&x, &loc);
        // Si la directive est suivie d'une parenthèse ouvrante, on cherche une liste de noms de fonctions de la forme (fun1,fun2,fun3,...)
        // Sinon, on s'attend au nom d'une seule fonction
        if (token == CPP_OPEN_PAREN)
        {
            close_paren_needed_p = true;
            token = pragma_lex(&x, &loc);
        }
    
        // Si le premier token (ou suivant la parenthèse) n'est pas un nom, on signale une erreur
        if (token != CPP_NAME)
            error_at(loc, "%<#pragma ProjetCA mpicoll_check%> is not a valid name");
        else
        {
            do
            {
                // On émet un avertissement si le nom d'une fonction est présent plusieurs fois dans l'ensemble des directives #pragma ProjetCA mpicoll_check
                if (fun_vec->contains(x))
                    warning_at(loc, 0, "%<#pragma ProjetCA mpicoll_check%> already contains the function %<%s%>", IDENTIFIER_POINTER(x));
                // Sinon, on ajoute la fonction au vecteur
                else
                    fun_vec->safe_push(x);
    
                // On parcourt la liste de noms de fonctions séparés par des virgules
                token = pragma_lex(&x, &loc);
                while (token == CPP_COMMA)
                    token = pragma_lex(&x, &loc);
            } while (token == CPP_NAME);
    
            // On vérifie que la liste de noms de fonctions est bien terminée par une parenthèse fermante
            if (close_paren_needed_p)
            {
                if (token == CPP_CLOSE_PAREN)
                    token = pragma_lex(&x, &loc);
                else
                    error_at(loc, "%<#pragma ProjetCA mpicoll_check%> does not have a final %<)%>");
            }
    
            if (token != CPP_EOF)
            {
                error_at(loc, "%<#pragma ProjetCA mpicoll_check%> string is badly formed");
                return;
            }
    
            if (debug_mode)
                print_fun_vec();
        }
    }
    
    // Enregistrement de la directive #pragma ProjetCA mpicoll_check
    static void register_pragma_mpicoll_check(void *event_data, void *data)
    {
        c_register_pragma("ProjetCA", "mpicoll_check", handle_pragma_mpicoll_check);
    }
    
    // Opérations de clôture de la directive #pragma ProjetCA mpicoll_check
    static void clear_pragma_mpicoll_check(void *event_data, void *data)
    {
        tree x;
        // On émet un avertissement pour chaque fonction présente dans la directive qui n'a pas été appelée durant les passes
        for (int i = 0; fun_vec->iterate(i, &x); i++)
            warning(0, "%<#pragma ProjetCA mpicoll_check%> contains the function %<%s%> which does not seem defined", IDENTIFIER_POINTER(x));
    
        // On libère le vecteur
        fun_vec->release();
    }
    
    // ========== //
    
    // ========== UTILS ========== //
    
    // Récupération du code de l'opération collective MPI associée à l'instruction courante
    // Si l'instruction courante n'est pas une opération collective MPI présente dans la liste, on renvoie LAST_AND_UNUSED_MPI_COLLECTIVE_CODE
    enum mpi_collective_code get_mpi_collective_code(gimple *stmt)
    {
        if (is_gimple_call(stmt))
        {
            const char *callee_name;
    
            callee_name = IDENTIFIER_POINTER(DECL_NAME(gimple_call_fndecl(stmt)));
    
            for (int i = 0; i < LAST_AND_UNUSED_MPI_COLLECTIVE_CODE; i++)
            {
                if (strcmp(callee_name, mpi_collective_name[i]) == 0)
                {
                    return mpi_collective_code(i);
                }
            }
        }
    
        return LAST_AND_UNUSED_MPI_COLLECTIVE_CODE;
    }
    
    // Séparation des blocks contenant plusieurs appels MPI
    void split_blocks_with_multiple_mpi_calls(function *fun)
    {
        basic_block bb;
    
        // On parcourt tous les basic blocks de la fonction
        FOR_EACH_BB_FN(bb, fun)
        {
            gimple *old_stmt = NULL;
    
            for (gimple_stmt_iterator gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi))
            {
                gimple *stmt = gsi_stmt(gsi);
    
                mpi_collective_code stmt_code = get_mpi_collective_code(stmt);
    
                // On repère les instructions courantes qui sont des fonctions MPI collective présentes dans la liste
                if (stmt_code != LAST_AND_UNUSED_MPI_COLLECTIVE_CODE)
                {
                    // Si on a déjà repéré une instruction MPI collective dans le basic block courant, on crée un nouveau basic block
                    if (old_stmt != NULL)
                        split_block(bb, old_stmt);
                    old_stmt = stmt;
                }
            }
        }
    }
    
    bitmap_head *generate_new_cfg_edges(function *fun)
    {
        bitmap_head *new_cfg_edges = XNEWVEC(bitmap_head, n_basic_blocks_for_fn(fun));
        basic_block src = ENTRY_BLOCK_PTR_FOR_FN(fun);
        bitmap *visited_blocks;
        visited_blocks = XNEWVEC(bitmap, n_basic_blocks_for_fn(fun));
        edge e;
        basic_block bb;
    
        // On initialise le bitmap des arcs du nouveau CFG
        FOR_EACH_BB_FN(bb, fun)
        {
            bitmap_initialize(&new_cfg_edges[bb->index], &bitmap_default_obstack);
        }
    
        // On initialise le bitmap des bb déjà visités
        bitmap_initialize(*visited_blocks, &bitmap_default_obstack);
    
        // On marque le bloc d'entrée comme visité
        bitmap_set_bit(*visited_blocks, src->index);
    
        // On parcourt le CFG à partir du bloc d'entrée
        std::queue<basic_block> q;
        // Ajout du bloc d'entrée dans la file
        q.push(src);
    
        // Tant que la file n'est pas vide, on traite le premier élément de la file
        // On ajoute les arcs sortants dont le bloc de destination n'a pas encore été visité
        while (!q.empty())
        {
            basic_block bb = q.front();
            q.pop();
    
            edge_iterator ei;
            FOR_EACH_EDGE(e, ei, bb->succs)
            {
                basic_block succ = e->dest;
    
                if (!bitmap_bit_p(*visited_blocks, succ->index))
                {
                    bitmap_set_bit(&new_cfg_edges[bb->index], succ->index);
                    bitmap_set_bit(*visited_blocks, succ->index);
                    q.push(succ);
                }
            }
        }
    
        // On libère le bitmap des bb déjà visités
        bitmap_clear(*visited_blocks);
    
        return new_cfg_edges;
    }
    
    // Calcul du rang maximal des bb d'une fonction
    static int get_max_rank(function *fun, int *ranks)
    {
        int max_rank = 0;
        for (int i = 0; i < n_basic_blocks_for_fn(fun); i++)
        {
            if (ranks[i] > max_rank)
            {
                max_rank = ranks[i];
            }
        }
    
        return max_rank;
    }
    
    // ========== //
    
    // ========== GRAPHVIZ ========== //
    
    // Création du nom du fichier .dot à partir du nom de la fonction et d'un suffixe
    static char *cfgviz_generate_filename(function *fun, const char *suffix)
    {
        char *target_filename;
    
        target_filename = (char *)xmalloc(1024 * sizeof(char));
    
        snprintf(target_filename, 1024, "%s_%s_%d_%s.dot",
                 current_function_name(),
                 LOCATION_FILE(fun->function_start_locus),
                 LOCATION_LINE(fun->function_start_locus),
                 suffix);
    
        return target_filename;
    }
    
    // Export de la représentation du CFG d'une fonction dans un descriptor de fichier
    static void cfgviz_internal_dump(function *fun, FILE *out)
    {
        basic_block bb;
    
        fprintf(out, "Digraph G{\n");
    
        FOR_ALL_BB_FN(bb, cfun)
        {
            fprintf(out, "%d [label=\"BB %d", bb->index, bb->index);
    
            gimple_stmt_iterator gsi;
            gimple *stmt;
            gsi = gsi_start_bb(bb);
            stmt = gsi_stmt(gsi);
    
            /* Itération sur les instructions du bloc */
            for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi))
            {
                stmt = gsi_stmt(gsi);
    
                enum mpi_collective_code returned_code = get_mpi_collective_code(stmt);
    
                if (returned_code != LAST_AND_UNUSED_MPI_COLLECTIVE_CODE)
                {
                    fprintf(out, " \\n %s", mpi_collective_name[returned_code]);
                }
            }
    
            fprintf(out, "\" shape=ellipse]\n");
    
            edge_iterator eit;
            edge e;
    
            // Représentation des arcs sortants
            FOR_EACH_EDGE(e, eit, bb->succs)
            {
                const char *label = "";
                if (e->flags == EDGE_TRUE_VALUE)
                    label = "true";
                else if (e->flags == EDGE_FALSE_VALUE)
                    label = "false";
    
                fprintf(out, "%d -> %d [color=red label=\"%s\"]\n",
                        bb->index, e->dest->index, label);
            }
        }
    
        fprintf(out, "}\n");
    }
    
    // Export de la représentation du CFG d'une fonction dans un fichier .dot
    void cfgviz_dump(function *fun, const char *suffix)
    {
        char *target_filename;
        FILE *out;
    
        target_filename = cfgviz_generate_filename(fun, suffix);
    
        printf("Generating CFG of function %s in file <%s>\n",
               current_function_name(), target_filename);
    
        out = fopen(target_filename, "w");
    
        cfgviz_internal_dump(fun, out);
    
        fclose(out);
        free(target_filename);
    }
    
    // ========== //
    
    // ========== DOMINANCE ========== //
    
    // Calcul de la post dominance d'un ensemble de basic blocks
    bitmap get_post_dominance_for_a_set(bitmap_head *set, function *fun, bitmap_head *pd)
    {
        bitmap_initialize(pd, &bitmap_default_obstack);
    
        // Pour chaque bb de l'ensemble, on récupère tous les bb post-dominés par celui-ci
        basic_block bb;
        FOR_ALL_BB_FN(bb, fun)
        {
            if (bitmap_bit_p(set, bb->index))
            {
                auto_vec<basic_block> pd_blocks = get_all_dominated_blocks(CDI_POST_DOMINATORS, bb);
                for (unsigned int i = 0; i < pd_blocks.length(); i++)
                {
                    bitmap_set_bit(pd, pd_blocks[i]->index);
                }
            }
        }
    
        // On garde les bb dont au moins un successeur est post-dominé par tous les bb de l'ensemble
        FOR_ALL_BB_FN(bb, fun)
        {
            edge e;
            edge_iterator ei;
            int total = 0;
            int succ_n = 0;
            FOR_EACH_EDGE(e, ei, bb->succs)
            {
                basic_block succ = e->dest;
                if (bitmap_bit_p(pd, succ->index))
                    succ_n++;
    
                total++;
            }
            if (total > 0 && succ_n == total)
                bitmap_set_bit(pd, bb->index);
        }
    
        return pd;
    }
    
    // Calcul de la frontière de post dominance d'un ensemble de basic blocks
    bitmap get_post_dominance_frontier_for_a_set(bitmap_head *set, function *fun, bitmap_head *pd)
    {
        bitmap_head pd2;
        bitmap_initialize(pd, &bitmap_default_obstack);
    
        // On récupère la post dominance de l'ensemble
        get_post_dominance_for_a_set(set, fun, &pd2);
    
        // On garde les noeuds qui ne sont pas post-dominés par l'ensemble mais dont au moins un successeur l'est
        basic_block bb;
        FOR_ALL_BB_FN(bb, fun)
        {
            edge e;
            edge_iterator ei;
            FOR_EACH_EDGE(e, ei, bb->succs)
            {
                basic_block succ = e->dest;
                if (bitmap_bit_p(&pd2, succ->index) && !bitmap_bit_p(&pd2, bb->index))
                {
                    bitmap_set_bit(pd, bb->index);
                }
            }
        }
    
        return pd;
    }
    
    // ========== //
    
    // ========== PASS ========== //
    
    const pass_data pass_mpicoll_check_data = {
        GIMPLE_PASS,
        "MPICOLL_CHECK",
        OPTGROUP_NONE,
        TV_OPTIMIZE,
        0,
        0,
        0,
        0,
        0,
    };
    
    class pass_mpicoll_check : public gimple_opt_pass
    {
    public:
        pass_mpicoll_check(gcc::context *ctxt) : gimple_opt_pass(pass_mpicoll_check_data, ctxt) {}
    
        pass_mpicoll_check *clone() { return new pass_mpicoll_check(g); }
    
        bool gate(function *fun)
        {
            bool pass_enabled = false;
    
            tree x;
            unsigned int i = 0;
            // On parcourt le vecteur des fonctions à vérifier
            while (!pass_enabled && i < fun_vec->length())
            {
                fun_vec->iterate(i, &x);
                i++;
                // Si la fonction courante est présente dans le vecteur, on l'enlève du vecteur et on active la passe
                if (strcmp(IDENTIFIER_POINTER(x), function_name(fun)) == 0)
                {
                    fun_vec->unordered_remove(i - 1);
                    pass_enabled = true;
                }
            }
    
            if (pass_enabled && debug_mode)
                printf("Executing mpicoll_check pass for the function %s\n", function_name(fun));
    
            return pass_enabled;
        }
    
        unsigned int execute(function *fun)
        {
            // Export du CFG de la fonction avant la séparation des blocs contenant plusieurs appels MPI
            if (debug_mode)
                cfgviz_dump(fun, "before_split");
    
            // Séparation des blocs contenant plusieurs appels MPI
            split_blocks_with_multiple_mpi_calls(fun);
    
            // Export du CFG de la fonction après la séparation des blocs contenant plusieurs appels MPI
            if (debug_mode)
                cfgviz_dump(fun, "after_split");
    
            // Calcul des informations de post-dominance
            calculate_dominance_info(CDI_POST_DOMINATORS);
    
            // Création du nouveau CFG en retirant les arcs retour
            bitmap_head *new_cfg_edges = generate_new_cfg_edges(fun);
    
            // Définition d'une structure pour stocker les informations de rang
            struct bb_with_rank
            {
                basic_block bb;
                int rank;
            };
    
            basic_block bb;
    
            std::queue<bb_with_rank> q;
            bb_with_rank bbr;
    
            bbr.bb = ENTRY_BLOCK_PTR_FOR_FN(fun);
            bbr.rank = 0;
            q.push(bbr);
    
            // Tableaux pour stocker les informations de rang et d'appel de chaque bb
            int calls[n_basic_blocks_for_fn(fun)];
            int ranks[n_basic_blocks_for_fn(fun)];
    
            // On parcourt l'ensemble des basic blocks de l'entrée vers la sortie
            while (!q.empty())
            {
                bbr = q.front();
                q.pop();
    
                gimple_stmt_iterator gsi;
                mpi_collective_code c = LAST_AND_UNUSED_MPI_COLLECTIVE_CODE;
    
                // Si le bb contient une instruction MPI, on sort de la boucle pour lui attribuer un rang
                for (gsi = gsi_start_bb(bbr.bb); !gsi_end_p(gsi); gsi_next(&gsi))
                {
                    gimple *stmt = gsi_stmt(gsi);
                    c = get_mpi_collective_code(stmt);
                    if (c != LAST_AND_UNUSED_MPI_COLLECTIVE_CODE)
                        break;
                }
    
                calls[bbr.bb->index] = c;
    
                // Si le bb contient un appel MPI, on augmente d'un son rang par rapport à celui du prédécesseur
                // Sinon, on garde le même rang et on continue
                if (c != LAST_AND_UNUSED_MPI_COLLECTIVE_CODE)
                    ranks[bbr.bb->index] = bbr.rank + 1;
                else
                    ranks[bbr.bb->index] = bbr.rank;
    
                FOR_ALL_BB_FN(bb, fun)
                {
                    // On ajoute les successeurs du bb courant dans la file en leur donnant le même rang
                    if (bitmap_bit_p(&new_cfg_edges[bbr.bb->index], bb->index))
                    {
                        bb_with_rank bbr2;
                        bbr2.bb = bb;
                        bbr2.rank = ranks[bbr.bb->index];
                        q.push(bbr2);
                    }
                }
            }
    
            // Récupération du rang maximal
            int max_rank = get_max_rank(fun, ranks);
    
            // Initialisation d'une matrice de bitmaps de taille nombre_appels_differents * rang_maximum pour avoir tous les ensembles possibles
            bitmap_head **sets = XNEWVEC(bitmap_head *, max_rank);
    
            // Initialisation des bitmaps
            for (int i = 0; i < max_rank; i++)
            {
                sets[i] = XNEWVEC(bitmap_head, LAST_AND_UNUSED_MPI_COLLECTIVE_CODE);
                for (unsigned int j = 0; j < LAST_AND_UNUSED_MPI_COLLECTIVE_CODE; j++)
                {
                    bitmap_initialize(&sets[i][j], &bitmap_default_obstack);
                }
            }
    
            // Remplissage des bitmaps avec les index des bb compris dans les ensembles correspondants
            FOR_ALL_BB_FN(bb, fun)
            {
                if (calls[bb->index] != LAST_AND_UNUSED_MPI_COLLECTIVE_CODE)
                {
                    bitmap_set_bit(&sets[ranks[bb->index] - 1][calls[bb->index]], bb->index);
                }
            }
    
            // Pour chaque ensemble possible de notre matrice, on récupère la frontière de post dominance
            for (int i = 0; i < max_rank; i++)
            {
                for (unsigned int j = 0; j < LAST_AND_UNUSED_MPI_COLLECTIVE_CODE; j++)
                {
                    bitmap_head pd;
                    int target_rank = 0;
                    get_post_dominance_frontier_for_a_set(&sets[i][j], fun, &pd);
    
                    // Si la frontière de post dominance n'est pas vide, on émet un avertissement pour deadlock
                    // Cela signifie que certains chemins du CFG ne passent pas forcément par cette collective MPI
                    // Certains processus peuvent donc être bloqués en attendant que les autres processus atteignent cette collective MPI à un autre moment
                    if (!bitmap_empty_p(&pd))
                    {
                        // On émet un avertissement en indiquant la collective concernée dans la fonction
                        warning(0, "The MPI collective %<%s%> (rank %d) differs between process (possible deadlock)", mpi_collective_name[j], i + 1);
                        target_rank = i + 1;
    
                        bitmap_head temp;
                        bitmap_initialize(&temp, &bitmap_default_obstack);
                        // On calcule la frontière de post-dominance itérée jusqu'à ce qu'elle soit stable pour trouver l'origine du problème
                        do
                        {
                            bitmap_copy(&temp, &pd);
                            get_post_dominance_frontier_for_a_set(&temp, fun, &pd);
                        } while (!bitmap_empty_p(&pd));
    
                        // Notre frontière de post-dominance itérée est stable, on peut donc récupérer et indiquer le basic block contenant l'instruction à partir de laquelle on observe des divergences
                        for (int i = 0; i < n_basic_blocks_for_fn(fun); i++)
                        {
                            if (bitmap_bit_p(&temp, i))
                            {
                                bb = BASIC_BLOCK_FOR_FN(fun, i);
    
                                // On parcourt les statements du basic block pour trouver la première instruction non MPI qui pourrait créer la divergence
                                gimple_stmt_iterator gsi;
                                gimple *stmt;
                                for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi))
                                {
                                    stmt = gsi_stmt(gsi);
                                    if (get_mpi_collective_code(stmt) == LAST_AND_UNUSED_MPI_COLLECTIVE_CODE)
                                    {
                                        warning_at(gimple_location(stmt), 0, "The deadlock origin for the MPI collective %<%s%> (rank %d) is probably here", mpi_collective_name[j], target_rank);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
    
            free_dominance_info(CDI_POST_DOMINATORS);
    
            return 0;
        }
    };
    
    // ========== //
    
    // ========== PLUGIN ========== //
    
    // Initialisation du plugin
    int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
    {
        // On crée le vecteur qui contiendra la liste des fonctions à vérifier issue de la directive #pragma ProjetCA mpicoll_check
        fun_vec = new vec<tree>;
        fun_vec->create(0);
    
        struct register_pass_info pass_info;
    
        // On vérifie que la version de GCC est compatible avec le plugin
        if (!plugin_default_version_check(version, &gcc_version))
            return 1;
    
        pass_mpicoll_check p(g);
        pass_info.pass = &p;
        pass_info.reference_pass_name = "cfg";
        pass_info.ref_pass_instance_number = 0;
        pass_info.pos_op = PASS_POS_INSERT_AFTER;
    
        register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info);
    
        register_callback(plugin_info->base_name, PLUGIN_PRAGMAS, register_pragma_mpicoll_check, NULL);
    
        register_callback(plugin_info->base_name, PLUGIN_FINISH, clear_pragma_mpicoll_check, NULL);
    
        return 0;
    }
    
    // ========== //