2015-12-19 1 views
4

J'ai un programme que j'ai écrit qui utilise LLVM 3.5 comme un compilateur JIT, que j'essaye de mettre à jour pour utiliser MCJIT dans LLVM 3.7. Je l'ai surtout travaillé, mais j'ai du mal à reproduire une fonctionnalité de débogage que j'ai implémentée avec LLVM 3.5. Je voudrais pouvoir voir le code machine hôte (par exemple x86, x64 ou ARM, pas LLVM IR) généré par le processus JIT; Dans les versions de débogage, je me déconnecte lorsque mon programme est en cours d'exécution. Avec LLVM 3.5 j'ai pu faire cela en appelant ExecutionEngine :: runJITOnFunction() pour remplir un objet llvm :: MachineCodeInfo, qui m'a donné l'adresse de début et la taille du code généré. Je pourrais alors démonter ce code.Comment puis-je désassembler le résultat de la compilation LLVM MCJIT?

Je n'arrive pas à trouver d'équivalent dans MCJIT. Je peux obtenir l'adresse de début de la fonction (par exemple via getPointerToFunction()) mais pas la taille.

J'ai vu Disassemble Memory mais en dehors de ne pas avoir autant de détails dans les réponses, il semble que ce soit plus sur la façon de désassembler une séquence d'octets. Je sais comment faire, ma question est: comment puis-je obtenir la séquence d'octets en premier lieu? Si cela peut aider à rendre ceci plus concret, veuillez réinterpréter cette question comme: "Comment puis-je étendre l'exemple Kaleidoscope JIT pour montrer le code machine (x86, ARM, etc.) qu'il produit, pas seulement le LLVM IR? "

Merci.

Répondre

1

Vous avez au moins deux options ici.

  1. Fournissez votre propre gestionnaire de mémoire. Cela doit être bien documenté et fait partie de plusieurs projets utilisant MCJIT. Mais pour être complet, voici le code:

    class MCJITMemoryManager : public llvm::RTDyldMemoryManager { 
    public: 
    static std::unique_ptr<MCJITMemoryManager> Create(); 
    
    MCJITMemoryManager(); 
    virtual ~MCJITMemoryManager(); 
    
    // Allocate a memory block of (at least) the given size suitable for 
    // executable code. The section_id is a unique identifier assigned by the 
    // MCJIT engine, and optionally recorded by the memory manager to access a 
    // loaded section. 
    byte* allocateCodeSection(uintptr_t size, unsigned alignment, 
              unsigned section_id, 
              llvm::StringRef section_name) override; 
    
    // Allocate a memory block of (at least) the given size suitable for data. 
    // The SectionID is a unique identifier assigned by the JIT engine, and 
    // optionally recorded by the memory manager to access a loaded section. 
    byte* allocateDataSection(uintptr_t size, unsigned alignment, 
            unsigned section_id, llvm::StringRef section_name, 
            bool is_readonly) override; 
    ... 
    } 
    

    passer une instance du gestionnaire de mémoire à EngineBuilder:

    std::unique_ptr<MCJITMemoryManager> manager = MCJITMemoryManager::Create(); 
    llvm::ExecutionEngine* raw = lvm::EngineBuilder(std::move(module)) 
        .setMCJITMemoryManager(std::move(manager)) 
        ... 
        .create(); 
    

    maintenant via ces callbacks vous avez le contrôle sur la mémoire où le code est émis. (Et la taille est passée directement à votre méthode). Souvenez-vous simplement de l'adresse du buffer que vous avez allouée pour la section de code et, arrêtez le programme dans gdb et désassemblez la mémoire (ou vider quelque part ou même utiliser le désassembleur de LLVM).

  2. Utilisez simplement llc sur votre LLVM IR avec les options appropriées (niveau d'optimisation, etc.). Comme je le vois, MCJIT est appelé ainsi pour une raison et que la raison est qu'il réutilise les modules de génération de code existants (même que llc).
0

Incluez l'en-tête suivant llvm/Object/SymbolSize.h et utilisez la fonction llvm::object::computeSymbolSizes(ObjectFile&). Vous aurez besoin d'obtenir une instance du ObjectFile en quelque sorte.

Pour obtenir cette instance, voici ce que vous pouvez faire:

  1. Déclarez une classe qui est appelée à convertir un Module à un ObjectFile, quelque chose comme: class ModuleToObjectFileCompiler { ... // Compile a Module to an ObjectFile. llvm::object::OwningBinary<llvm::object::ObjectFile> operator() (llvm::Module&); };
  2. Pour mettre en œuvre le operator() de ModuleToObjectFileCompiler, jetez un oeil à llvm/ExecutionEngine/Orc/CompileUtils.h où la classe SimpleCompiler est définie.

  3. Fournir une instance de ModuleToObjectFileCompiler à une instance de llvm::orc::IRCompileLayer, par exemple: new llvm::orc::IRCompileLayer <llvm::orc::ObjectLinkingLayer <llvm::orc::DoNothingOnNotifyLoaded> > (_object_layer, _module_to_object_file);

  4. Le operator() de ModuleToObjectFileCompiler reçoit l'instance de ObjectFile que vous pouvez fournir à computeSymbolSizes(). Ensuite, vérifiez le std::vector retourné pour trouver les tailles en octets de tous les symboles définis dans ce Module. Enregistrez les informations pour les symboles qui vous intéressent. Et c'est tout.