mirror of
https://github.com/llvm/llvm-project.git
synced 2026-10-26 13:53:31 +08:00
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
717 lines
27 KiB
C++
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);
|
|
}
|