|
@@ -40,7 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
namespace randomx {
|
|
namespace randomx {
|
|
|
|
|
|
|
|
- static bool isMultiplication(int type) {
|
|
|
|
|
|
|
+ static bool isMultiplication(SuperscalarInstructionType type) {
|
|
|
return type == SuperscalarInstructionType::IMUL_R || type == SuperscalarInstructionType::IMULH_R || type == SuperscalarInstructionType::ISMULH_R || type == SuperscalarInstructionType::IMUL_RCP;
|
|
return type == SuperscalarInstructionType::IMUL_R || type == SuperscalarInstructionType::IMULH_R || type == SuperscalarInstructionType::ISMULH_R || type == SuperscalarInstructionType::IMUL_RCP;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -167,7 +167,7 @@ namespace randomx {
|
|
|
const MacroOp& getOp(int index) const {
|
|
const MacroOp& getOp(int index) const {
|
|
|
return ops_[index];
|
|
return ops_[index];
|
|
|
}
|
|
}
|
|
|
- int getType() const {
|
|
|
|
|
|
|
+ SuperscalarInstructionType getType() const {
|
|
|
return type_;
|
|
return type_;
|
|
|
}
|
|
}
|
|
|
int getResultOp() const {
|
|
int getResultOp() const {
|
|
@@ -196,7 +196,7 @@ namespace randomx {
|
|
|
static const SuperscalarInstructionInfo NOP;
|
|
static const SuperscalarInstructionInfo NOP;
|
|
|
private:
|
|
private:
|
|
|
const char* name_;
|
|
const char* name_;
|
|
|
- int type_;
|
|
|
|
|
|
|
+ SuperscalarInstructionType type_;
|
|
|
std::vector<MacroOp> ops_;
|
|
std::vector<MacroOp> ops_;
|
|
|
int latency_;
|
|
int latency_;
|
|
|
int resultOp_ = 0;
|
|
int resultOp_ = 0;
|
|
@@ -204,13 +204,13 @@ namespace randomx {
|
|
|
int srcOp_;
|
|
int srcOp_;
|
|
|
|
|
|
|
|
SuperscalarInstructionInfo(const char* name)
|
|
SuperscalarInstructionInfo(const char* name)
|
|
|
- : name_(name), type_(-1), latency_(0) {}
|
|
|
|
|
- SuperscalarInstructionInfo(const char* name, int type, const MacroOp& op, int srcOp)
|
|
|
|
|
|
|
+ : name_(name), type_(SuperscalarInstructionType::INVALID), latency_(0) {}
|
|
|
|
|
+ SuperscalarInstructionInfo(const char* name, SuperscalarInstructionType type, const MacroOp& op, int srcOp)
|
|
|
: name_(name), type_(type), latency_(op.getLatency()), srcOp_(srcOp) {
|
|
: name_(name), type_(type), latency_(op.getLatency()), srcOp_(srcOp) {
|
|
|
ops_.push_back(MacroOp(op));
|
|
ops_.push_back(MacroOp(op));
|
|
|
}
|
|
}
|
|
|
template <size_t N>
|
|
template <size_t N>
|
|
|
- SuperscalarInstructionInfo(const char* name, int type, const MacroOp(&arr)[N], int resultOp, int dstOp, int srcOp)
|
|
|
|
|
|
|
+ SuperscalarInstructionInfo(const char* name, SuperscalarInstructionType type, const MacroOp(&arr)[N], int resultOp, int dstOp, int srcOp)
|
|
|
: name_(name), type_(type), latency_(0), resultOp_(resultOp), dstOp_(dstOp), srcOp_(srcOp) {
|
|
: name_(name), type_(type), latency_(0), resultOp_(resultOp), dstOp_(dstOp), srcOp_(srcOp) {
|
|
|
for (unsigned i = 0; i < N; ++i) {
|
|
for (unsigned i = 0; i < N; ++i) {
|
|
|
ops_.push_back(MacroOp(arr[i]));
|
|
ops_.push_back(MacroOp(arr[i]));
|
|
@@ -267,7 +267,7 @@ namespace randomx {
|
|
|
const char* getName() const {
|
|
const char* getName() const {
|
|
|
return name_;
|
|
return name_;
|
|
|
}
|
|
}
|
|
|
- const DecoderBuffer* fetchNext(int instrType, int cycle, int mulCount, Blake2Generator& gen) const {
|
|
|
|
|
|
|
+ const DecoderBuffer* fetchNext(SuperscalarInstructionType instrType, int cycle, int mulCount, Blake2Generator& gen) const {
|
|
|
//If the current RandomX instruction is "IMULH", the next fetch configuration must be 3-3-10
|
|
//If the current RandomX instruction is "IMULH", the next fetch configuration must be 3-3-10
|
|
|
//because the full 128-bit multiplication instruction is 3 bytes long and decodes to 2 uOPs on Intel CPUs.
|
|
//because the full 128-bit multiplication instruction is 3 bytes long and decodes to 2 uOPs on Intel CPUs.
|
|
|
//Intel CPUs can decode at most 4 uOPs per cycle, so this requires a 2-1-1 configuration for a total of 3 macro ops.
|
|
//Intel CPUs can decode at most 4 uOPs per cycle, so this requires a 2-1-1 configuration for a total of 3 macro ops.
|
|
@@ -345,9 +345,9 @@ namespace randomx {
|
|
|
|
|
|
|
|
class RegisterInfo {
|
|
class RegisterInfo {
|
|
|
public:
|
|
public:
|
|
|
- RegisterInfo() : latency(0), lastOpGroup(-1), lastOpPar(-1), value(0) {}
|
|
|
|
|
|
|
+ RegisterInfo() : latency(0), lastOpGroup(SuperscalarInstructionType::INVALID), lastOpPar(-1), value(0) {}
|
|
|
int latency;
|
|
int latency;
|
|
|
- int lastOpGroup;
|
|
|
|
|
|
|
+ SuperscalarInstructionType lastOpGroup;
|
|
|
int lastOpPar;
|
|
int lastOpPar;
|
|
|
int value;
|
|
int value;
|
|
|
};
|
|
};
|
|
@@ -356,7 +356,7 @@ namespace randomx {
|
|
|
class SuperscalarInstruction {
|
|
class SuperscalarInstruction {
|
|
|
public:
|
|
public:
|
|
|
void toInstr(Instruction& instr) { //translate to a RandomX instruction format
|
|
void toInstr(Instruction& instr) { //translate to a RandomX instruction format
|
|
|
- instr.opcode = getType();
|
|
|
|
|
|
|
+ instr.opcode = (int)getType();
|
|
|
instr.dst = dst_;
|
|
instr.dst = dst_;
|
|
|
instr.src = src_ >= 0 ? src_ : dst_;
|
|
instr.src = src_ >= 0 ? src_ : dst_;
|
|
|
instr.setMod(mod_);
|
|
instr.setMod(mod_);
|
|
@@ -534,7 +534,7 @@ namespace randomx {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- int getType() {
|
|
|
|
|
|
|
+ SuperscalarInstructionType getType() {
|
|
|
return info_->getType();
|
|
return info_->getType();
|
|
|
}
|
|
}
|
|
|
int getSource() {
|
|
int getSource() {
|
|
@@ -543,7 +543,7 @@ namespace randomx {
|
|
|
int getDestination() {
|
|
int getDestination() {
|
|
|
return dst_;
|
|
return dst_;
|
|
|
}
|
|
}
|
|
|
- int getGroup() {
|
|
|
|
|
|
|
+ SuperscalarInstructionType getGroup() {
|
|
|
return opGroup_;
|
|
return opGroup_;
|
|
|
}
|
|
}
|
|
|
int getGroupPar() {
|
|
int getGroupPar() {
|
|
@@ -562,7 +562,7 @@ namespace randomx {
|
|
|
int dst_ = -1;
|
|
int dst_ = -1;
|
|
|
int mod_;
|
|
int mod_;
|
|
|
uint32_t imm32_;
|
|
uint32_t imm32_;
|
|
|
- int opGroup_;
|
|
|
|
|
|
|
+ SuperscalarInstructionType opGroup_;
|
|
|
int opGroupPar_;
|
|
int opGroupPar_;
|
|
|
bool canReuse_ = false;
|
|
bool canReuse_ = false;
|
|
|
bool groupParIsSource_ = false;
|
|
bool groupParIsSource_ = false;
|
|
@@ -673,8 +673,8 @@ namespace randomx {
|
|
|
//Each decode cycle decodes 16 bytes of x86 code.
|
|
//Each decode cycle decodes 16 bytes of x86 code.
|
|
|
//Since a decode cycle produces on average 3.45 macro-ops and there are only 3 ALU ports, execution ports are always
|
|
//Since a decode cycle produces on average 3.45 macro-ops and there are only 3 ALU ports, execution ports are always
|
|
|
//saturated first. The cycle limit is present only to guarantee loop termination.
|
|
//saturated first. The cycle limit is present only to guarantee loop termination.
|
|
|
- //Program size is limited to RANDOMX_SUPERSCALAR_MAX_SIZE instructions.
|
|
|
|
|
- for (decodeCycle = 0; decodeCycle < RANDOMX_SUPERSCALAR_LATENCY && !portsSaturated && programSize < RANDOMX_SUPERSCALAR_MAX_SIZE; ++decodeCycle) {
|
|
|
|
|
|
|
+ //Program size is limited to SuperscalarMaxSize instructions.
|
|
|
|
|
+ for (decodeCycle = 0; decodeCycle < RANDOMX_SUPERSCALAR_LATENCY && !portsSaturated && programSize < SuperscalarMaxSize; ++decodeCycle) {
|
|
|
|
|
|
|
|
//select a decode configuration
|
|
//select a decode configuration
|
|
|
decodeBuffer = decodeBuffer->fetchNext(currentInstruction.getType(), decodeCycle, mulCount, gen);
|
|
decodeBuffer = decodeBuffer->fetchNext(currentInstruction.getType(), decodeCycle, mulCount, gen);
|
|
@@ -688,7 +688,7 @@ namespace randomx {
|
|
|
|
|
|
|
|
//if we have issued all macro-ops for the current RandomX instruction, create a new instruction
|
|
//if we have issued all macro-ops for the current RandomX instruction, create a new instruction
|
|
|
if (macroOpIndex >= currentInstruction.getInfo().getSize()) {
|
|
if (macroOpIndex >= currentInstruction.getInfo().getSize()) {
|
|
|
- if (portsSaturated || programSize >= RANDOMX_SUPERSCALAR_MAX_SIZE)
|
|
|
|
|
|
|
+ if (portsSaturated || programSize >= SuperscalarMaxSize)
|
|
|
break;
|
|
break;
|
|
|
//select an instruction so that the first macro-op fits into the current slot
|
|
//select an instruction so that the first macro-op fits into the current slot
|
|
|
currentInstruction.createForSlot(gen, decodeBuffer->getCounts()[bufferIndex], decodeBuffer->getIndex(), decodeBuffer->getSize() == bufferIndex + 1, bufferIndex == 0);
|
|
currentInstruction.createForSlot(gen, decodeBuffer->getCounts()[bufferIndex], decodeBuffer->getIndex(), decodeBuffer->getSize() == bufferIndex + 1, bufferIndex == 0);
|
|
@@ -849,40 +849,40 @@ namespace randomx {
|
|
|
void executeSuperscalar(int_reg_t(&r)[8], SuperscalarProgram& prog, std::vector<uint64_t> *reciprocals) {
|
|
void executeSuperscalar(int_reg_t(&r)[8], SuperscalarProgram& prog, std::vector<uint64_t> *reciprocals) {
|
|
|
for (unsigned j = 0; j < prog.getSize(); ++j) {
|
|
for (unsigned j = 0; j < prog.getSize(); ++j) {
|
|
|
Instruction& instr = prog(j);
|
|
Instruction& instr = prog(j);
|
|
|
- switch (instr.opcode)
|
|
|
|
|
|
|
+ switch ((SuperscalarInstructionType)instr.opcode)
|
|
|
{
|
|
{
|
|
|
- case randomx::SuperscalarInstructionType::ISUB_R:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::ISUB_R:
|
|
|
r[instr.dst] -= r[instr.src];
|
|
r[instr.dst] -= r[instr.src];
|
|
|
break;
|
|
break;
|
|
|
- case randomx::SuperscalarInstructionType::IXOR_R:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::IXOR_R:
|
|
|
r[instr.dst] ^= r[instr.src];
|
|
r[instr.dst] ^= r[instr.src];
|
|
|
break;
|
|
break;
|
|
|
- case randomx::SuperscalarInstructionType::IADD_RS:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::IADD_RS:
|
|
|
r[instr.dst] += r[instr.src] << instr.getModShift();
|
|
r[instr.dst] += r[instr.src] << instr.getModShift();
|
|
|
break;
|
|
break;
|
|
|
- case randomx::SuperscalarInstructionType::IMUL_R:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::IMUL_R:
|
|
|
r[instr.dst] *= r[instr.src];
|
|
r[instr.dst] *= r[instr.src];
|
|
|
break;
|
|
break;
|
|
|
- case randomx::SuperscalarInstructionType::IROR_C:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::IROR_C:
|
|
|
r[instr.dst] = rotr(r[instr.dst], instr.getImm32());
|
|
r[instr.dst] = rotr(r[instr.dst], instr.getImm32());
|
|
|
break;
|
|
break;
|
|
|
- case randomx::SuperscalarInstructionType::IADD_C7:
|
|
|
|
|
- case randomx::SuperscalarInstructionType::IADD_C8:
|
|
|
|
|
- case randomx::SuperscalarInstructionType::IADD_C9:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::IADD_C7:
|
|
|
|
|
+ case SuperscalarInstructionType::IADD_C8:
|
|
|
|
|
+ case SuperscalarInstructionType::IADD_C9:
|
|
|
r[instr.dst] += signExtend2sCompl(instr.getImm32());
|
|
r[instr.dst] += signExtend2sCompl(instr.getImm32());
|
|
|
break;
|
|
break;
|
|
|
- case randomx::SuperscalarInstructionType::IXOR_C7:
|
|
|
|
|
- case randomx::SuperscalarInstructionType::IXOR_C8:
|
|
|
|
|
- case randomx::SuperscalarInstructionType::IXOR_C9:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::IXOR_C7:
|
|
|
|
|
+ case SuperscalarInstructionType::IXOR_C8:
|
|
|
|
|
+ case SuperscalarInstructionType::IXOR_C9:
|
|
|
r[instr.dst] ^= signExtend2sCompl(instr.getImm32());
|
|
r[instr.dst] ^= signExtend2sCompl(instr.getImm32());
|
|
|
break;
|
|
break;
|
|
|
- case randomx::SuperscalarInstructionType::IMULH_R:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::IMULH_R:
|
|
|
r[instr.dst] = mulh(r[instr.dst], r[instr.src]);
|
|
r[instr.dst] = mulh(r[instr.dst], r[instr.src]);
|
|
|
break;
|
|
break;
|
|
|
- case randomx::SuperscalarInstructionType::ISMULH_R:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::ISMULH_R:
|
|
|
r[instr.dst] = smulh(r[instr.dst], r[instr.src]);
|
|
r[instr.dst] = smulh(r[instr.dst], r[instr.src]);
|
|
|
break;
|
|
break;
|
|
|
- case randomx::SuperscalarInstructionType::IMUL_RCP:
|
|
|
|
|
|
|
+ case SuperscalarInstructionType::IMUL_RCP:
|
|
|
if (reciprocals != nullptr)
|
|
if (reciprocals != nullptr)
|
|
|
r[instr.dst] *= (*reciprocals)[instr.getImm32()];
|
|
r[instr.dst] *= (*reciprocals)[instr.getImm32()];
|
|
|
else
|
|
else
|