Files
Fangrui Song 340c69d7db [CodeGen] Stop exporting cl::opts. NFC (#228728)
AMDGPU reads -misched-prera-direction and -verify-misched, every
target's TTI reads -partial-unrolling-threshold through BasicTTIImpl.h,
and AsmPrinter reads -emit-bb-hash through extern declarations. Add
getters for them, move inline code that reads options (LiveIntervals,
MachinePipeliner, MIR2Vec) out of line, and let unittests use default
weights, so that the options can be file-local.

Aided by Opus 5.5
2026-10-03 18:11:39 -07:00

717 lines
27 KiB
C++

//===- MIR2Vec.cpp - Implementation of MIR2Vec ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM
// Exceptions. See the LICENSE file for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements the MIR2Vec algorithm for Machine IR embeddings.
///
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/MIR2Vec.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Regex.h"
using namespace llvm;
using namespace mir2vec;
#define DEBUG_TYPE "mir2vec"
STATISTIC(MIRVocabMissCounter,
"Number of lookups to MIR entities not present in the vocabulary");
STATISTIC(MIRClasslessRegCounter,
"Number of register operands with no register class");
namespace llvm {
namespace mir2vec {
cl::OptionCategory MIR2VecCategory("MIR2Vec Options");
// FIXME: Use a default vocab when not specified
static cl::opt<std::string>
VocabFile("mir2vec-vocab-path",
cl::desc("Path to the vocabulary file for MIR2Vec"), cl::init(""),
cl::cat(MIR2VecCategory));
static cl::opt<float>
OpcWeight("mir2vec-opc-weight", cl::init(1.0),
cl::desc("Weight for machine opcode embeddings"),
cl::cat(MIR2VecCategory));
static cl::opt<float>
CommonOperandWeight("mir2vec-common-operand-weight", cl::init(1.0),
cl::desc("Weight for common operand embeddings"),
cl::cat(MIR2VecCategory));
static cl::opt<float>
RegOperandWeight("mir2vec-reg-operand-weight", cl::init(1.0),
cl::desc("Weight for register operand embeddings"),
cl::cat(MIR2VecCategory));
cl::opt<MIR2VecKind> MIR2VecEmbeddingKind(
"mir2vec-kind",
cl::values(clEnumValN(MIR2VecKind::Symbolic, "symbolic",
"Generate symbolic embeddings for MIR")),
cl::init(MIR2VecKind::Symbolic), cl::desc("MIR2Vec embedding kind"),
cl::cat(MIR2VecCategory));
static cl::opt<bool> PrintAllVocabEntries(
"mir2vec-print-all-vocab-entries", cl::init(false),
cl::desc("Print all vocabulary entries including zero embeddings"),
cl::cat(MIR2VecCategory));
} // namespace mir2vec
} // namespace llvm
//===----------------------------------------------------------------------===//
// Vocabulary
//===----------------------------------------------------------------------===//
MIRVocabulary::MIRVocabulary(VocabMap &&OpcodeMap, VocabMap &&CommonOperandMap,
VocabMap &&PhysicalRegisterMap,
VocabMap &&VirtualRegisterMap,
const TargetInstrInfo &TII,
const TargetRegisterInfo &TRI,
const MachineRegisterInfo &MRI)
: TII(TII), TRI(TRI), MRI(MRI) {
buildCanonicalOpcodeMapping();
unsigned CanonicalOpcodeCount = UniqueBaseOpcodeNames.size();
assert(CanonicalOpcodeCount > 0 &&
"No canonical opcodes found for target - invalid vocabulary");
buildRegisterOperandMapping();
// Define layout of vocabulary sections
Layout.OpcodeBase = 0;
Layout.CommonOperandBase = CanonicalOpcodeCount;
// We expect same classes for physical and virtual registers
Layout.PhyRegBase = Layout.CommonOperandBase + std::size(CommonOperandNames);
Layout.VirtRegBase = Layout.PhyRegBase + RegisterOperandNames.size();
generateStorage(OpcodeMap, CommonOperandMap, PhysicalRegisterMap,
VirtualRegisterMap);
Layout.TotalEntries = Storage.size();
}
Expected<MIRVocabulary>
MIRVocabulary::create(VocabMap &&OpcodeMap, VocabMap &&CommonOperandMap,
VocabMap &&PhyRegMap, VocabMap &&VirtRegMap,
const TargetInstrInfo &TII, const TargetRegisterInfo &TRI,
const MachineRegisterInfo &MRI) {
if (OpcodeMap.empty() || CommonOperandMap.empty() || PhyRegMap.empty() ||
VirtRegMap.empty())
return createStringError(errc::invalid_argument,
"Empty vocabulary entries provided");
MIRVocabulary Vocab(std::move(OpcodeMap), std::move(CommonOperandMap),
std::move(PhyRegMap), std::move(VirtRegMap), TII, TRI,
MRI);
// Validate Storage after construction
if (!Vocab.Storage.isValid())
return createStringError(errc::invalid_argument,
"Failed to create valid vocabulary storage");
Vocab.ZeroEmbedding = Embedding(Vocab.Storage.getDimension(), 0.0);
return std::move(Vocab);
}
std::string MIRVocabulary::extractBaseOpcodeName(StringRef InstrName) {
// Extract base instruction name using regex to capture letters and
// underscores Examples: "ADD32rr" -> "ADD", "ARITH_FENCE" -> "ARITH_FENCE"
//
// TODO: Consider more sophisticated extraction:
// - Handle complex prefixes like "AVX1_SETALLONES" correctly (Currently, it
// would naively map to "AVX")
// - Extract width suffixes (8,16,32,64) as separate features
// - Capture addressing mode suffixes (r,i,m,ri,etc.) for better analysis
// (Currently, instances like "MOV32mi" map to "MOV", but "ADDPDrr" would map
// to "ADDPDrr")
assert(!InstrName.empty() && "Instruction name should not be empty");
// Use regex to extract initial sequence of letters and underscores
static const Regex BaseOpcodeRegex("([a-zA-Z_]+)");
SmallVector<StringRef, 2> Matches;
if (BaseOpcodeRegex.match(InstrName, &Matches) && Matches.size() > 1) {
StringRef Match = Matches[1];
// Trim trailing underscores
while (!Match.empty() && Match.back() == '_')
Match = Match.drop_back();
return Match.str();
}
// Fallback to original name if no pattern matches
return InstrName.str();
}
unsigned MIRVocabulary::getCanonicalIndexForBaseName(StringRef BaseName) const {
assert(!UniqueBaseOpcodeNames.empty() && "Canonical mapping not built");
auto It = std::find(UniqueBaseOpcodeNames.begin(),
UniqueBaseOpcodeNames.end(), BaseName.str());
assert(It != UniqueBaseOpcodeNames.end() &&
"Base name not found in unique opcodes");
return std::distance(UniqueBaseOpcodeNames.begin(), It);
}
unsigned MIRVocabulary::getCanonicalOpcodeIndex(unsigned Opcode) const {
auto BaseOpcode = extractBaseOpcodeName(TII.getName(Opcode));
return getCanonicalIndexForBaseName(BaseOpcode);
}
unsigned
MIRVocabulary::getCanonicalIndexForOperandName(StringRef OperandName) const {
auto It = std::find(std::begin(CommonOperandNames),
std::end(CommonOperandNames), OperandName);
assert(It != std::end(CommonOperandNames) &&
"Operand name not found in common operands");
return Layout.CommonOperandBase +
std::distance(std::begin(CommonOperandNames), It);
}
unsigned
MIRVocabulary::getCanonicalIndexForRegisterClass(StringRef RegName,
bool IsPhysical) const {
auto It = std::find(RegisterOperandNames.begin(), RegisterOperandNames.end(),
RegName);
assert(It != RegisterOperandNames.end() &&
"Register name not found in register operands");
unsigned LocalIndex = std::distance(RegisterOperandNames.begin(), It);
return (IsPhysical ? Layout.PhyRegBase : Layout.VirtRegBase) + LocalIndex;
}
std::string MIRVocabulary::getStringKey(unsigned Pos) const {
assert(Pos < Layout.TotalEntries && "Position out of bounds in vocabulary");
// Handle opcodes section
if (Pos < Layout.CommonOperandBase) {
// Convert canonical index back to base opcode name
auto It = UniqueBaseOpcodeNames.begin();
std::advance(It, Pos);
assert(It != UniqueBaseOpcodeNames.end() &&
"Canonical index out of bounds in opcode section");
return *It;
}
auto getLocalIndex = [](unsigned Pos, size_t BaseOffset, size_t Bound,
const char *Msg) {
unsigned LocalIndex = Pos - BaseOffset;
assert(LocalIndex < Bound && Msg);
return LocalIndex;
};
// Handle common operands section
if (Pos < Layout.PhyRegBase) {
unsigned LocalIndex = getLocalIndex(
Pos, Layout.CommonOperandBase, std::size(CommonOperandNames),
"Local index out of bounds in common operands");
return CommonOperandNames[LocalIndex].str();
}
// Handle physical registers section
if (Pos < Layout.VirtRegBase) {
unsigned LocalIndex =
getLocalIndex(Pos, Layout.PhyRegBase, RegisterOperandNames.size(),
"Local index out of bounds in physical registers");
return "PhyReg_" + RegisterOperandNames[LocalIndex];
}
// Handle virtual registers section
unsigned LocalIndex =
getLocalIndex(Pos, Layout.VirtRegBase, RegisterOperandNames.size(),
"Local index out of bounds in virtual registers");
return "VirtReg_" + RegisterOperandNames[LocalIndex];
}
void MIRVocabulary::generateStorage(const VocabMap &OpcodeMap,
const VocabMap &CommonOperandsMap,
const VocabMap &PhyRegMap,
const VocabMap &VirtRegMap) {
// Helper for handling missing entities in the vocabulary.
// Currently, we use a zero vector. In the future, we will throw an error to
// ensure that *all* known entities are present in the vocabulary.
auto handleMissingEntity = [](StringRef Key) {
LLVM_DEBUG(errs() << "MIR2Vec: Missing vocabulary entry for " << Key
<< "; using zero vector. This will result in an error "
"in the future.\n");
++MIRVocabMissCounter;
};
// Initialize opcode embeddings section
unsigned EmbeddingDim = OpcodeMap.begin()->second.size();
std::vector<Embedding> OpcodeEmbeddings(Layout.CommonOperandBase,
Embedding(EmbeddingDim));
// Populate opcode embeddings using canonical mapping
for (auto COpcodeName : UniqueBaseOpcodeNames) {
if (auto It = OpcodeMap.find(COpcodeName); It != OpcodeMap.end()) {
auto COpcodeIndex = getCanonicalIndexForBaseName(COpcodeName);
assert(COpcodeIndex < Layout.CommonOperandBase &&
"Canonical index out of bounds");
OpcodeEmbeddings[COpcodeIndex] = It->second;
} else {
handleMissingEntity(COpcodeName);
}
}
// Initialize common operand embeddings section
std::vector<Embedding> CommonOperandEmbeddings(std::size(CommonOperandNames),
Embedding(EmbeddingDim));
unsigned OperandIndex = 0;
for (const auto &CommonOperandName : CommonOperandNames) {
if (auto It = CommonOperandsMap.find(CommonOperandName.str());
It != CommonOperandsMap.end()) {
CommonOperandEmbeddings[OperandIndex] = It->second;
} else {
handleMissingEntity(CommonOperandName);
}
++OperandIndex;
}
// Helper lambda for creating register operand embeddings
auto createRegisterEmbeddings = [&](const VocabMap &RegMap) {
std::vector<Embedding> RegEmbeddings(TRI.getNumRegClasses(),
Embedding(EmbeddingDim));
unsigned RegOperandIndex = 0;
for (const auto &RegOperandName : RegisterOperandNames) {
if (auto It = RegMap.find(RegOperandName); It != RegMap.end())
RegEmbeddings[RegOperandIndex] = It->second;
else
handleMissingEntity(RegOperandName);
++RegOperandIndex;
}
return RegEmbeddings;
};
// Initialize register operand embeddings sections
std::vector<Embedding> PhyRegEmbeddings = createRegisterEmbeddings(PhyRegMap);
std::vector<Embedding> VirtRegEmbeddings =
createRegisterEmbeddings(VirtRegMap);
// Scale the vocabulary sections based on the provided weights
auto scaleVocabSection = [](std::vector<Embedding> &Embeddings,
double Weight) {
for (auto &Embedding : Embeddings)
Embedding *= Weight;
};
scaleVocabSection(OpcodeEmbeddings, OpcWeight);
scaleVocabSection(CommonOperandEmbeddings, CommonOperandWeight);
scaleVocabSection(PhyRegEmbeddings, RegOperandWeight);
scaleVocabSection(VirtRegEmbeddings, RegOperandWeight);
std::vector<std::vector<Embedding>> Sections(
static_cast<unsigned>(Section::MaxSections));
Sections[static_cast<unsigned>(Section::Opcodes)] =
std::move(OpcodeEmbeddings);
Sections[static_cast<unsigned>(Section::CommonOperands)] =
std::move(CommonOperandEmbeddings);
Sections[static_cast<unsigned>(Section::PhyRegisters)] =
std::move(PhyRegEmbeddings);
Sections[static_cast<unsigned>(Section::VirtRegisters)] =
std::move(VirtRegEmbeddings);
Storage = ir2vec::VocabStorage(std::move(Sections));
}
void MIRVocabulary::buildCanonicalOpcodeMapping() {
// Check if already built
if (!UniqueBaseOpcodeNames.empty())
return;
// Build mapping from opcodes to canonical base opcode indices
for (unsigned Opcode = 0; Opcode < TII.getNumOpcodes(); ++Opcode) {
std::string BaseOpcode = extractBaseOpcodeName(TII.getName(Opcode));
UniqueBaseOpcodeNames.insert(BaseOpcode);
}
LLVM_DEBUG(dbgs() << "MIR2Vec: Built canonical mapping for target with "
<< UniqueBaseOpcodeNames.size()
<< " unique base opcodes\n");
}
void MIRVocabulary::buildRegisterOperandMapping() {
// Check if already built
if (!RegisterOperandNames.empty())
return;
for (unsigned RC = 0; RC < TRI.getNumRegClasses(); ++RC) {
const TargetRegisterClass *RegClass = TRI.getRegClass(RC);
if (!RegClass)
continue;
// Get the register class name
StringRef ClassName = TRI.getRegClassName(RegClass);
RegisterOperandNames.push_back(ClassName.str());
}
}
unsigned MIRVocabulary::getCommonOperandIndex(
MachineOperand::MachineOperandType OperandType) const {
assert(OperandType != MachineOperand::MO_Register &&
"Expected non-register operand type");
assert(OperandType > MachineOperand::MO_Register &&
OperandType < MachineOperand::MO_Last && "Operand type out of bounds");
return static_cast<unsigned>(OperandType) - 1;
}
std::optional<unsigned>
MIRVocabulary::getRegisterOperandIndex(Register Reg) const {
assert(!RegisterOperandNames.empty() && "Register operand mapping not built");
assert(Reg.isValid() && "Invalid register; not expected here");
assert((Reg.isPhysical() || Reg.isVirtual()) &&
"Expected a physical or virtual register");
const TargetRegisterClass *RegClass = nullptr;
// For physical registers, use TRI to get minimal register class as a
// physical register can belong to multiple classes. For virtual
// registers, use MRI to uniquely identify the assigned register class.
if (Reg.isPhysical())
RegClass = TRI.getMinimalPhysRegClass(Reg);
else
RegClass = MRI.getRegClassOrNull(Reg);
// Not every register belongs to a register class. This can happen for
// physical registers, e.g. X86's $mxcsr and $fpcw or AMDGPU's $mode, for
// which getMinimalPhysRegClass() returns nullptr. It can also happen for
// generic virtual registers that have not yet been through (or completed)
// GlobalISel's register bank selection, and thus carry an LLT or a
// RegisterBank instead of a TargetRegisterClass, for which
// getRegClassOrNull() returns nullptr.
// TODO: Avoid special-casing these registers at every use site. Classless
// registers currently fall back to a zero embedding in operator[] and to
// VirtRegBase in getEntityIDForRegister(), which is the same ad-hoc handling
// the invalid/stack-slot cases already get. Give them a real vocabulary
// representation instead -- e.g. an explicit "no register class" entry, or
// keying generic vregs on their LLT/RegisterBank -- so that the lookup is
// total and the callers need no fallbacks.
if (!RegClass) {
LLVM_DEBUG(errs() << "MIR2Vec: No register class for register " << Reg.id()
<< "; using zero vector.\n");
++MIRClasslessRegCounter;
return std::nullopt;
}
return RegClass->getID();
}
Expected<MIRVocabulary> MIRVocabulary::createDummyVocabForTest(
const TargetInstrInfo &TII, const TargetRegisterInfo &TRI,
const MachineRegisterInfo &MRI, unsigned Dim) {
assert(Dim > 0 && "Dimension must be greater than zero");
float DummyVal = 0.1f;
VocabMap DummyOpcMap, DummyOperandMap, DummyPhyRegMap, DummyVirtRegMap;
// Process opcodes directly without creating temporary vocabulary
for (unsigned Opcode = 0; Opcode < TII.getNumOpcodes(); ++Opcode) {
std::string BaseOpcode = extractBaseOpcodeName(TII.getName(Opcode));
if (DummyOpcMap.count(BaseOpcode) == 0) { // Only add if not already present
DummyOpcMap[BaseOpcode] = Embedding(Dim, DummyVal);
DummyVal += 0.1f;
}
}
// Add common operands
for (const auto &CommonOperandName : CommonOperandNames) {
DummyOperandMap[CommonOperandName.str()] = Embedding(Dim, DummyVal);
DummyVal += 0.1f;
}
// Process register classes directly
for (unsigned RC = 0; RC < TRI.getNumRegClasses(); ++RC) {
const TargetRegisterClass *RegClass = TRI.getRegClass(RC);
if (!RegClass)
continue;
std::string ClassName = TRI.getRegClassName(RegClass);
DummyPhyRegMap[ClassName] = Embedding(Dim, DummyVal);
DummyVirtRegMap[ClassName] = Embedding(Dim, DummyVal);
DummyVal += 0.1f;
}
// Create vocabulary directly without temporary instance
return MIRVocabulary::create(
std::move(DummyOpcMap), std::move(DummyOperandMap),
std::move(DummyPhyRegMap), std::move(DummyVirtRegMap), TII, TRI, MRI);
}
//===----------------------------------------------------------------------===//
// MIR2VecVocabProvider and MIR2VecVocabLegacyAnalysis
//===----------------------------------------------------------------------===//
Expected<mir2vec::MIRVocabulary>
MIR2VecVocabProvider::getVocabulary(const Module &M) {
VocabMap OpcVocab, CommonOperandVocab, PhyRegVocabMap, VirtRegVocabMap;
if (Error Err = readVocabulary(OpcVocab, CommonOperandVocab, PhyRegVocabMap,
VirtRegVocabMap))
return std::move(Err);
for (const auto &F : M) {
if (F.isDeclaration())
continue;
if (auto *MF = MMI.getMachineFunction(F)) {
auto &Subtarget = MF->getSubtarget();
if (const auto *TII = Subtarget.getInstrInfo())
if (const auto *TRI = Subtarget.getRegisterInfo())
return mir2vec::MIRVocabulary::create(
std::move(OpcVocab), std::move(CommonOperandVocab),
std::move(PhyRegVocabMap), std::move(VirtRegVocabMap), *TII, *TRI,
MF->getRegInfo());
}
}
return createStringError(errc::invalid_argument,
"No machine functions found in module");
}
Error MIR2VecVocabProvider::readVocabulary(VocabMap &OpcodeVocab,
VocabMap &CommonOperandVocab,
VocabMap &PhyRegVocabMap,
VocabMap &VirtRegVocabMap) {
if (VocabFile.empty())
return createStringError(
errc::invalid_argument,
"MIR2Vec vocabulary file path not specified; set it "
"using --mir2vec-vocab-path");
auto BufOrError = MemoryBuffer::getFileOrSTDIN(VocabFile, /*IsText=*/true);
if (!BufOrError)
return createFileError(VocabFile, BufOrError.getError());
auto Content = BufOrError.get()->getBuffer();
Expected<json::Value> ParsedVocabValue = json::parse(Content);
if (!ParsedVocabValue)
return ParsedVocabValue.takeError();
unsigned OpcodeDim = 0, CommonOperandDim = 0, PhyRegOperandDim = 0,
VirtRegOperandDim = 0;
if (auto Err = ir2vec::VocabStorage::parseVocabSection(
"Opcodes", *ParsedVocabValue, OpcodeVocab, OpcodeDim))
return Err;
if (auto Err = ir2vec::VocabStorage::parseVocabSection(
"CommonOperands", *ParsedVocabValue, CommonOperandVocab,
CommonOperandDim))
return Err;
if (auto Err = ir2vec::VocabStorage::parseVocabSection(
"PhysicalRegisters", *ParsedVocabValue, PhyRegVocabMap,
PhyRegOperandDim))
return Err;
if (auto Err = ir2vec::VocabStorage::parseVocabSection(
"VirtualRegisters", *ParsedVocabValue, VirtRegVocabMap,
VirtRegOperandDim))
return Err;
// All sections must have the same embedding dimension
if (!(OpcodeDim == CommonOperandDim && CommonOperandDim == PhyRegOperandDim &&
PhyRegOperandDim == VirtRegOperandDim)) {
return createStringError(
errc::illegal_byte_sequence,
"MIR2Vec vocabulary sections have different dimensions");
}
return Error::success();
}
char MIR2VecVocabLegacyAnalysis::ID = 0;
INITIALIZE_PASS_BEGIN(MIR2VecVocabLegacyAnalysis, "mir2vec-vocab-analysis",
"MIR2Vec Vocabulary Analysis", false, true)
INITIALIZE_PASS_DEPENDENCY(MachineModuleInfoWrapperPass)
INITIALIZE_PASS_END(MIR2VecVocabLegacyAnalysis, "mir2vec-vocab-analysis",
"MIR2Vec Vocabulary Analysis", false, true)
StringRef MIR2VecVocabLegacyAnalysis::getPassName() const {
return "MIR2Vec Vocabulary Analysis";
}
//===----------------------------------------------------------------------===//
// MIREmbedder and its subclasses
//===----------------------------------------------------------------------===//
std::unique_ptr<MIREmbedder> MIREmbedder::create(MIR2VecKind Mode,
const MachineFunction &MF,
const MIRVocabulary &Vocab) {
switch (Mode) {
case MIR2VecKind::Symbolic:
return std::make_unique<SymbolicMIREmbedder>(MF, Vocab);
}
return nullptr;
}
MIREmbedder::MIREmbedder(const MachineFunction &MF, const MIRVocabulary &Vocab)
: MF(MF), Vocab(Vocab), Dimension(Vocab.getDimension()),
OpcWeight(mir2vec::OpcWeight),
CommonOperandWeight(mir2vec::CommonOperandWeight),
RegOperandWeight(mir2vec::RegOperandWeight) {}
Embedding MIREmbedder::computeEmbeddings(const MachineBasicBlock &MBB) const {
Embedding MBBVector(Dimension, 0);
// Get instruction info for opcode name resolution
const auto &Subtarget = MF.getSubtarget();
const auto *TII = Subtarget.getInstrInfo();
if (!TII) {
MF.getFunction().getContext().emitError(
"MIR2Vec: No TargetInstrInfo available; cannot compute embeddings");
return MBBVector;
}
// Process each machine instruction in the basic block
for (const auto &MI : MBB) {
// Skip debug instructions and other metadata
if (MI.isDebugInstr())
continue;
MBBVector += computeEmbeddings(MI);
}
return MBBVector;
}
Embedding MIREmbedder::computeEmbeddings() const {
Embedding MFuncVector(Dimension, 0);
if (MF.empty())
return MFuncVector;
// Consider all reachable machine basic blocks in the function
for (const auto *MBB : depth_first(&MF))
MFuncVector += computeEmbeddings(*MBB);
return MFuncVector;
}
SymbolicMIREmbedder::SymbolicMIREmbedder(const MachineFunction &MF,
const MIRVocabulary &Vocab)
: MIREmbedder(MF, Vocab) {}
std::unique_ptr<SymbolicMIREmbedder>
SymbolicMIREmbedder::create(const MachineFunction &MF,
const MIRVocabulary &Vocab) {
return std::make_unique<SymbolicMIREmbedder>(MF, Vocab);
}
Embedding SymbolicMIREmbedder::computeEmbeddings(const MachineInstr &MI) const {
// Skip debug instructions and other metadata
if (MI.isDebugInstr())
return Embedding(Dimension, 0);
// Opcode embedding
Embedding InstructionEmbedding = Vocab[MI.getOpcode()];
// Add operand contributions
for (const MachineOperand &MO : MI.operands())
InstructionEmbedding += Vocab[MO];
return InstructionEmbedding;
}
//===----------------------------------------------------------------------===//
// Printer Passes
//===----------------------------------------------------------------------===//
char MIR2VecVocabPrinterLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(MIR2VecVocabPrinterLegacyPass, "print-mir2vec-vocab",
"MIR2Vec Vocabulary Printer Pass", false, true)
INITIALIZE_PASS_DEPENDENCY(MIR2VecVocabLegacyAnalysis)
INITIALIZE_PASS_DEPENDENCY(MachineModuleInfoWrapperPass)
INITIALIZE_PASS_END(MIR2VecVocabPrinterLegacyPass, "print-mir2vec-vocab",
"MIR2Vec Vocabulary Printer Pass", false, true)
bool MIR2VecVocabPrinterLegacyPass::runOnMachineFunction(MachineFunction &MF) {
return false;
}
bool MIR2VecVocabPrinterLegacyPass::doFinalization(Module &M) {
auto &Analysis = getAnalysis<MIR2VecVocabLegacyAnalysis>();
auto MIR2VecVocabOrErr = Analysis.getMIR2VecVocabulary(M);
if (!MIR2VecVocabOrErr) {
OS << "MIR2Vec Vocabulary Printer: Failed to get vocabulary - "
<< toString(MIR2VecVocabOrErr.takeError()) << "\n";
return false;
}
auto &MIR2VecVocab = *MIR2VecVocabOrErr;
unsigned Pos = 0;
for (const auto &Entry : MIR2VecVocab) {
// Skip zero embeddings to avoid printing entries not in the vocabulary.
// This makes the output stable across changes to the opcode list.
if (PrintAllVocabEntries || !Entry.isZero()) {
OS << "Key: " << MIR2VecVocab.getStringKey(Pos) << ": ";
Entry.print(OS);
}
++Pos;
}
return false;
}
MachineFunctionPass *
llvm::createMIR2VecVocabPrinterLegacyPass(raw_ostream &OS) {
return new MIR2VecVocabPrinterLegacyPass(OS);
}
char MIR2VecPrinterLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(MIR2VecPrinterLegacyPass, "print-mir2vec",
"MIR2Vec Embedder Printer Pass", false, true)
INITIALIZE_PASS_DEPENDENCY(MIR2VecVocabLegacyAnalysis)
INITIALIZE_PASS_DEPENDENCY(MachineModuleInfoWrapperPass)
INITIALIZE_PASS_END(MIR2VecPrinterLegacyPass, "print-mir2vec",
"MIR2Vec Embedder Printer Pass", false, true)
bool MIR2VecPrinterLegacyPass::runOnMachineFunction(MachineFunction &MF) {
auto &Analysis = getAnalysis<MIR2VecVocabLegacyAnalysis>();
auto VocabOrErr =
Analysis.getMIR2VecVocabulary(*MF.getFunction().getParent());
assert(VocabOrErr && "Failed to get MIR2Vec vocabulary");
auto &MIRVocab = *VocabOrErr;
auto Emb = mir2vec::MIREmbedder::create(MIR2VecEmbeddingKind, MF, MIRVocab);
if (!Emb) {
OS << "Error creating MIR2Vec embeddings for function " << MF.getName()
<< "\n";
return false;
}
OS << "MIR2Vec embeddings for machine function " << MF.getName() << ":\n";
OS << "Machine Function vector: ";
Emb->getMFunctionVector().print(OS);
OS << "Machine basic block vectors:\n";
for (const MachineBasicBlock &MBB : MF) {
OS << "Machine basic block: " << MBB.getFullName() << ":\n";
Emb->getMBBVector(MBB).print(OS);
}
OS << "Machine instruction vectors:\n";
for (const MachineBasicBlock &MBB : MF) {
for (const MachineInstr &MI : MBB) {
// Skip debug instructions as they are not
// embedded
if (MI.isDebugInstr())
continue;
OS << "Machine instruction: ";
MI.print(OS);
Emb->getMInstVector(MI).print(OS);
}
}
return false;
}
MachineFunctionPass *llvm::createMIR2VecPrinterLegacyPass(raw_ostream &OS) {
return new MIR2VecPrinterLegacyPass(OS);
}