Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ option(SIMENG_OPTIMIZE "Enable Extra Compiler Optimizatoins" OFF)
option(SIMENG_ENABLE_SST "Compile SimEng SST Wrapper" OFF)
option(SIMENG_ENABLE_SST_TESTS "Enable testing for SST" OFF)

# Set CXX flag for Apple Mac so that `binary_function` and `unary_function` types that are used in SST can be recognised.
# They were deprecated in C++11 and removed in C++17, and Apple Clang v15 no longer supports these types without the following flag
# TODO: Remove once SST integration has updated to SST version 13 or later - the use of unary and binary functions are removed in later versions.
if(APPLE)
add_compile_definitions(_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION)
endif()

if (SIMENG_OPTIMIZE)
# Turn on link time optimization for all targets.
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
Expand Down
4 changes: 4 additions & 0 deletions src/include/simeng/Instruction.hh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class Instruction {
/** Retrieve the source registers this instruction reads. */
virtual const span<Register> getOperandRegisters() const = 0;

/** Retrieve the data contained in the source registers this instruction
* reads.*/
virtual const span<RegisterValue> getSourceOperands() const = 0;

/** Retrieve the destination registers this instruction will write to.
* A register value of -1 signifies a Zero Register read, and should not be
* renamed. */
Expand Down
4 changes: 4 additions & 0 deletions src/include/simeng/arch/aarch64/Instruction.hh
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ class Instruction : public simeng::Instruction {
/** Retrieve the source registers this instruction reads. */
const span<Register> getOperandRegisters() const override;

/** Retrieve the data contained in the source registers this instruction
* reads.*/
const span<RegisterValue> getSourceOperands() const override;

/** Retrieve the destination registers this instruction will write to.
* A register value of -1 signifies a Zero Register read, and should not be
* renamed. */
Expand Down
4 changes: 4 additions & 0 deletions src/include/simeng/arch/riscv/Instruction.hh
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ class Instruction : public simeng::Instruction {
/** Retrieve the source registers this instruction reads. */
const span<Register> getOperandRegisters() const override;

/** Retrieve the data contained in the source registers this instruction
* reads.*/
const span<RegisterValue> getSourceOperands() const override;

/** Retrieve the destination registers this instruction will write to.
* A register value of -1 signifies a Zero Register read, and should not be
* renamed. */
Expand Down
1 change: 1 addition & 0 deletions src/lib/arch/aarch64/Architecture.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Architecture::Architecture(kernel::Linux& kernel, YAML::Node config)
systemRegisterMap_[ARM64_SYSREG_MIDR_EL1] = systemRegisterMap_.size();
systemRegisterMap_[ARM64_SYSREG_CNTVCT_EL0] = systemRegisterMap_.size();
systemRegisterMap_[ARM64_SYSREG_PMCCNTR_EL0] = systemRegisterMap_.size();
systemRegisterMap_[ARM64_SYSREG_SVCR] = systemRegisterMap_.size();

// Get Virtual Counter Timer and Processor Cycle Counter system registers.
VCTreg_ = {
Expand Down
86 changes: 52 additions & 34 deletions src/lib/arch/aarch64/ExceptionHandler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -649,57 +649,75 @@ bool ExceptionHandler::init() {
} else if (exception == InstructionException::StreamingModeUpdate ||
exception == InstructionException::ZAregisterStatusUpdate ||
exception == InstructionException::SMZAUpdate) {
// Get Architecture
const Architecture& arch = instruction_.getArchitecture();
// Retrieve register file structure from architecture
auto regFileStruct =
instruction_.getArchitecture().getRegisterFileStructures();
auto regFileStruct = arch.getRegisterFileStructures();
// Retrieve metadata from architecture
auto metadata = instruction_.getMetadata();

// Update SVCR value
const uint64_t svcrBits = static_cast<uint64_t>(metadata.operands[0].svcr);
const uint8_t imm = metadata.operands[1].imm;
const uint64_t currSVCR = instruction_.getArchitecture().getSVCRval();
uint64_t newSVCR = 0;

if (imm == 0) {
// Zero out relevant bits dictated by svcrBits
const uint64_t mask = 0xFFFFFFFFFFFFFFFF ^ svcrBits;
newSVCR = currSVCR & mask;
} else if (imm == 1) {
// Enable relevant bits, dictated by svcrBits
const uint64_t mask = 0xFFFFFFFFFFFFFFFF & svcrBits;
newSVCR = currSVCR | mask;
const uint64_t currSVCR = arch.getSVCRval();

// Check if exception was called by AArch64_MSR (msr systemreg, xt) or
// AArch64_MSRpstatesvcrImm1 (msr svcr<sm|za|smza>, #imm)
if (metadata.opcode == Opcode::AArch64_MSR) {
newSVCR = instruction_.getSourceOperands()[0].get<uint64_t>();
} else if (metadata.opcode == Opcode::AArch64_MSRpstatesvcrImm1) {
const uint64_t svcrBits =
static_cast<uint64_t>(metadata.operands[0].svcr);
const uint64_t imm = metadata.operands[1].imm;
assert((imm == 0 || imm == 1) &&
"[SimEng:ExceptionHandler] SVCR Instruction invalid - Imm value "
"can only be 0 or 1");
// Zero out SM & ZA bits as appropriate
newSVCR = currSVCR & ~(svcrBits);
// Update only relevant bits of SVCR
newSVCR = newSVCR | (svcrBits * imm);
} else {
// Invalid instruction
assert("SVCR Instruction invalid - Imm value can only be 0 or 1");
std::cerr << "[SimEng::ExceptionHandler] SVCR system register exception "
"triggered by incorrect instruction. Opcode "
<< metadata.opcode << std::endl;
exit(1);
}
instruction_.getArchitecture().setSVCRval(newSVCR);
arch.setSVCRval(newSVCR);

// Initialise vectors for all registers & values
std::vector<Register> regs;
std::vector<RegisterValue> regValues;

// Add Vector/Predicate registers + 0 values (zeroed out on Streaming Mode
// context switch)
if (exception != InstructionException::ZAregisterStatusUpdate) {
for (uint16_t i = 0; i < regFileStruct[RegisterType::VECTOR].quantity;
i++) {
regs.push_back({RegisterType::VECTOR, i});
regValues.push_back(RegisterValue(0, 256));
if (i < regFileStruct[RegisterType::PREDICATE].quantity) {
regs.push_back({RegisterType::PREDICATE, i});
regValues.push_back(RegisterValue(0, 32));
// If SVCR.ZA has changed state then zero out ZA register, else don't
if (exception != InstructionException::StreamingModeUpdate) {
if ((newSVCR & ARM64_SVCR_SVCRZA) != (currSVCR & ARM64_SVCR_SVCRZA)) {
for (uint16_t i = 0; i < regFileStruct[RegisterType::MATRIX].quantity;
i++) {
regs.push_back({RegisterType::MATRIX, i});
regValues.push_back(RegisterValue(0, 256));
}
}
}
// Zero out ZA register (zeroed out on ZA-reg context switch)
if (exception != InstructionException::StreamingModeUpdate) {
for (uint16_t i = 0; i < regFileStruct[RegisterType::MATRIX].quantity;
i++) {
regs.push_back({RegisterType::MATRIX, i});
regValues.push_back(RegisterValue(0, 256));
// If SVCR.SM has changed state then zero out SVE, NEON, Predicate
// registers, else don't
if (exception != InstructionException::ZAregisterStatusUpdate) {
if ((newSVCR & ARM64_SVCR_SVCRSM) != (currSVCR & ARM64_SVCR_SVCRSM)) {
for (uint16_t i = 0; i < regFileStruct[RegisterType::VECTOR].quantity;
i++) {
regs.push_back({RegisterType::VECTOR, i});
regValues.push_back(RegisterValue(0, 256));
if (i < regFileStruct[RegisterType::PREDICATE].quantity) {
regs.push_back({RegisterType::PREDICATE, i});
regValues.push_back(RegisterValue(0, 32));
}
}
}
}

// Update SVCR system register in regFile
regs.push_back(
{RegisterType::SYSTEM,
static_cast<uint16_t>(arch.getSystemRegisterTag(ARM64_SYSREG_SVCR))});
regValues.push_back(RegisterValue(newSVCR, 8));

ProcessStateChange stateChange = {ChangeType::REPLACEMENT, regs, regValues};
return concludeSyscall(stateChange);
}
Expand Down
5 changes: 5 additions & 0 deletions src/lib/arch/aarch64/Instruction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ InstructionException Instruction::getException() const { return exception_; }
const span<Register> Instruction::getOperandRegisters() const {
return {const_cast<Register*>(sourceRegisters.data()), sourceRegisterCount};
}

const span<RegisterValue> Instruction::getSourceOperands() const {
return {const_cast<RegisterValue*>(operands.data()), operands.size()};
}

Comment thread
dANW34V3R marked this conversation as resolved.
const span<Register> Instruction::getDestinationRegisters() const {
return {const_cast<Register*>(destinationRegisters.data()),
destinationRegisterCount};
Expand Down
12 changes: 12 additions & 0 deletions src/lib/arch/aarch64/InstructionMetadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1406,6 +1406,12 @@ InstructionMetadata::InstructionMetadata(const cs_insn& insn)
operands[3].access = CS_AC_READ;
operands[4].access = CS_AC_READ | CS_AC_WRITE;
operands[5].access = CS_AC_READ;
// determine correct type for operand 5
if (operandStr.find("#") != std::string::npos) {
operands[5].type = ARM64_OP_IMM;
} else {
operands[5].type = ARM64_OP_REG;
}
break;
case Opcode::AArch64_ST1Twov16b:
[[fallthrough]];
Expand All @@ -1426,6 +1432,12 @@ InstructionMetadata::InstructionMetadata(const cs_insn& insn)
operands[1].access = CS_AC_READ;
operands[2].access = CS_AC_READ | CS_AC_WRITE;
operands[3].access = CS_AC_READ;
// determine correct type for operand 3
if (operandStr.find("#") != std::string::npos) {
operands[3].type = ARM64_OP_IMM;
} else {
operands[3].type = ARM64_OP_REG;
}
break;
case Opcode::AArch64_ST2Twov4s_POST:
// ST2 post incorrectly flags read and write
Expand Down
9 changes: 8 additions & 1 deletion src/lib/arch/aarch64/Instruction_execute.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3812,7 +3812,14 @@ void Instruction::execute() {
break;
}
case Opcode::AArch64_MSR: { // msr (systemreg|Sop0_op1_Cn_Cm_op2), xt
results[0] = operands[0];
// Handle case where SVCR is being updated as this invokes additional
// functionality
if (metadata.operands[0].reg ==
static_cast<arm64_reg>(ARM64_SYSREG_SVCR)) {
return SMZAupdated();
} else {
results[0] = operands[0];
}
break;
}
case Opcode::AArch64_MSUBWrrr: { // msub wd, wn, wm, wa
Expand Down
5 changes: 5 additions & 0 deletions src/lib/arch/riscv/Instruction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ InstructionException Instruction::getException() const { return exception_; }
const span<Register> Instruction::getOperandRegisters() const {
return {const_cast<Register*>(sourceRegisters.data()), sourceRegisterCount};
}

const span<RegisterValue> Instruction::getSourceOperands() const {
return {const_cast<RegisterValue*>(operands.data()), operands.size()};
}

const span<Register> Instruction::getDestinationRegisters() const {
return {const_cast<Register*>(destinationRegisters.data()),
destinationRegisterCount};
Expand Down
131 changes: 131 additions & 0 deletions test/regression/aarch64/Exception.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,49 @@ TEST_P(Exception, SME_context_modes) {
EXPECT_EQ(stdout_.substr(0, sizeof(err2) - 1), err2);
}

// Ensure that calling smstart/smstop such that the values in SVCR.SMZA do not
// change doesn't cause a flush of the associated register files
TEST_P(Exception, Null_Smstart_smstop_calls) {
RUN_AARCH64(R"(
smstart
dup z0.d, #3
smstart
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({3}, SVL / 8));

RUN_AARCH64(R"(
smstart
dup z0.d, #4
smstart sm
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({4}, SVL / 8));

RUN_AARCH64(R"(
smstart
dup z0.d, #5
smstart za
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({5}, SVL / 8));

RUN_AARCH64(R"(
dup z0.d, #6
smstop
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({6}, VL / 8));

RUN_AARCH64(R"(
dup z0.d, #7
smstop sm
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({7}, VL / 8));

RUN_AARCH64(R"(
dup z0.d, #8
smstop za
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({8}, VL / 8));
}

TEST_P(Exception, svcr) {
// Check that smstart and smstop correctly change value of SVCR system
// register, verified by the correctly performed behaviour
Expand All @@ -76,6 +119,13 @@ TEST_P(Exception, svcr) {
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({0}, VL / 8));

RUN_AARCH64(R"(
# Ensure z regs get enabled when SM enabled
smstart sm
dup z0.d, #3
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({3}, SVL / 8));

RUN_AARCH64(R"(
# Ensure z regs get zeroed out when SM disabled
smstart
Expand All @@ -84,6 +134,14 @@ TEST_P(Exception, svcr) {
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({0}, VL / 8));

RUN_AARCH64(R"(
# Ensure z regs do not get zeroed out when ZA is disabled
smstart
dup z0.d, #3
smstop za
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({3}, SVL / 8));

RUN_AARCH64(R"(
# Ensure za reg gets zeroed out when ZA enabled
smstart
Expand All @@ -98,6 +156,79 @@ TEST_P(Exception, svcr) {
for (int i = 0; i < (SVL / 8); i++) {
CHECK_MAT_ROW(ARM64_REG_ZA, i, uint32_t, fillNeon<uint32_t>({0}, SVL / 8));
}

// Check that changes to SVCR using msr svcr, xn work correctly
RUN_AARCH64(R"(
mov x4, #3
mov x5, #0
# Ensure vector length changes from SVE's to SME's
cntb x0
msr svcr, x4
cntb x1
msr svcr, x5
cntb x2
)");
EXPECT_EQ(getGeneralRegister<uint64_t>(0), VL / 8);
EXPECT_EQ(getGeneralRegister<uint64_t>(1), SVL / 8);
EXPECT_EQ(getGeneralRegister<uint64_t>(2), VL / 8);
EXPECT_EQ(getGeneralRegister<uint64_t>(0), getGeneralRegister<uint64_t>(2));
EXPECT_GT(getGeneralRegister<uint64_t>(1), getGeneralRegister<uint64_t>(0));
EXPECT_GT(SVL, VL);

RUN_AARCH64(R"(
mov x4, #3
# Ensure z regs get zeroed out when SM enabled
dup z0.d, #3
msr svcr, x4
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({0}, VL / 8));

RUN_AARCH64(R"(
mov x4, #1
# Ensure z regs get enabled when SM enabled
msr svcr, x4
dup z0.d, #3
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({3}, SVL / 8));

RUN_AARCH64(R"(
mov x4, #3
mov x5, #0
# Ensure z regs get zeroed out when SM disabled
msr svcr, x4
dup z0.d, #3
msr svcr, x5
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({0}, VL / 8));

RUN_AARCH64(R"(
# enable SM and ZA
mov x4, #3
# just disable ZA
mov x5, #1
# Ensure z regs do not get zeroed out when ZA is disabled
msr svcr, x4
dup z0.d, #3
msr svcr, x5
)");
CHECK_NEON(0, uint64_t, fillNeon<uint64_t>({3}, SVL / 8));

RUN_AARCH64(R"(
mov x4, #3
mov x5, #0
# Ensure za reg gets zeroed out when ZA enabled
msr svcr, x4
dup z0.s, #2
dup z1.s, #3
ptrue p0.s
ptrue p1.s
fmopa za0.s, p0/m, p1/m, z0.s, z1.s
msr svcr, x5
msr svcr, x4
)");
for (int i = 0; i < (SVL / 8); i++) {
CHECK_MAT_ROW(ARM64_REG_ZA, i, uint32_t, fillNeon<uint32_t>({0}, SVL / 8));
}
}
#endif

Expand Down
1 change: 1 addition & 0 deletions test/unit/MockInstruction.hh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class MockInstruction : public Instruction {
public:
MOCK_CONST_METHOD0(getException, InstructionException());
MOCK_CONST_METHOD0(getOperandRegisters, const span<Register>());
MOCK_CONST_METHOD0(getSourceOperands, const span<RegisterValue>());
MOCK_CONST_METHOD0(getDestinationRegisters, const span<Register>());
MOCK_METHOD2(renameSource, void(uint16_t i, Register renamed));
MOCK_METHOD2(renameDestination, void(uint16_t i, Register renamed));
Expand Down