51 changes: 51 additions & 0 deletions llvm/lib/MCA/IncrementalSourceMgr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===-------------------- IncrementalSourceMgr.cpp ------------------------===//
//
// 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 defines some implementations for IncrementalSourceMgr.
///
//===----------------------------------------------------------------------===//

#include "llvm/MCA/IncrementalSourceMgr.h"
#ifndef NDEBUG
#include "llvm/Support/Format.h"
#endif

using namespace llvm;
using namespace llvm::mca;

void IncrementalSourceMgr::clear() {
Staging.clear();
InstStorage.clear();
TotalCounter = 0U;
EOS = false;
}

void IncrementalSourceMgr::updateNext() {
++TotalCounter;
Instruction *I = Staging.front();
Staging.pop_front();
I->reset();

if (InstFreedCB)
InstFreedCB(I);
}

#ifndef NDEBUG
void IncrementalSourceMgr::printStatistic(raw_ostream &OS) {
unsigned MaxInstStorageSize = InstStorage.size();
if (MaxInstStorageSize <= TotalCounter) {
auto Ratio = double(MaxInstStorageSize) / double(TotalCounter);
OS << "Cache ratio = " << MaxInstStorageSize << " / " << TotalCounter
<< llvm::format(" (%.2f%%)", (1.0 - Ratio) * 100.0) << "\n";
} else {
OS << "Error: Number of created instructions "
<< "are larger than the number of issued instructions\n";
}
}
#endif
77 changes: 63 additions & 14 deletions llvm/lib/MCA/InstrBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@
#include "llvm/MCA/InstrBuilder.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/MC/MCInst.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"

#define DEBUG_TYPE "llvm-mca"
#define DEBUG_TYPE "llvm-mca-instrbuilder"

