blob: 1a14c687295cad289771461a0c3b35128ef08dfe [file] [log] [blame]
#include "MCTargetDesc/X86MCTargetDesc.h"
#include "Views/SummaryView.h"
#include "X86TestBase.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MCA/CustomBehaviour.h"
#include "llvm/MCA/IncrementalSourceMgr.h"
#include "llvm/MCA/InstrBuilder.h"
#include "llvm/MCA/Pipeline.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <unordered_map>
using namespace llvm;
using namespace mca;
TEST_F(X86TestBase, TestResumablePipeline) {
mca::Context MCA(*MRI, *STI);
mca::IncrementalSourceMgr ISM;
// Empty CustomBehaviour.
auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);
auto PO = getDefaultPipelineOptions();
auto P = MCA.createDefaultPipeline(PO, ISM, *CB);
ASSERT_TRUE(P);
SmallVector<MCInst> MCIs;
getSimpleInsts(MCIs, /*Repeats=*/100);
// Add views.
auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), MCIs,
PO.DispatchWidth);
P->addEventListener(SV.get());
auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, /*CallLatency=*/100);
const SmallVector<mca::Instrument *> Instruments;
// Tile size = 7
for (unsigned i = 0U, E = MCIs.size(); i < E;) {
for (unsigned TE = i + 7; i < TE && i < E; ++i) {
Expected<std::unique_ptr<mca::Instruction>> InstOrErr =
IB.createInstruction(MCIs[i], Instruments);
ASSERT_TRUE(bool(InstOrErr));
ISM.addInst(std::move(InstOrErr.get()));
}
// Run the pipeline.
Expected<unsigned> Cycles = P->run();
if (!Cycles) {
// Should be a stream pause error.
ASSERT_TRUE(Cycles.errorIsA<mca::InstStreamPause>());
llvm::consumeError(Cycles.takeError());
}
}
ISM.endOfStream();
// Has to terminate properly.
Expected<unsigned> Cycles = P->run();
ASSERT_TRUE(bool(Cycles));
json::Value Result = SV->toJSON();
auto *ResultObj = Result.getAsObject();
ASSERT_TRUE(ResultObj);
// Run the baseline.
json::Object BaselineResult;
auto E = runBaselineMCA(BaselineResult, MCIs);
ASSERT_FALSE(bool(E)) << "Failed to run baseline";
auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString());
ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result";
// Compare the results.
constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps",
"BlockRThroughput"};
for (const auto *F : Fields) {
auto V = ResultObj->getInteger(F);
auto BV = BaselineObj->getInteger(F);
ASSERT_TRUE(V && BV);
ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";
}
}
TEST_F(X86TestBase, TestInstructionRecycling) {
mca::Context MCA(*MRI, *STI);
std::unordered_map<const mca::InstrDesc *, SmallPtrSet<mca::Instruction *, 2>>
RecycledInsts;
auto GetRecycledInst = [&](const mca::InstrDesc &Desc) -> mca::Instruction * {
auto It = RecycledInsts.find(&Desc);
if (It != RecycledInsts.end()) {
auto &Insts = It->second;
if (Insts.size()) {
mca::Instruction *I = *Insts.begin();
Insts.erase(I);
return I;
}
}
return nullptr;
};
auto AddRecycledInst = [&](mca::Instruction *I) {
const mca::InstrDesc &D = I->getDesc();
RecycledInsts[&D].insert(I);
};
mca::IncrementalSourceMgr ISM;
ISM.setOnInstFreedCallback(AddRecycledInst);
// Empty CustomBehaviour.
auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);
auto PO = getDefaultPipelineOptions();
auto P = MCA.createDefaultPipeline(PO, ISM, *CB);
ASSERT_TRUE(P);
SmallVector<MCInst> MCIs;
getSimpleInsts(MCIs, /*Repeats=*/100);
// Add views.
auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), MCIs,
PO.DispatchWidth);
P->addEventListener(SV.get());
// Default InstrumentManager
auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, /*CallLatency=*/100);
IB.setInstRecycleCallback(GetRecycledInst);
const SmallVector<mca::Instrument *> Instruments;
// Tile size = 7
for (unsigned i = 0U, E = MCIs.size(); i < E;) {
for (unsigned TE = i + 7; i < TE && i < E; ++i) {
Expected<std::unique_ptr<mca::Instruction>> InstOrErr =
IB.createInstruction(MCIs[i], Instruments);
if (!InstOrErr) {
mca::Instruction *RecycledInst = nullptr;
// Check if the returned instruction is a recycled
// one.
auto RemainingE = handleErrors(InstOrErr.takeError(),
[&](const mca::RecycledInstErr &RC) {
RecycledInst = RC.getInst();
});
ASSERT_FALSE(bool(RemainingE));
ASSERT_TRUE(RecycledInst);
ISM.addRecycledInst(RecycledInst);
} else {
ISM.addInst(std::move(InstOrErr.get()));
}
}
// Run the pipeline.
Expected<unsigned> Cycles = P->run();
if (!Cycles) {
// Should be a stream pause error.
ASSERT_TRUE(Cycles.errorIsA<mca::InstStreamPause>());
llvm::consumeError(Cycles.takeError());
}
}
ISM.endOfStream();
// Has to terminate properly.
Expected<unsigned> Cycles = P->run();
ASSERT_TRUE(bool(Cycles));
json::Value Result = SV->toJSON();
auto *ResultObj = Result.getAsObject();
ASSERT_TRUE(ResultObj);
// Run the baseline.
json::Object BaselineResult;
auto E = runBaselineMCA(BaselineResult, MCIs);
ASSERT_FALSE(bool(E)) << "Failed to run baseline";
auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString());
ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result";
// Compare the results.
constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps",
"BlockRThroughput"};
for (const auto *F : Fields) {
auto V = ResultObj->getInteger(F);
auto BV = BaselineObj->getInteger(F);
ASSERT_TRUE(V && BV);
ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";
}
}
// Test that we do not depend upon the MCInst address for variant description
// construction. This test creates two instructions that will use variant
// description as they are both zeroing idioms, but write to different
// registers. If the key used to access the variant instruction description is
// the same between the descriptions (like the MCInst pointer), we will run into
// an assertion failure due to the different writes.
TEST_F(X86TestBase, TestVariantInstructionsSameAddress) {
mca::Context MCA(*MRI, *STI);
mca::IncrementalSourceMgr ISM;
// Empty CustomBehaviour.
auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);
auto PO = getDefaultPipelineOptions();
auto P = MCA.createDefaultPipeline(PO, ISM, *CB);
ASSERT_TRUE(P);
auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, 100);
const SmallVector<mca::Instrument *> Instruments;
MCInst InstructionToAdd;
InstructionToAdd = MCInstBuilder(X86::XOR64rr)
.addReg(X86::RAX)
.addReg(X86::RAX)
.addReg(X86::RAX);
Expected<std::unique_ptr<mca::Instruction>> Instruction1OrErr =
IB.createInstruction(InstructionToAdd, Instruments);
ASSERT_TRUE(static_cast<bool>(Instruction1OrErr));
ISM.addInst(std::move(Instruction1OrErr.get()));
InstructionToAdd = MCInstBuilder(X86::XORPSrr)
.addReg(X86::XMM0)
.addReg(X86::XMM0)
.addReg(X86::XMM0);
Expected<std::unique_ptr<mca::Instruction>> Instruction2OrErr =
IB.createInstruction(InstructionToAdd, Instruments);
ASSERT_TRUE(static_cast<bool>(Instruction2OrErr));
ISM.addInst(std::move(Instruction2OrErr.get()));
ISM.endOfStream();
Expected<unsigned> Cycles = P->run();
ASSERT_TRUE(static_cast<bool>(Cycles));
}