Skip to content

Commit

Permalink
Group Tests (#419)
Browse files Browse the repository at this point in the history
Add infrastructure for testing the groups assigned to each micro-op of a macro-op, along with tests for all RISC-V instructions and most AArch64 aliases. Expected micro-op groups should be provided in the order which micro-ops are held in the MacroOp structure.
  • Loading branch information
dANW34V3R authored Aug 29, 2024
1 parent 05fdd87 commit 4134c3e
Show file tree
Hide file tree
Showing 27 changed files with 867 additions and 125 deletions.
19 changes: 18 additions & 1 deletion src/lib/arch/aarch64/InstructionMetadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1798,6 +1798,7 @@ void InstructionMetadata::revertAliasing() {
case ARM64_INS_AT:
return aliasNYI();
case ARM64_INS_BFI:
// TODO no tests of alias
if (opcode == Opcode::AArch64_BFMWri) {
// bfi wd, wn, #lsb, #width; alias for
// bfm wd, wn, #(-lsb MOD 32), #(width - 1)
Expand All @@ -1814,6 +1815,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_BFXIL:
// TODO no tests for alias
if (opcode == Opcode::AArch64_BFMWri ||
opcode == Opcode::AArch64_BFMXri) {
// bfxil rd, rn, #lsb, #width; alias for
Expand Down Expand Up @@ -1896,6 +1898,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_CSET:
// TODO no usage in regression tests
if (opcode == Opcode::AArch64_CSINCWr ||
opcode == Opcode::AArch64_CSINCXr) {
// cset rd, cc; alias for: csinc rd, zr, zr, invert(cc)
Expand Down Expand Up @@ -1952,6 +1955,7 @@ void InstructionMetadata::revertAliasing() {
case ARM64_INS_IC:
return aliasNYI();
case ARM64_INS_LSL:
// TODO no usage in regression tests
if (opcode == Opcode::AArch64_UBFMWri ||
opcode == Opcode::AArch64_UBFMXri) {
// lsl rd, rn, #shift; alias for:
Expand Down Expand Up @@ -1998,6 +2002,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_MNEG:
// TODO no test
if (opcode == Opcode::AArch64_MSUBXrrr) {
// mneg xd, xn, xm; alias for msub xd, xn, xm, xzr
operandCount = 4;
Expand All @@ -2016,6 +2021,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_MOV:
// TODO no specific tests
if (opcode == Opcode::AArch64_AND_PPzPP) {
// mov pd.b, pg/z, pn.b; alias for: and pd.b, pg/z, pn.b, pn.b
operandCount = 4;
Expand Down Expand Up @@ -2272,6 +2278,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_MUL:
// TODO add comment
if (opcode == Opcode::AArch64_MADDXrrr ||
opcode == Opcode::AArch64_MADDWrrr) {
operandCount = 4;
Expand Down Expand Up @@ -2310,13 +2317,15 @@ void InstructionMetadata::revertAliasing() {
}
if (opcode == Opcode::AArch64_NOTv16i8 ||
opcode == Opcode::AArch64_NOTv8i8) {
// TODO needs tests
// mvn vd.t, vn.t; alias for : not vd.t, vn.t
// Blank entry was for a legitimate alias, however operands were
// identical so nothing to alter between the instructions.
return;
}
return aliasNYI();
case ARM64_INS_NEG:
// TODO needs tests
if (opcode == Opcode::AArch64_SUBWrs ||
opcode == Opcode::AArch64_SUBXrs) {
// neg rd, rm{, shift #amount}; alias for:
Expand Down Expand Up @@ -2364,7 +2373,7 @@ void InstructionMetadata::revertAliasing() {
return aliasNYI();
case ARM64_INS_NOT:
if (opcode == Opcode::AArch64_EOR_PPzPP) {
// not pd.b, pg/z, pn.b; alisas for: eor pd.b, pg/z, pn.b, pg.b
// not pd.b, pg/z, pn.b; alias for: eor pd.b, pg/z, pn.b, pg.b
operandCount = 4;
operands[0].access = CS_AC_WRITE;
operands[1].access = CS_AC_READ;
Expand All @@ -2388,6 +2397,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_ROR:
// TODO needs test
if (opcode == Opcode::AArch64_RORVWr ||
opcode == Opcode::AArch64_RORVXr) {
// ror wd, wn, wm; alias for : rorv wd, wn, wm
Expand All @@ -2398,6 +2408,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_SBFIZ:
// TODO needs test
if (opcode == Opcode::AArch64_SBFMWri ||
opcode == Opcode::AArch64_SBFMXri) {
operands[3].imm -= 1;
Expand All @@ -2412,6 +2423,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_SBFX:
// TODO needs test
if (opcode == Opcode::AArch64_SBFMWri ||
opcode == Opcode::AArch64_SBFMXri) {
// sbfx rd, rn, #lsb, #width; alias for
Expand Down Expand Up @@ -2482,6 +2494,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_SYS: {
// TODO no test
// Extract IC/DC/AT/TLBI operation
if (std::string(mnemonic) == "dc") {
if (operandStr.substr(0, 3) == "zva") {
Expand All @@ -2502,6 +2515,7 @@ void InstructionMetadata::revertAliasing() {
case ARM64_INS_TLBI:
return aliasNYI();
case ARM64_INS_TST:
// TODO needs test for register case
if (opcode == Opcode::AArch64_ANDSWrs ||
opcode == Opcode::AArch64_ANDSXrs ||
opcode == Opcode::AArch64_ANDSWri ||
Expand All @@ -2525,6 +2539,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_UBFIZ:
// TODO needs test and comment
if (opcode == Opcode::AArch64_UBFMWri ||
opcode == Opcode::AArch64_UBFMXri) {
operands[3].imm -= 1;
Expand All @@ -2539,6 +2554,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_UBFX:
// TODO needs test
if (opcode == Opcode::AArch64_UBFMWri ||
opcode == Opcode::AArch64_UBFMXri) {
// ubfx rd, rn, #lsb, #width; alias for
Expand All @@ -2560,6 +2576,7 @@ void InstructionMetadata::revertAliasing() {
}
return aliasNYI();
case ARM64_INS_UXTB:
// TODO needs test
// uxtb wd, wn; alias for: ubfm wd, wn, #0, #7
if (opcode == Opcode::AArch64_UBFMWri) {
operandCount = 4;
Expand Down
123 changes: 77 additions & 46 deletions test/regression/RegressionTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ void RegressionTest::TearDown() {
}
}

void RegressionTest::run(const char* source, const char* triple,
const char* extensions) {
testing::internal::CaptureStdout();

void RegressionTest::createArchitecture(const char* source, const char* triple,
const char* extensions) {
// Zero-out process memory from any prior runs
if (processMemory_ != nullptr)
std::memset(processMemory_, '\0', processMemorySize_);
Expand All @@ -47,56 +45,44 @@ void RegressionTest::run(const char* source, const char* triple,
// The process image is finalised by the createStack method
// which creates and populates the initial process stack.
// The created process image can be accessed via a shared_ptr
// returned by the getProcessImage method.
// returned by the getProcessImage method
process_ = std::make_unique<simeng::kernel::LinuxProcess>(
simeng::span(reinterpret_cast<const uint8_t*>(code_), codeSize_));

ASSERT_TRUE(process_->isValid());
uint64_t entryPoint = process_->getEntryPoint();
entryPoint_ = process_->getEntryPoint();
processMemorySize_ = process_->getProcessImageSize();

// This instance of procImgPtr pointer needs to be shared because
// getMemoryValue in RegressionTest.hh uses reference to the class
// member processMemory_.
// member processMemory_
std::shared_ptr<char> procImgPtr = process_->getProcessImage();
processMemory_ = procImgPtr.get();

// Create memory interfaces for instruction and data access.
// For each memory interface, a dereferenced shared_ptr to the
// processImage is passed as argument.
simeng::memory::FlatMemoryInterface instructionMemory(processMemory_,
processMemorySize_);

std::unique_ptr<simeng::memory::FlatMemoryInterface> flatDataMemory =
std::make_unique<simeng::memory::FlatMemoryInterface>(processMemory_,
processMemorySize_);
// Populate the heap with initial data (specified by the test being run)
ASSERT_LT(process_->getHeapStart() + initialHeapData_.size(),
process_->getInitialStackPointer());
std::copy(initialHeapData_.begin(), initialHeapData_.end(),
processMemory_ + process_->getHeapStart());

std::unique_ptr<simeng::memory::FixedLatencyMemoryInterface>
fixedLatencyDataMemory =
std::make_unique<simeng::memory::FixedLatencyMemoryInterface>(
processMemory_, processMemorySize_, 4);
std::unique_ptr<simeng::memory::MemoryInterface> dataMemory;
ASSERT_TRUE(process_ != nullptr);

// Create the OS kernel and the process
simeng::kernel::Linux kernel(
kernel_ = std::make_unique<simeng::kernel::Linux>(
simeng::config::SimInfo::getConfig()["CPU-Info"]["Special-File-Dir-Path"]
.as<std::string>());
kernel.createProcess(*process_);

// Populate the heap with initial data (specified by the test being run).
ASSERT_LT(process_->getHeapStart() + initialHeapData_.size(),
process_->getInitialStackPointer());
std::copy(initialHeapData_.begin(), initialHeapData_.end(),
processMemory_ + process_->getHeapStart());
kernel_->createProcess(*process_);

// Create the architecture
architecture_ = createArchitecture(kernel);
architecture_ = instantiateArchitecture(*kernel_);
}

// Create a port allocator for an out-of-order core
std::unique_ptr<simeng::pipeline::PortAllocator> portAllocator =
createPortAllocator();
void RegressionTest::createCore(const char* source, const char* triple,
const char* extensions) {
// Create the architecture, kernel and process
createArchitecture(source, triple, extensions);

// Create a branch predictor for a pipelined core
std::unique_ptr<simeng::BranchPredictor> predictor_ = nullptr;
std::string predictorType =
simeng::config::SimInfo::getConfig()["Branch-Predictor"]["Type"]
.as<std::string>();
Expand All @@ -106,34 +92,61 @@ void RegressionTest::run(const char* source, const char* triple,
predictor_ = std::make_unique<simeng::PerceptronPredictor>();
}

// Create memory interfaces for instruction and data access.
// For each memory interface, a dereferenced shared_ptr to the
// processImage is passed as an argument

ASSERT_TRUE(processMemory_ != nullptr);

instructionMemory_ = std::make_unique<simeng::memory::FlatMemoryInterface>(
processMemory_, processMemorySize_);

flatDataMemory_ = std::make_unique<simeng::memory::FlatMemoryInterface>(
processMemory_, processMemorySize_);

fixedLatencyDataMemory_ =
std::make_unique<simeng::memory::FixedLatencyMemoryInterface>(
processMemory_, processMemorySize_, 4);

// Create the core model
switch (std::get<0>(GetParam())) {
case EMULATION:
core_ = std::make_unique<simeng::models::emulation::Core>(
instructionMemory, *flatDataMemory, entryPoint, processMemorySize_,
*architecture_);
dataMemory = std::move(flatDataMemory);
*instructionMemory_, *flatDataMemory_, entryPoint_,
processMemorySize_, *architecture_);
dataMemory_ = std::move(flatDataMemory_);
break;
case INORDER:
core_ = std::make_unique<simeng::models::inorder::Core>(
instructionMemory, *flatDataMemory, processMemorySize_, entryPoint,
*architecture_, *predictor_);
dataMemory = std::move(flatDataMemory);
*instructionMemory_, *flatDataMemory_, processMemorySize_,
entryPoint_, *architecture_, *predictor_);
dataMemory_ = std::move(flatDataMemory_);
break;
case OUTOFORDER:
// Create a port allocator for an out-of-order core
portAllocator_ = createPortAllocator();

core_ = std::make_unique<simeng::models::outoforder::Core>(
instructionMemory, *fixedLatencyDataMemory, processMemorySize_,
entryPoint, *architecture_, *predictor_, *portAllocator);
dataMemory = std::move(fixedLatencyDataMemory);
*instructionMemory_, *fixedLatencyDataMemory_, processMemorySize_,
entryPoint_, *architecture_, *predictor_, *portAllocator_);
dataMemory_ = std::move(fixedLatencyDataMemory_);
break;
}
}

void RegressionTest::run(const char* source, const char* triple,
const char* extensions) {
testing::internal::CaptureStdout();

// Create the core, memory interfaces, kernel and process
createCore(source, triple, extensions);

// Run the core model until the program is complete
while (!core_->hasHalted() || dataMemory->hasPendingRequests()) {
while (!core_->hasHalted() || dataMemory_->hasPendingRequests()) {
ASSERT_LT(numTicks_, maxTicks_) << "Maximum tick count exceeded.";
core_->tick();
instructionMemory.tick();
dataMemory->tick();
instructionMemory_->tick();
dataMemory_->tick();
numTicks_++;
}

Expand All @@ -143,6 +156,24 @@ void RegressionTest::run(const char* source, const char* triple,
programFinished_ = true;
}

void RegressionTest::checkGroup(const char* source, const char* triple,
const char* extensions,
const std::vector<uint16_t>& expectedGroups) {
createArchitecture(source, triple, extensions);

std::vector<std::shared_ptr<simeng::Instruction>> macroOp;
architecture_->predecode(code_, 4, 0, macroOp);

// Check that there is one expectation group per micro-op
EXPECT_EQ(macroOp.size(), expectedGroups.size());

// Check the assigned and expected group for each micro-op match
for (size_t i = 0; i < macroOp.size(); i++) {
auto group = macroOp[i]->getGroup();
EXPECT_EQ(group, expectedGroups[i]);
}
}

void RegressionTest::assemble(const char* source, const char* triple,
const char* extensions) {
// Get LLVM target
Expand Down
Loading

0 comments on commit 4134c3e

Please sign in to comment.