namespace llvm {
namespace mca {

char RecycledInstErr::ID = 0;

InstrBuilder::InstrBuilder(const llvm::MCSubtargetInfo &sti,
const llvm::MCInstrInfo &mcii,
const llvm::MCRegisterInfo &mri,
Expand Down Expand Up @@ -612,7 +615,7 @@ InstrBuilder::createInstrDescImpl(const MCInst &MCI) {

// Now add the new descriptor.
bool IsVariadic = MCDesc.isVariadic();
if (!IsVariadic && !IsVariant) {
if ((ID->IsRecyclable = !IsVariadic && !IsVariant)) {
Descriptors[MCI.getOpcode()] = std::move(ID);
return *Descriptors[MCI.getOpcode()];
}
Expand All @@ -632,14 +635,32 @@ InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
return createInstrDescImpl(MCI);
}

STATISTIC(NumVariantInst, "Number of MCInsts that doesn't have static Desc");

Expected<std::unique_ptr<Instruction>>
InstrBuilder::createInstruction(const MCInst &MCI) {
Expected<const InstrDesc &> DescOrErr = getOrCreateInstrDesc(MCI);
if (!DescOrErr)
return DescOrErr.takeError();
const InstrDesc &D = *DescOrErr;
std::unique_ptr<Instruction> NewIS =
std::make_unique<Instruction>(D, MCI.getOpcode());
Instruction *NewIS = nullptr;
std::unique_ptr<Instruction> CreatedIS;
bool IsInstRecycled = false;

if (!D.IsRecyclable)
++NumVariantInst;

if (D.IsRecyclable && InstRecycleCB) {
if (auto *I = InstRecycleCB(D)) {
NewIS = I;
NewIS->reset();
IsInstRecycled = true;
}
}
if (!IsInstRecycled) {
CreatedIS = std::make_unique<Instruction>(D, MCI.getOpcode());
NewIS = CreatedIS.get();
}

const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
const MCSchedClassDesc &SCDesc =
Expand Down Expand Up @@ -668,6 +689,7 @@ InstrBuilder::createInstruction(const MCInst &MCI) {

// Initialize Reads first.
MCPhysReg RegID = 0;
size_t Idx = 0U;
for (const ReadDescriptor &RD : D.Reads) {
if (!RD.isImplicitRead()) {
// explicit read.
Expand All @@ -686,15 +708,22 @@ InstrBuilder::createInstruction(const MCInst &MCI) {
continue;

// Okay, this is a register operand. Create a ReadState for it.
NewIS->getUses().emplace_back(RD, RegID);
ReadState &RS = NewIS->getUses().back();
ReadState *RS = nullptr;
if (IsInstRecycled && Idx < NewIS->getUses().size()) {
NewIS->getUses()[Idx] = ReadState(RD, RegID);
RS = &NewIS->getUses()[Idx++];
} else {
NewIS->getUses().emplace_back(RD, RegID);
RS = &NewIS->getUses().back();
++Idx;
}

if (IsDepBreaking) {
// A mask of all zeroes means: explicit input operands are not
// independent.
if (Mask.isZero()) {
if (!RD.isImplicitRead())
RS.setIndependentFromDef();
RS->setIndependentFromDef();
} else {
// Check if this register operand is independent according to `Mask`.
// Note that Mask may not have enough bits to describe all explicit and
Expand All @@ -704,15 +733,21 @@ InstrBuilder::createInstruction(const MCInst &MCI) {
if (Mask.getBitWidth() > RD.UseIndex) {
// Okay. This map describe register use `RD.UseIndex`.
if (Mask[RD.UseIndex])
RS.setIndependentFromDef();
RS->setIndependentFromDef();
}
}
}
}
if (IsInstRecycled && Idx < NewIS->getUses().size())
NewIS->getUses().pop_back_n(NewIS->getUses().size() - Idx);

// Early exit if there are no writes.
if (D.Writes.empty())
return std::move(NewIS);
if (D.Writes.empty()) {
if (IsInstRecycled)
return llvm::make_error<RecycledInstErr>(NewIS);
else
return std::move(CreatedIS);
}

// Track register writes that implicitly clear the upper portion of the
// underlying super-registers using an APInt.
Expand All @@ -725,6 +760,7 @@ InstrBuilder::createInstruction(const MCInst &MCI) {

// Initialize writes.
unsigned WriteIndex = 0;
Idx = 0U;
for (const WriteDescriptor &WD : D.Writes) {
RegID = WD.isImplicitWrite() ? WD.RegisterID
: MCI.getOperand(WD.OpIndex).getReg();
Expand All @@ -735,13 +771,26 @@ InstrBuilder::createInstruction(const MCInst &MCI) {
}

assert(RegID && "Expected a valid register ID!");
NewIS->getDefs().emplace_back(WD, RegID,
/* ClearsSuperRegs */ WriteMask[WriteIndex],
/* WritesZero */ IsZeroIdiom);
if (IsInstRecycled && Idx < NewIS->getDefs().size()) {
NewIS->getDefs()[Idx++] =
WriteState(WD, RegID,
/* ClearsSuperRegs */ WriteMask[WriteIndex],
/* WritesZero */ IsZeroIdiom);
} else {
NewIS->getDefs().emplace_back(WD, RegID,
/* ClearsSuperRegs */ WriteMask[WriteIndex],
/* WritesZero */ IsZeroIdiom);
++Idx;
}
++WriteIndex;
}
if (IsInstRecycled && Idx < NewIS->getDefs().size())
NewIS->getDefs().pop_back_n(NewIS->getDefs().size() - Idx);

return std::move(NewIS);
if (IsInstRecycled)
return llvm::make_error<RecycledInstErr>(NewIS);
else
return std::move(CreatedIS);
}
} // namespace mca
} // namespace llvm
12 changes: 12 additions & 0 deletions llvm/lib/MCA/Instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,18 @@ const CriticalDependency &Instruction::computeCriticalRegDep() {
return CriticalRegDep;
}

void Instruction::reset() {
// Note that this won't clear read/write descriptors
// or other non-trivial fields
Stage = IS_INVALID;
CyclesLeft = UNKNOWN_CYCLES;
clearOptimizableMove();
RCUTokenID = 0;
LSUTokenID = 0;
CriticalResourceMask = 0;
IsEliminated = false;
}

void Instruction::dispatch(unsigned RCUToken) {
assert(Stage == IS_INVALID);
Stage = IS_DISPATCHED;
Expand Down
15 changes: 13 additions & 2 deletions llvm/lib/MCA/Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ Expected<unsigned> Pipeline::run() {
assert(!Stages.empty() && "Unexpected empty pipeline found!");

do {
notifyCycleBegin();
if (!isPaused())
notifyCycleBegin();
if (Error Err = runCycle())
return std::move(Err);
notifyCycleEnd();
Expand All @@ -53,15 +54,25 @@ Error Pipeline::runCycle() {
// Update stages before we start processing new instructions.
for (auto I = Stages.rbegin(), E = Stages.rend(); I != E && !Err; ++I) {
const std::unique_ptr<Stage> &S = *I;
Err = S->cycleStart();
if (isPaused())
Err = S->cycleResume();
else
Err = S->cycleStart();
}

CurrentState = State::Started;

// Now fetch and execute new instructions.
InstRef IR;
Stage &FirstStage = *Stages[0];
while (!Err && FirstStage.isAvailable(IR))
Err = FirstStage.execute(IR);

if (Err.isA<InstStreamPause>()) {
CurrentState = State::Paused;
return Err;
}

// Update stages in preparation for a new cycle.
for (const std::unique_ptr<Stage> &S : Stages) {
Err = S->cycleEnd();
Expand Down
23 changes: 16 additions & 7 deletions llvm/lib/MCA/Stages/EntryStage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace llvm {
namespace mca {

bool EntryStage::hasWorkToComplete() const {
return static_cast<bool>(CurrentInstruction);
return static_cast<bool>(CurrentInstruction) || !SM.isEnd();
}

bool EntryStage::isAvailable(const InstRef & /* unused */) const {
Expand All @@ -28,15 +28,20 @@ bool EntryStage::isAvailable(const InstRef & /* unused */) const {
return false;
}

void EntryStage::getNextInstruction() {
Error EntryStage::getNextInstruction() {
assert(!CurrentInstruction && "There is already an instruction to process!");
if (!SM.hasNext())
return;
if (!SM.hasNext()) {
if (!SM.isEnd())
return llvm::make_error<InstStreamPause>();
else
return llvm::ErrorSuccess();
}
SourceRef SR = SM.peekNext();
std::unique_ptr<Instruction> Inst = std::make_unique<Instruction>(SR.second);
CurrentInstruction = InstRef(SR.first, Inst.get());
Instructions.emplace_back(std::move(Inst));
SM.updateNext();
return llvm::ErrorSuccess();
}

llvm::Error EntryStage::execute(InstRef & /*unused */) {
Expand All @@ -46,16 +51,20 @@ llvm::Error EntryStage::execute(InstRef & /*unused */) {

// Move the program counter.
CurrentInstruction.invalidate();
getNextInstruction();
return llvm::ErrorSuccess();
return getNextInstruction();
}

llvm::Error EntryStage::cycleStart() {
if (!CurrentInstruction)
getNextInstruction();
return getNextInstruction();
return llvm::ErrorSuccess();
}

llvm::Error EntryStage::cycleResume() {
assert(!CurrentInstruction);
return getNextInstruction();
}

llvm::Error EntryStage::cycleEnd() {
// Find the first instruction which hasn't been retired.
auto Range = make_range(&Instructions[NumRetired], Instructions.end());
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/MCA/Stages/Stage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ void Stage::addListener(HWEventListener *Listener) {
Listeners.insert(Listener);
}

char InstStreamPause::ID = 0;
} // namespace mca
} // namespace llvm
6 changes: 3 additions & 3 deletions llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,9 @@ void AMDGPUCustomBehaviour::generateWaitCntInfo() {
AMDGPU::IsaVersion IV = AMDGPU::getIsaVersion(STI.getCPU());
InstrWaitCntInfo.resize(SrcMgr.size());

int Index = 0;
for (auto I = SrcMgr.begin(), E = SrcMgr.end(); I != E; ++I, ++Index) {
const std::unique_ptr<Instruction> &Inst = *I;
for (const auto &EN : llvm::enumerate(SrcMgr.getInstructions())) {
const std::unique_ptr<Instruction> &Inst = EN.value();
unsigned Index = EN.index();
unsigned Opcode = Inst->getOpcode();
const MCInstrDesc &MCID = MCII.get(Opcode);
if ((MCID.TSFlags & SIInstrFlags::DS) &&
Expand Down
3 changes: 2 additions & 1 deletion llvm/tools/llvm-mca/llvm-mca.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,8 @@ int main(int argc, char **argv) {
LoweredSequence.emplace_back(std::move(Inst.get()));
}

mca::SourceMgr S(LoweredSequence, PrintInstructionTables ? 1 : Iterations);
mca::CircularSourceMgr S(LoweredSequence,
PrintInstructionTables ? 1 : Iterations);

if (PrintInstructionTables) {
// Create a pipeline, stages, and a printer.
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ add_subdirectory(
llvm-exegesis
)
add_subdirectory(llvm-profgen)
add_subdirectory(llvm-mca)
52 changes: 52 additions & 0 deletions llvm/unittests/tools/llvm-mca/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
set(LLVM_LINK_COMPONENTS
MC
MCA
Object
Support
)

set(mca_root ${LLVM_MAIN_SRC_DIR}/tools/llvm-mca)

set(mca_includes
${CMAKE_CURRENT_SOURCE_DIR}
${mca_root}
)

# Right now we only need SummaryView.
set(mca_views_sources
SummaryView.cpp
)
list(TRANSFORM mca_views_sources PREPEND "${mca_root}/Views/")

set(mca_sources
MCATestBase.cpp
${mca_views_sources}
)

function(add_llvm_mca_unittest_includes)
set(mca_includes ${mca_includes} ${ARGV} PARENT_SCOPE)
endfunction()

function(add_llvm_mca_unittest_sources)
set(sources ${ARGV})
list(TRANSFORM sources PREPEND "${CMAKE_CURRENT_LIST_DIR}/")
set(mca_sources ${mca_sources} ${sources} PARENT_SCOPE)
endfunction()

function(add_llvm_mca_unittest_link_components comps)
set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} ${ARGV} PARENT_SCOPE)
endfunction()

if(LLVM_TARGETS_TO_BUILD MATCHES "X86")
include(X86/CMakeLists.txt)
endif()

list(REMOVE_DUPLICATES LLVM_LINK_COMPONENTS)

include_directories(${mca_includes})

add_llvm_target_unittest(LLVMMCATests
${mca_sources}
)

set_property(TARGET LLVMMCATests PROPERTY FOLDER "Tests/UnitTests/ToolTests")
123 changes: 123 additions & 0 deletions llvm/unittests/tools/llvm-mca/MCATestBase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "MCATestBase.h"
#include "Views/SummaryView.h"
#include "llvm/MCA/CustomBehaviour.h"
#include "llvm/MCA/InstrBuilder.h"
#include "llvm/MCA/Pipeline.h"
#include "llvm/MCA/SourceMgr.h"
#include "llvm/MCA/View.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/WithColor.h"
#include <string>

using namespace llvm;
using namespace mca;

const Target *MCATestBase::getLLVMTarget() const {
std::string Error;
return TargetRegistry::lookupTarget(TheTriple.getTriple(), Error);
}

mca::PipelineOptions MCATestBase::getDefaultPipelineOptions() {
mca::PipelineOptions PO(/*MicroOpQueue=*/0, /*DecoderThroughput=*/0,
/*DispatchWidth=*/0,
/*RegisterFileSize=*/0,
/*LoadQueueSize=*/0, /*StoreQueueSize=*/0,
/*AssumeNoAlias=*/true,
/*EnableBottleneckAnalysis=*/false);
return PO;
}

void MCATestBase::SetUp() {
TheTarget = getLLVMTarget();
ASSERT_NE(TheTarget, nullptr);

StringRef TripleName = TheTriple.getTriple();

STI.reset(TheTarget->createMCSubtargetInfo(TripleName, CPUName, MAttr));
ASSERT_TRUE(STI);
ASSERT_TRUE(STI->isCPUStringValid(CPUName));

MRI.reset(TheTarget->createMCRegInfo(TripleName));
ASSERT_TRUE(MRI);

auto MCOptions = getMCTargetOptions();
MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
ASSERT_TRUE(MAI);

Ctx = std::make_unique<MCContext>(TheTriple, MAI.get(), MRI.get(), STI.get());
MOFI.reset(TheTarget->createMCObjectFileInfo(*Ctx, /*PIC=*/false));
Ctx->setObjectFileInfo(MOFI.get());

MCII.reset(TheTarget->createMCInstrInfo());
ASSERT_TRUE(MCII);

MCIA.reset(TheTarget->createMCInstrAnalysis(MCII.get()));
ASSERT_TRUE(MCIA);

IP.reset(TheTarget->createMCInstPrinter(TheTriple, /*AssemblerDialect=*/0,
*MAI, *MCII, *MRI));
ASSERT_TRUE(IP);
}

Error MCATestBase::runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
ArrayRef<mca::View *> Views,
const mca::PipelineOptions *PO) {
mca::Context MCA(*MRI, *STI);

mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());

SmallVector<std::unique_ptr<mca::Instruction>> LoweredInsts;
for (const auto &MCI : Insts) {
Expected<std::unique_ptr<mca::Instruction>> Inst =
IB.createInstruction(MCI);
if (!Inst) {
if (auto NewE =
handleErrors(Inst.takeError(),
[this](const mca::InstructionError<MCInst> &IE) {
std::string InstructionStr;
raw_string_ostream SS(InstructionStr);
WithColor::error() << IE.Message << '\n';
IP->printInst(&IE.Inst, 0, "", *STI, SS);
WithColor::note()
<< "instruction: " << InstructionStr << '\n';
})) {
// Default case.
return std::move(NewE);
}
} else {
LoweredInsts.emplace_back(std::move(Inst.get()));
}
}

mca::CircularSourceMgr SM(LoweredInsts, /*Iterations=*/1);

// Empty CustomBehaviour.
auto CB = std::make_unique<mca::CustomBehaviour>(*STI, SM, *MCII);

mca::PipelineOptions ThePO = PO ? *PO : getDefaultPipelineOptions();
auto P = MCA.createDefaultPipeline(ThePO, SM, *CB);

SmallVector<std::unique_ptr<mca::View>, 1> DefaultViews;
if (Views.empty()) {
// By default, we only add SummaryView.
auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), Insts,
ThePO.DispatchWidth);
P->addEventListener(SV.get());
DefaultViews.emplace_back(std::move(SV));
} else {
for (auto *V : Views)
P->addEventListener(V);
}

// Run the pipeline.
Expected<unsigned> Cycles = P->run();
if (!Cycles)
return Cycles.takeError();

for (const auto *V : Views)
Result[V->getNameAsString()] = V->toJSON();
for (const auto &V : DefaultViews)
Result[V->getNameAsString()] = V->toJSON();

return Error::success();
}
83 changes: 83 additions & 0 deletions llvm/unittests/tools/llvm-mca/MCATestBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//===---- MCATestBase.h -----------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
// Test fixture common to all MCA tests.
//===----------------------------------------------------------------------===//

#ifndef LLVM_UNITTESTS_TOOLS_LLVMMCA_MCATESTBASE_H
#define LLVM_UNITTESTS_TOOLS_LLVMMCA_MCATESTBASE_H

#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrAnalysis.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/MCA/Context.h"

#include "gtest/gtest.h"

namespace llvm {
namespace json {
class Object;
} // end namespace json

namespace mca {
class View;

class MCATestBase : public ::testing::Test {
protected:
// Note: Subclass ctors are expected to perform target-specific
// initializations.
MCATestBase(StringRef TripleStr, StringRef CPUName, StringRef MAttr = "")
: TheTriple(TripleStr), CPUName(CPUName), MAttr(MAttr) {}

/// Factory function to create a Target.
virtual const Target *getLLVMTarget() const;

/// Factory function to create a MCTargetOptions instance. Returns an
/// empty one by default.
virtual MCTargetOptions getMCTargetOptions() { return MCTargetOptions(); }

const Target *TheTarget;
const Triple TheTriple;
StringRef CPUName;
StringRef MAttr;

// MC components.
std::unique_ptr<MCSubtargetInfo> STI;
std::unique_ptr<MCRegisterInfo> MRI;
std::unique_ptr<MCAsmInfo> MAI;
std::unique_ptr<MCObjectFileInfo> MOFI;
std::unique_ptr<MCContext> Ctx;
std::unique_ptr<MCInstrInfo> MCII;
std::unique_ptr<MCInstrAnalysis> MCIA;
std::unique_ptr<MCInstPrinter> IP;

static mca::PipelineOptions getDefaultPipelineOptions();

void SetUp() override;

/// Utility function to run MCA with (nearly) the same configuration as the
/// `llvm-mca` tool to verify result correctness.
/// This function only displays on SummaryView by default.
virtual Error runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
ArrayRef<mca::View *> Views = None,
const mca::PipelineOptions *PO = nullptr);
};

} // end namespace mca
} // end namespace llvm
#endif
13 changes: 13 additions & 0 deletions llvm/unittests/tools/llvm-mca/X86/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
add_llvm_mca_unittest_includes(
${LLVM_MAIN_SRC_DIR}/lib/Target/X86
${LLVM_BINARY_DIR}/lib/Target/X86
)

add_llvm_mca_unittest_sources(
TestIncrementalMCA.cpp
X86TestBase.cpp
)

add_llvm_mca_unittest_link_components(
X86
)
181 changes: 181 additions & 0 deletions llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#include "Views/SummaryView.h"
#include "X86TestBase.h"
#include "llvm/ADT/SmallPtrSet.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 <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());

mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());

// 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]);
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());

mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());
IB.setInstRecycleCallback(GetRecycledInst);

// 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]);

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";
}
}
35 changes: 35 additions & 0 deletions llvm/unittests/tools/llvm-mca/X86/X86TestBase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "X86TestBase.h"
#include "MCTargetDesc/X86MCTargetDesc.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/Support/TargetSelect.h"

using namespace llvm;
using namespace mca;

X86TestBase::X86TestBase() : MCATestBase("x86_64-unknown-linux", "skylake") {
LLVMInitializeX86TargetInfo();
LLVMInitializeX86TargetMC();
LLVMInitializeX86Target();
LLVMInitializeX86AsmPrinter();
}

void X86TestBase::getSimpleInsts(SmallVectorImpl<MCInst> &Insts,
unsigned Repeats) {
for (unsigned i = 0U; i < Repeats; ++i) {
// vmulps %xmm0, %xmm1, %xmm2
Insts.push_back(MCInstBuilder(X86::VMULPSrr)
.addReg(X86::XMM2)
.addReg(X86::XMM1)
.addReg(X86::XMM0));
// vhaddps %xmm2, %xmm2, %xmm3
Insts.push_back(MCInstBuilder(X86::VHADDPSrr)
.addReg(X86::XMM3)
.addReg(X86::XMM2)
.addReg(X86::XMM2));
// vhaddps %xmm3, %xmm3, %xmm4
Insts.push_back(MCInstBuilder(X86::VHADDPSrr)
.addReg(X86::XMM4)
.addReg(X86::XMM3)
.addReg(X86::XMM3));
}
}
30 changes: 30 additions & 0 deletions llvm/unittests/tools/llvm-mca/X86/X86TestBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===---- X86TestBase.h -----------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
// Test fixture common to all X86 MCA tests.
//===----------------------------------------------------------------------===//

#ifndef LLVM_UNITTESTS_TOOLS_LLVMMCA_X86_X86TESTBASE_H
#define LLVM_UNITTESTS_TOOLS_LLVMMCA_X86_X86TESTBASE_H

#include "MCATestBase.h"
#include "llvm/ADT/SmallVector.h"

namespace llvm {
namespace mca {

class X86TestBase : public MCATestBase {
protected:
X86TestBase();

void getSimpleInsts(SmallVectorImpl<MCInst> &Insts, unsigned Repeats = 1);
};

} // end namespace mca
} // end namespace llvm

#endif