//===- 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> loadVocabulary(StringRef VocabPath) { auto VocabOrErr = Vocabulary::fromFile(VocabPath); if (!VocabOrErr) return VocabOrErr.takeError(); auto V = std::make_shared(std::move(*VocabOrErr)); if (!V->isValid()) return createStringError(errc::invalid_argument, "Failed to initialize IR2Vec vocabulary"); return V; } Error IR2VecTool::setVocabulary(std::shared_ptr 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> 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 IR2VecTool::getFunctionEmbedding(const Function &F, IR2VecKind Kind) const { auto Emb = createIR2VecEmbedder(F, Kind); if (!Emb) return Emb.takeError(); return (*Emb)->getFunctionVector(); } Expected 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 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 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