| //===- IRUtils.cpp - IR2Vec Embedding Generation ----------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// This file implements the IR2VecTool class for IR2Vec embedding generation | |
| /// from LLVM IR. It has no dependency on Machine IR. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "IRUtils.h" | |
| #include "llvm/Analysis/IR2Vec.h" | |
| #include "llvm/IR/BasicBlock.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/InstIterator.h" | |
| #include "llvm/IR/Instructions.h" | |
| #include "llvm/IR/LLVMContext.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/IR/PassInstrumentation.h" | |
| #include "llvm/IR/PassManager.h" | |
| #include "llvm/IR/Type.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/Errc.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/WithColor.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #define DEBUG_TYPE "ir2vec" | |
| namespace llvm { | |
| namespace ir2vec { | |
| Expected<std::shared_ptr<Vocabulary>> loadVocabulary(StringRef VocabPath) { | |
| auto VocabOrErr = Vocabulary::fromFile(VocabPath); | |
| if (!VocabOrErr) | |
| return VocabOrErr.takeError(); | |
| auto V = std::make_shared<Vocabulary>(std::move(*VocabOrErr)); | |
| if (!V->isValid()) | |
| return createStringError(errc::invalid_argument, | |
| "Failed to initialize IR2Vec vocabulary"); | |
| return V; | |
| } | |
| Error IR2VecTool::setVocabulary(std::shared_ptr<Vocabulary> V) { | |
| if (!V) | |
| return createStringError(errc::invalid_argument, | |
| "Null pointer provided for vocabulary. Will not " | |
| "set IR2VecTool vocabulary."); | |
| if (!V->isValid()) | |
| return createStringError( | |
| errc::invalid_argument, | |
| "Vocabulary is not valid. Will not set IR2VecTool vocabulary."); | |
| Vocab = std::move(V); | |
| return Error::success(); | |
| } | |
| TripletResult IR2VecTool::generateTriplets(const Function &F) const { | |
| if (F.isDeclaration()) | |
| return {}; | |
| TripletResult Result; | |
| Result.MaxRelation = 0; | |
| unsigned MaxRelation = NextRelation; | |
| unsigned PrevOpcode = 0; | |
| bool HasPrevOpcode = false; | |
| for (const BasicBlock &BB : F) { | |
| for (const auto &I : BB) { | |
| if (I.isDebugOrPseudoInst()) | |
| continue; | |
| unsigned Opcode = Vocabulary::getIndex(I.getOpcode()); | |
| unsigned TypeID = Vocabulary::getIndex(I.getType()->getTypeID()); | |
| // Add "Next" relationship with previous instruction | |
| if (HasPrevOpcode) { | |
| Result.Triplets.push_back({PrevOpcode, Opcode, NextRelation}); | |
| LLVM_DEBUG(dbgs() << Vocabulary::getVocabKeyForOpcode(PrevOpcode + 1) | |
| << '\t' | |
| << Vocabulary::getVocabKeyForOpcode(Opcode + 1) | |
| << '\t' << "Next\n"); | |
| } | |
| // Add "Type" relationship | |
| Result.Triplets.push_back({Opcode, TypeID, TypeRelation}); | |
| LLVM_DEBUG( | |
| dbgs() << Vocabulary::getVocabKeyForOpcode(Opcode + 1) << '\t' | |
| << Vocabulary::getVocabKeyForTypeID(I.getType()->getTypeID()) | |
| << '\t' << "Type\n"); | |
| // Add "Arg" relationships | |
| unsigned ArgIndex = 0; | |
| for (const Use &U : I.operands()) { | |
| unsigned OperandID = Vocabulary::getIndex(*U.get()); | |
| unsigned RelationID = ArgRelation + ArgIndex; | |
| Result.Triplets.push_back({Opcode, OperandID, RelationID}); | |
| LLVM_DEBUG({ | |
| StringRef OperandStr = Vocabulary::getVocabKeyForOperandKind( | |
| Vocabulary::getOperandKind(U.get())); | |
| dbgs() << Vocabulary::getVocabKeyForOpcode(Opcode + 1) << '\t' | |
| << OperandStr << '\t' << "Arg" << ArgIndex << '\n'; | |
| }); | |
| ++ArgIndex; | |
| } | |
| // Only update MaxRelation if there were operands | |
| if (ArgIndex > 0) | |
| MaxRelation = std::max(MaxRelation, ArgRelation + ArgIndex - 1); | |
| PrevOpcode = Opcode; | |
| HasPrevOpcode = true; | |
| } | |
| } | |
| Result.MaxRelation = MaxRelation; | |
| return Result; | |
| } | |
| TripletResult IR2VecTool::generateTriplets() const { | |
| TripletResult Result; | |
| Result.MaxRelation = NextRelation; | |
| for (const Function &F : M.getFunctionDefs()) { | |
| TripletResult FuncResult = generateTriplets(F); | |
| Result.MaxRelation = std::max(Result.MaxRelation, FuncResult.MaxRelation); | |
| Result.Triplets.insert(Result.Triplets.end(), FuncResult.Triplets.begin(), | |
| FuncResult.Triplets.end()); | |
| } | |
| return Result; | |
| } | |
| void IR2VecTool::writeTripletsToStream(raw_ostream &OS) const { | |
| auto Result = generateTriplets(); | |
| OS << "MAX_RELATION=" << Result.MaxRelation << '\n'; | |
| for (const auto &T : Result.Triplets) | |
| OS << T.Head << '\t' << T.Tail << '\t' << T.Relation << '\n'; | |
| } | |
| EntityList IR2VecTool::collectEntityMappings() { | |
| auto EntityLen = Vocabulary::getCanonicalSize(); | |
| EntityList Result; | |
| for (unsigned EntityID = 0; EntityID < EntityLen; ++EntityID) | |
| Result.push_back(Vocabulary::getStringKey(EntityID).str()); | |
| return Result; | |
| } | |
| void IR2VecTool::writeEntitiesToStream(raw_ostream &OS) { | |
| auto Entities = collectEntityMappings(); | |
| OS << Entities.size() << "\n"; | |
| for (unsigned EntityID = 0; EntityID < Entities.size(); ++EntityID) | |
| OS << Entities[EntityID] << '\t' << EntityID << '\n'; | |
| } | |
| Expected<std::unique_ptr<Embedder>> | |
| IR2VecTool::createIR2VecEmbedder(const Function &F, IR2VecKind Kind) const { | |
| if (!Vocab || !Vocab->isValid()) | |
| return createStringError( | |
| errc::invalid_argument, | |
| "Vocabulary is not valid. IR2VecTool not initialized."); | |
| if (F.isDeclaration()) | |
| return createStringError(errc::invalid_argument, | |
| "Function is a declaration."); | |
| auto Emb = Embedder::create(Kind, F, *Vocab); | |
| if (!Emb) | |
| return createStringError(errc::invalid_argument, | |
| "Failed to create embedder for function '%s'.", | |
| F.getName().str().c_str()); | |
| return std::move(Emb); | |
| } | |
| Expected<Embedding> IR2VecTool::getFunctionEmbedding(const Function &F, | |
| IR2VecKind Kind) const { | |
| auto Emb = createIR2VecEmbedder(F, Kind); | |
| if (!Emb) | |
| return Emb.takeError(); | |
| return (*Emb)->getFunctionVector(); | |
| } | |
| Expected<FuncEmbMap> | |
| IR2VecTool::getFunctionEmbeddingsMap(IR2VecKind Kind) const { | |
| FuncEmbMap Result; | |
| for (const Function &F : M.getFunctionDefs()) { | |
| auto Emb = getFunctionEmbedding(F, Kind); | |
| if (!Emb) | |
| return Emb.takeError(); | |
| Result.try_emplace(&F, std::move(*Emb)); | |
| } | |
| return Result; | |
| } | |
| Expected<BBEmbeddingsMap> | |
| IR2VecTool::getBBEmbeddingsMap(const Function &F, IR2VecKind Kind) const { | |
| auto Emb = createIR2VecEmbedder(F, Kind); | |
| if (!Emb) | |
| return Emb.takeError(); | |
| BBEmbeddingsMap Result; | |
| for (const BasicBlock &BB : F) | |
| Result.try_emplace(&BB, (*Emb)->getBBVector(BB)); | |
| return Result; | |
| } | |
| Expected<InstEmbeddingsMap> | |
| IR2VecTool::getInstEmbeddingsMap(const Function &F, IR2VecKind Kind) const { | |
| auto Emb = createIR2VecEmbedder(F, Kind); | |
| if (!Emb) | |
| return Emb.takeError(); | |
| InstEmbeddingsMap Result; | |
| for (const Instruction &I : instructions(F)) | |
| Result.try_emplace(&I, (*Emb)->getInstVector(I)); | |
| return Result; | |
| } | |
| void IR2VecTool::writeEmbeddingsToStream(raw_ostream &OS, | |
| EmbeddingLevel Level) const { | |
| for (const Function &F : M.getFunctionDefs()) | |
| writeEmbeddingsToStream(F, OS, Level); | |
| } | |
| void IR2VecTool::writeEmbeddingsToStream(const Function &F, raw_ostream &OS, | |
| EmbeddingLevel Level) const { | |
| auto IR2VecEmbedderObj = createIR2VecEmbedder(F, IR2VecEmbeddingKind); | |
| if (!IR2VecEmbedderObj) { | |
| WithColor::error(errs(), ToolName) | |
| << toString(IR2VecEmbedderObj.takeError()) << "\n"; | |
| return; | |
| } | |
| auto Emb = std::move(*IR2VecEmbedderObj); | |
| OS << "Function: " << F.getName() << "\n"; | |
| // Generate embeddings based on the specified level | |
| switch (Level) { | |
| case FunctionLevel: | |
| Emb->getFunctionVector().print(OS); | |
| break; | |
| case BasicBlockLevel: | |
| for (const BasicBlock &BB : F) { | |
| OS << BB.getName() << ":"; | |
| Emb->getBBVector(BB).print(OS); | |
| } | |
| break; | |
| case InstructionLevel: | |
| for (const Instruction &I : instructions(F)) { | |
| OS << I; | |
| Emb->getInstVector(I).print(OS); | |
| } | |
| break; | |
| } | |
| } | |
| } // namespace ir2vec | |
| } // namespace llvm |