Assembly code generator for Windows 64-bit

This commit is contained in:
tevador 2018-12-13 23:11:55 +01:00
parent c9102ee88c
commit cb0721056a
16 changed files with 8082 additions and 83 deletions

View File

@ -11,7 +11,7 @@ SRCDIR=src
OBJDIR=obj
LDFLAGS=
TOBJS=$(addprefix $(OBJDIR)/,instructionsPortable.o TestAluFpu.o)
ROBJS=$(addprefix $(OBJDIR)/,argon2_core.o argon2_ref.o blake2b.o dataset.o instructionsPortable.o InterpretedVirtualMachine.o main.o Program.o softAes.o VirtualMachine.o)
ROBJS=$(addprefix $(OBJDIR)/,argon2_core.o argon2_ref.o AssemblyGeneratorX86.o blake2b.o CompiledVirtualMachine.o dataset.o executeProgram-linux.o instructionsPortable.o Instruction.o InterpretedVirtualMachine.o main.o Program.o softAes.o VirtualMachine.o)
SRC1=$(addprefix $(SRCDIR)/,TestAluFpu.cpp instructions.hpp Pcg32.hpp)
all: release test
@ -43,14 +43,26 @@ $(OBJDIR)/argon2_core.o: $(addprefix $(SRCDIR)/,argon2_core.c argon2_core.h blak
$(OBJDIR)/argon2_ref.o: $(addprefix $(SRCDIR)/,argon2_ref.c argon2.h argon2_core.h blake2/blake2.h blake2/blake2-impl.h blake2/blamka-round-ref.h) | $(OBJDIR)
$(CC) $(CCFLAGS) -c $(SRCDIR)/argon2_ref.c -o $@
$(OBJDIR)/AssemblyGeneratorX86.o: $(addprefix $(SRCDIR)/,AssemblyGeneratorX86.cpp AssemblyGeneratorX86.hpp Instruction.hpp Pcg32.hpp common.hpp instructions.hpp) | $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $(SRCDIR)/AssemblyGeneratorX86.cpp -o $@
$(OBJDIR)/blake2b.o: $(addprefix $(SRCDIR)/blake2/,blake2b.c blake2.h blake2-impl.h) | $(OBJDIR)
$(CC) $(CCFLAGS) -c $(SRCDIR)/blake2/blake2b.c -o $@
$(OBJDIR)/CompiledVirtualMachine.o: $(addprefix $(SRCDIR)/,CompiledVirtualMachine.cpp CompiledVirtualMachine.hpp Pcg32.hpp common.hpp instructions.hpp) | $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $(SRCDIR)/CompiledVirtualMachine.cpp -o $@
$(OBJDIR)/dataset.o: $(addprefix $(SRCDIR)/,dataset.cpp common.hpp Pcg32.hpp argon2_core.h) | $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $(SRCDIR)/dataset.cpp -o $@
$(OBJDIR)/executeProgram-linux.o: $(addprefix $(SRCDIR)/,executeProgram-linux.cpp common.hpp) | $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $(SRCDIR)/executeProgram-linux.cpp -o $@
$(OBJDIR)/instructionsPortable.o: $(addprefix $(SRCDIR)/,instructionsPortable.cpp instructions.hpp intrinPortable.h) | $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $(SRCDIR)/instructionsPortable.cpp -o $@
$(OBJDIR)/Instruction.o: $(addprefix $(SRCDIR)/,Instruction.cpp Instruction.hpp) | $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $(SRCDIR)/Instruction.cpp -o $@
$(OBJDIR)/InterpretedVirtualMachine.o: $(addprefix $(SRCDIR)/,InterpretedVirtualMachine.cpp InterpretedVirtualMachine.hpp Pcg32.hpp instructions.hpp) | $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $(SRCDIR)/InterpretedVirtualMachine.cpp -o $@

View File

@ -0,0 +1,523 @@
/*
Copyright (c) 2018 tevador
This file is part of RandomX.
RandomX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
RandomX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/
#include "AssemblyGeneratorX86.hpp"
#include "Pcg32.hpp"
#include "common.hpp"
#include "instructions.hpp"
namespace RandomX {
static const char* regR[8] = { "rbx", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
static const char* regR32[8] = { "ebx", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" };
static const char* regF[8] = { "xmm8", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" };
void AssemblyGeneratorX86::generateProgram(const void* seed) {
asmCode.str(std::string()); //clear
Pcg32 gen(seed);
for (unsigned i = 0; i < sizeof(RegisterFile) / sizeof(Pcg32::result_type); ++i) {
gen();
}
Instruction instr;
for (unsigned i = 0; i < ProgramLength; ++i) {
for (unsigned j = 0; j < sizeof(instr) / sizeof(Pcg32::result_type); ++j) {
*(((uint32_t*)&instr) + j) = gen();
}
generateCode(instr, i);
asmCode << std::endl;
}
if(ProgramLength > 0)
asmCode << "\tjmp rx_i_0" << std::endl;
}
void AssemblyGeneratorX86::generateCode(Instruction& instr, int i) {
asmCode << "rx_i_" << i << ": ;" << instr.getName() << std::endl;
asmCode << "\tdec edi" << std::endl;
asmCode << "\tjs rx_finish" << std::endl;
auto generator = engine[instr.opcode];
(this->*generator)(instr, i);
}
void AssemblyGeneratorX86::gena(Instruction& instr) {
asmCode << "\txor " << regR[instr.rega % RegistersCount] << ", 0" << std::hex << instr.addr0 << "h" << std::dec << std::endl;
switch (instr.loca & 7)
{
case 0:
case 1:
case 2:
case 3:
asmCode << "\tmov ecx, " << regR32[instr.rega % RegistersCount] << std::endl;
asmCode << "\tcall rx_read_dataset" << std::endl;
return;
case 4:
asmCode << "\tmov eax, " << regR32[instr.rega % RegistersCount] << std::endl;
asmCode << "\tand eax, " << (ScratchpadL2 - 1) << std::endl;
asmCode << "\tmov rax, qword ptr [rsi + rax * 8]" << std::endl;
return;
default:
asmCode << "\tmov eax, " << regR32[instr.rega % RegistersCount] << std::endl;
asmCode << "\tand eax, " << (ScratchpadL1 - 1) << std::endl;
asmCode << "\tmov rax, qword ptr [rsi + rax * 8]" << std::endl;
return;
}
}
void AssemblyGeneratorX86::genbr0(Instruction& instr, const char* instrx86) {
switch (instr.locb & 7)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
asmCode << "\tmov rcx, " << regR[instr.regb % RegistersCount] << std::endl;
asmCode << "\t" << instrx86 << " rax, cl" << std::endl;
return;
default:
asmCode << "\t" << instrx86 << " rax, " << (instr.imm0 & 63) << std::endl;;
return;
}
}
void AssemblyGeneratorX86::genbr1(Instruction& instr) {
switch (instr.locb & 7)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
asmCode << regR[instr.regb % RegistersCount] << std::endl;
return;
default:
asmCode << instr.imm1 << std::endl;;
return;
}
}
void AssemblyGeneratorX86::genbr132(Instruction& instr) {
switch (instr.locb & 7)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
asmCode << regR32[instr.regb % RegistersCount] << std::endl;
return;
default:
asmCode << instr.imm1 << std::endl;;
return;
}
}
void AssemblyGeneratorX86::genbf(Instruction& instr, const char* instrx86) {
asmCode << "\tand rax, -2048" << std::endl;
asmCode << "\tcvtsi2sd xmm0, rax" << std::endl;
switch (instr.locb & 7)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
asmCode << "\t" << instrx86 << " xmm0, " << regF[instr.regb % RegistersCount] << std::endl;
return;
default:
convertible_t bimm;
bimm.f64 = (double)instr.imm1;
asmCode << "\tmov rax, " << bimm.i64 << std::endl;
asmCode << "\tmovd xmm9, rax" << std::endl;
asmCode << "\t" << instrx86 << " xmm0, xmm9" << std::endl;
return;
}
}
void AssemblyGeneratorX86::gencr(Instruction& instr) {
switch (instr.locc & 7)
{
case 0:
asmCode << "\tmov rcx, rax" << std::endl;
asmCode << "\tmov eax, " << regR32[instr.regc % RegistersCount] << std::endl;
asmCode << "\txor eax, 0" << std::hex << instr.addr1 << "h" << std::dec << std::endl;
asmCode << "\tand eax, " << (ScratchpadL2 - 1) << std::endl;
asmCode << "\tmov qword ptr [rsi + rax * 8], rcx" << std::endl;
return;
case 1:
case 2:
case 3:
asmCode << "\tmov rcx, rax" << std::endl;
asmCode << "\tmov eax, " << regR32[instr.regc % RegistersCount] << std::endl;
asmCode << "\txor eax, 0" << std::hex << instr.addr1 << "h" << std::dec << std::endl;
asmCode << "\tand eax, " << (ScratchpadL1 - 1) << std::endl;
asmCode << "\tmov qword ptr [rsi + rax * 8], rcx" << std::endl;
return;
default:
asmCode << "\tmov " << regR[instr.regc % RegistersCount] << ", rax" << std::endl;
}
}
void AssemblyGeneratorX86::gencf(Instruction& instr) {
switch (instr.locc & 7)
{
case 0:
asmCode << "\tmov eax, " << regR32[instr.regc % RegistersCount] << std::endl;
asmCode << "\txor eax, 0" << std::hex << instr.addr1 << "h" << std::dec << std::endl;
asmCode << "\tand eax, " << (ScratchpadL2 - 1) << std::endl;
asmCode << "\tmovd qword ptr [rsi + rax * 8], xmm0" << std::endl;
return;
case 1:
case 2:
case 3:
asmCode << "\tmov eax, " << regR32[instr.regc % RegistersCount] << std::endl;
asmCode << "\txor eax, 0" << std::hex << instr.addr1 << "h" << std::dec << std::endl;
asmCode << "\tand eax, " << (ScratchpadL1 - 1) << std::endl;
asmCode << "\tmovd qword ptr [rsi + rax * 8], xmm0" << std::endl;
return;
default:
asmCode << "\tmovsd " << regF[instr.regc % RegistersCount] << ", xmm0" << std::endl;
}
}
static inline int wrapi(int i) {
return i % RandomX::ProgramLength;
}
void AssemblyGeneratorX86::h_ADD_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\tadd rax, ";
genbr1(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_ADD_32(Instruction& instr, int i) {
gena(instr);
asmCode << "\tadd eax, ";
genbr132(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_SUB_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\tsub rax, ";
genbr1(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_SUB_32(Instruction& instr, int i) {
gena(instr);
asmCode << "\tsub eax, ";
genbr132(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_MUL_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\timul rax, ";
if ((instr.locb & 7) >= 6) {
asmCode << "rax, ";
}
genbr1(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_MULH_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\tmov r8, rdx" << std::endl;
asmCode << "\tmov rcx, ";
genbr1(instr);
asmCode << "\tmul rcx" << std::endl;
asmCode << "\tmov rax, rdx" << std::endl;
asmCode << "\tmov rdx, r8" << std::endl;
gencr(instr);
}
void AssemblyGeneratorX86::h_MUL_32(Instruction& instr, int i) {
gena(instr);
asmCode << "\tmov ecx, eax" << std::endl;
asmCode << "\tmov eax, ";
genbr132(instr);
asmCode << "\timul rax, rcx" << std::endl;
gencr(instr);
}
void AssemblyGeneratorX86::h_IMUL_32(Instruction& instr, int i) {
gena(instr);
asmCode << "\tmovsxd rcx, eax" << std::endl;
if ((instr.locb & 7) >= 6) {
asmCode << "\tmov rax, " << instr.imm1 << std::endl;
}
else {
asmCode << "\tmovsxd rax, " << regR32[instr.regb % RegistersCount] << std::endl;
}
asmCode << "\timul rax, rcx" << std::endl;
gencr(instr);
}
void AssemblyGeneratorX86::h_IMULH_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\tmov r8, rdx" << std::endl;
asmCode << "\tmov rcx, ";
genbr1(instr);
asmCode << "\timul rcx" << std::endl;
asmCode << "\tmov rax, rdx" << std::endl;
asmCode << "\tmov rdx, r8" << std::endl;
gencr(instr);
}
void AssemblyGeneratorX86::h_DIV_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\tmov r8, rdx" << std::endl;
if ((instr.locb & 7) >= 6) {
if (instr.imm1 == 0) {
asmCode << "\tmov ecx, 1" << std::endl;
}
else {
asmCode << "\tmov ecx, " << instr.imm1 << std::endl;
}
}
else {
asmCode << "mov ecx, 1" << std::endl;
asmCode << "\tmov edx, " << regR32[instr.regb % RegistersCount] << std::endl;
asmCode << "\ttest edx, edx" << std::endl;
asmCode << "\tcmovne ecx, edx" << std::endl;
}
asmCode << "\txor edx, edx" << std::endl;
asmCode << "\tdiv rcx" << std::endl;
asmCode << "\tmov rdx, r8" << std::endl;
gencr(instr);
}
void AssemblyGeneratorX86::h_IDIV_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\tmov r8, rdx" << std::endl;
asmCode << "\tmov edx, ";
genbr132(instr);
asmCode << "\tcmp edx, -1" << std::endl;
asmCode << "\tjne short safe_idiv_" << i << std::endl;
asmCode << "\tmov rcx, rax" << std::endl;
asmCode << "\trol rcx, 1" << std::endl;
asmCode << "\tdec rcx" << std::endl;
asmCode << "\tjz short result_idiv_" << i << std::endl;
asmCode << "safe_idiv_" << i << ":" << std::endl;
asmCode << "\tmov ecx, 1" << std::endl;
asmCode << "\ttest edx, edx" << std::endl;
asmCode << "\tcmovne ecx, edx" << std::endl;
asmCode << "\tmovsxd rcx, ecx" << std::endl;
asmCode << "\tcqo" << std::endl;
asmCode << "\tidiv rcx" << std::endl;
asmCode << "result_idiv_" << i << ":" << std::endl;
asmCode << "\tmov rdx, r8" << std::endl;
gencr(instr);
}
void AssemblyGeneratorX86::h_AND_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\tand rax, ";
genbr1(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_AND_32(Instruction& instr, int i) {
gena(instr);
asmCode << "\tand eax, ";
genbr132(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_OR_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\tor rax, ";
genbr1(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_OR_32(Instruction& instr, int i) {
gena(instr);
asmCode << "\tor eax, ";
genbr132(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_XOR_64(Instruction& instr, int i) {
gena(instr);
asmCode << "\txor rax, ";
genbr1(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_XOR_32(Instruction& instr, int i) {
gena(instr);
asmCode << "\txor eax, ";
genbr132(instr);
gencr(instr);
}
void AssemblyGeneratorX86::h_SHL_64(Instruction& instr, int i) {
gena(instr);
genbr0(instr, "shl");
gencr(instr);
}
void AssemblyGeneratorX86::h_SHR_64(Instruction& instr, int i) {
gena(instr);
genbr0(instr, "shr");
gencr(instr);
}
void AssemblyGeneratorX86::h_SAR_64(Instruction& instr, int i) {
gena(instr);
genbr0(instr, "sar");
gencr(instr);
}
void AssemblyGeneratorX86::h_ROL_64(Instruction& instr, int i) {
gena(instr);
genbr0(instr, "rol");
gencr(instr);
}
void AssemblyGeneratorX86::h_ROR_64(Instruction& instr, int i) {
gena(instr);
genbr0(instr, "ror");
gencr(instr);
}
void AssemblyGeneratorX86::h_FPADD(Instruction& instr, int i) {
gena(instr);
genbf(instr, "addsd");
gencf(instr);
}
void AssemblyGeneratorX86::h_FPSUB(Instruction& instr, int i) {
gena(instr);
genbf(instr, "subsd");
gencf(instr);
}
void AssemblyGeneratorX86::h_FPMUL(Instruction& instr, int i) {
gena(instr);
asmCode << "\tor rax, 2048" << std::endl;
genbf(instr, "mulsd");
gencf(instr);
}
void AssemblyGeneratorX86::h_FPDIV(Instruction& instr, int i) {
gena(instr);
asmCode << "\tor rax, 2048" << std::endl;
genbf(instr, "divsd");
gencf(instr);
}
void AssemblyGeneratorX86::h_FPSQRT(Instruction& instr, int i) {
gena(instr);
asmCode << "\tmov rcx, 9223372036854773760" << std::endl;
asmCode << "\tand rax, rcx" << std::endl;
asmCode << "\tcvtsi2sd xmm0, rax" << std::endl;
asmCode << "\tsqrtsd xmm0, xmm0" << std::endl;
gencf(instr);
}
void AssemblyGeneratorX86::h_FPROUND(Instruction& instr, int i) {
gena(instr);
asmCode << "\tmov rcx, rax" << std::endl;
asmCode << "\tshl eax, 13" << std::endl;
asmCode << "\tand rcx, -2048" << std::endl;
asmCode << "\tand eax, 24576" << std::endl;
asmCode << "\tcvtsi2sd xmm0, rcx" << std::endl;
asmCode << "\tor eax, 40896" << std::endl;
asmCode << "\tmov dword ptr [rsp - 8], eax" << std::endl;
asmCode << "\tldmxcsr dword ptr [rsp - 8]" << std::endl;
gencf(instr);
}
void AssemblyGeneratorX86::h_CALL(Instruction& instr, int i) {
gena(instr);
if ((instr.locb & 7) < 6) {
asmCode << "\tcmp " << regR32[instr.regb % RegistersCount] << ", " << instr.imm1 << std::endl;
asmCode << "\tjbe short taken_call_" << i << std::endl;
gencr(instr);
asmCode << "\tjmp rx_i_" << wrapi(i + 1) << std::endl;
asmCode << "taken_call_" << i << ":" << std::endl;
}
asmCode << "\tpush rax" << std::endl;
asmCode << "\tcall rx_i_" << wrapi(i + (instr.imm0 & 127) + 1) << std::endl;
}
void AssemblyGeneratorX86::h_RET(Instruction& instr, int i) {
gena(instr);
asmCode << "\tcmp rsp, rbp" << std::endl;
asmCode << "\tje short not_taken_ret_" << i << std::endl;
if ((instr.locb & 7) < 6) {
asmCode << "\tcmp " << regR32[instr.regb % RegistersCount] << ", " << instr.imm1 << std::endl;
asmCode << "\tja short not_taken_ret_" << i << std::endl;
}
asmCode << "\txor rax, qword ptr [rsp + 8]" << std::endl;
gencr(instr);
asmCode << "\tret 8" << std::endl;
asmCode << "not_taken_ret_" << i << ":" << std::endl;
gencr(instr);
}
#include "instructionWeights.hpp"
#define INST_HANDLE(x) REPN(&AssemblyGeneratorX86::h_##x, WT(x))
InstructionGenerator AssemblyGeneratorX86::engine[256] = {
INST_HANDLE(ADD_64)
INST_HANDLE(ADD_32)
INST_HANDLE(SUB_64)
INST_HANDLE(SUB_32)
INST_HANDLE(MUL_64)
INST_HANDLE(MULH_64)
INST_HANDLE(MUL_32)
INST_HANDLE(IMUL_32)
INST_HANDLE(IMULH_64)
INST_HANDLE(DIV_64)
INST_HANDLE(IDIV_64)
INST_HANDLE(AND_64)
INST_HANDLE(AND_32)
INST_HANDLE(OR_64)
INST_HANDLE(OR_32)
INST_HANDLE(XOR_64)
INST_HANDLE(XOR_32)
INST_HANDLE(SHL_64)
INST_HANDLE(SHR_64)
INST_HANDLE(SAR_64)
INST_HANDLE(ROL_64)
INST_HANDLE(ROR_64)
INST_HANDLE(FPADD)
INST_HANDLE(FPSUB)
INST_HANDLE(FPMUL)
INST_HANDLE(FPDIV)
INST_HANDLE(FPSQRT)
INST_HANDLE(FPROUND)
INST_HANDLE(CALL)
INST_HANDLE(RET)
};
}

View File

@ -0,0 +1,82 @@
/*
Copyright (c) 2018 tevador
This file is part of RandomX.
RandomX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
RandomX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Instruction.hpp"
#include <sstream>
namespace RandomX {
class AssemblyGeneratorX86;
typedef void(AssemblyGeneratorX86::*InstructionGenerator)(Instruction&, int);
class AssemblyGeneratorX86 {
public:
void generateProgram(const void* seed);
void printCode(std::ostream& os) {
os << asmCode.rdbuf();
}
private:
static InstructionGenerator engine[256];
std::stringstream asmCode;
void gena(Instruction&);
void genbr0(Instruction&, const char*);
void genbr1(Instruction&);
void genbr132(Instruction&);
void genbf(Instruction&, const char*);
void gencr(Instruction&);
void gencf(Instruction&);
void generateCode(Instruction&, int);
void h_ADD_64(Instruction&, int);
void h_ADD_32(Instruction&, int);
void h_SUB_64(Instruction&, int);
void h_SUB_32(Instruction&, int);
void h_MUL_64(Instruction&, int);
void h_MULH_64(Instruction&, int);
void h_MUL_32(Instruction&, int);
void h_IMUL_32(Instruction&, int);
void h_IMULH_64(Instruction&, int);
void h_DIV_64(Instruction&, int);
void h_IDIV_64(Instruction&, int);
void h_AND_64(Instruction&, int);
void h_AND_32(Instruction&, int);
void h_OR_64(Instruction&, int);
void h_OR_32(Instruction&, int);
void h_XOR_64(Instruction&, int);
void h_XOR_32(Instruction&, int);
void h_SHL_64(Instruction&, int);
void h_SHR_64(Instruction&, int);
void h_SAR_64(Instruction&, int);
void h_ROL_64(Instruction&, int);
void h_ROR_64(Instruction&, int);
void h_FPADD(Instruction&, int);
void h_FPSUB(Instruction&, int);
void h_FPMUL(Instruction&, int);
void h_FPDIV(Instruction&, int);
void h_FPSQRT(Instruction&, int);
void h_FPROUND(Instruction&, int);
void h_CALL(Instruction&, int);
void h_RET(Instruction&, int);
};
}

View File

@ -0,0 +1,47 @@
/*
Copyright (c) 2018 tevador
This file is part of RandomX.
RandomX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
RandomX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/
#include "CompiledVirtualMachine.hpp"
#include "Pcg32.hpp"
#include "common.hpp"
#include "instructions.hpp"
namespace RandomX {
void CompiledVirtualMachine::initializeProgram(const void* seed) {
Pcg32 gen(seed);
for (unsigned i = 0; i < sizeof(reg) / sizeof(Pcg32::result_type); ++i) {
*(((uint32_t*)&reg) + i) = gen();
}
FPINIT();
for (int i = 0; i < 8; ++i) {
reg.f[i].f64 = (double)reg.f[i].i64;
}
for (unsigned i = 0; i < ProgramLength; ++i) {
gen(); gen(); gen(); gen();
}
mem.ma = (gen() ^ *(((uint32_t*)seed) + 4)) & ~7;
mem.mx = *(((uint32_t*)seed) + 5);
}
void CompiledVirtualMachine::execute() {
FPINIT();
executeProgram(reg, mem, readDataset, scratchpad);
}
}

View File

@ -0,0 +1,34 @@
/*
Copyright (c) 2018 tevador
This file is part of RandomX.
RandomX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
RandomX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/
#pragma once
#include "VirtualMachine.hpp"
#include "Program.hpp"
#include <sstream>
namespace RandomX {
class CompiledVirtualMachine : public VirtualMachine {
public:
CompiledVirtualMachine(bool softAes) : VirtualMachine(softAes) {}
virtual void initializeProgram(const void* seed) override;
virtual void execute() override;
};
}

70
src/Instruction.cpp Normal file
View File

@ -0,0 +1,70 @@
/*
Copyright (c) 2018 tevador
This file is part of RandomX.
RandomX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
RandomX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/
#include "Instruction.hpp"
namespace RandomX {
void Instruction::print(std::ostream& os) const {
os << " A: loc = " << std::dec << (loca & 7) << ", reg: " << (rega & 7) << std::endl;
os << " B: loc = " << (locb & 7) << ", reg: " << (regb & 7) << std::endl;
os << " C: loc = " << (locc & 7) << ", reg: " << (regc & 7) << std::endl;
os << " addr0 = " << std::hex << addr0 << std::endl;
os << " addr1 = " << addr1 << std::endl;
os << " imm0 = " << std::dec << (int)imm0 << std::endl;
os << " imm1 = " << imm1 << std::endl;
}
#include "instructionWeights.hpp"
#define INST_NAME(x) REPN(#x, WT(x))
const char* Instruction::names[256] = {
INST_NAME(ADD_64)
INST_NAME(ADD_32)
INST_NAME(SUB_64)
INST_NAME(SUB_32)
INST_NAME(MUL_64)
INST_NAME(MULH_64)
INST_NAME(MUL_32)
INST_NAME(IMUL_32)
INST_NAME(IMULH_64)
INST_NAME(DIV_64)
INST_NAME(IDIV_64)
INST_NAME(AND_64)
INST_NAME(AND_32)
INST_NAME(OR_64)
INST_NAME(OR_32)
INST_NAME(XOR_64)
INST_NAME(XOR_32)
INST_NAME(SHL_64)
INST_NAME(SHR_64)
INST_NAME(SAR_64)
INST_NAME(ROL_64)
INST_NAME(ROR_64)
INST_NAME(FPADD)
INST_NAME(FPSUB)
INST_NAME(FPMUL)
INST_NAME(FPDIV)
INST_NAME(FPSQRT)
INST_NAME(FPROUND)
INST_NAME(CALL)
INST_NAME(RET)
};
}

56
src/Instruction.hpp Normal file
View File

@ -0,0 +1,56 @@
/*
Copyright (c) 2018 tevador
This file is part of RandomX.
RandomX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
RandomX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <iostream>
namespace RandomX {
class Instruction {
public:
uint8_t opcode;
uint8_t loca;
uint8_t rega;
uint8_t locb;
uint8_t regb;
uint8_t locc;
uint8_t regc;
uint8_t imm0;
uint32_t addr0;
union {
uint32_t addr1;
int32_t imm1;
};
const char* getName() const {
return names[opcode];
}
friend std::ostream& operator<<(std::ostream& os, const Instruction& i) {
i.print(os);
return os;
}
private:
void print(std::ostream&) const;
static const char* names[256];
};
static_assert(sizeof(Instruction) == 16, "Invalid alignment of struct Instruction");
}

View File

@ -55,7 +55,7 @@ namespace RandomX {
void InterpretedVirtualMachine::execute() {
while (ic > 0) {
auto& inst = p(pc);
if(trace) std::cout << p.getName(inst) << " (" << std::dec << pc << ")" << std::endl;
if(trace) std::cout << inst.getName() << " (" << std::dec << pc << ")" << std::endl;
pc = (pc + 1) % ProgramLength;
auto handler = engine[inst.opcode];
(this->*handler)(inst);

View File

@ -30,48 +30,8 @@ namespace RandomX {
void Program::print(std::ostream& os) const {
for (int i = 0; i < RandomX::ProgramLength; ++i) {
auto instr = programBuffer[i];
os << std::dec << instrNames[instr.opcode] << " (" << i << "):" << std::endl;
os << " A: loc = " << (instr.loca & 7) << ", reg: " << (instr.rega & 7) << std::endl;
os << " B: loc = " << (instr.locb & 7) << ", reg: " << (instr.regb & 7) << std::endl;
os << " C: loc = " << (instr.locc & 7) << ", reg: " << (instr.regc & 7) << std::endl;
os << " imm0 = " << (int)instr.imm0 << std::endl;
os << " imm1 = " << std::hex << instr.imm1 << std::endl;
os << std::dec << instr.getName() << " (" << i << "):" << std::endl;
os << instr;
}
}
#include "instructionWeights.hpp"
#define INST_NAME(x) REPN(#x, WT(x))
const char* Program::instrNames[256] = {
INST_NAME(ADD_64)
INST_NAME(ADD_32)
INST_NAME(SUB_64)
INST_NAME(SUB_32)
INST_NAME(MUL_64)
INST_NAME(MULH_64)
INST_NAME(MUL_32)
INST_NAME(IMUL_32)
INST_NAME(IMULH_64)
INST_NAME(DIV_64)
INST_NAME(IDIV_64)
INST_NAME(AND_64)
INST_NAME(AND_32)
INST_NAME(OR_64)
INST_NAME(OR_32)
INST_NAME(XOR_64)
INST_NAME(XOR_32)
INST_NAME(SHL_64)
INST_NAME(SHR_64)
INST_NAME(SAR_64)
INST_NAME(ROL_64)
INST_NAME(ROR_64)
INST_NAME(FPADD)
INST_NAME(FPSUB)
INST_NAME(FPMUL)
INST_NAME(FPDIV)
INST_NAME(FPSQRT)
INST_NAME(FPROUND)
INST_NAME(CALL)
INST_NAME(RET)
};
}

View File

@ -22,37 +22,17 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <cstdint>
#include <ostream>
#include "common.hpp"
#include "Instruction.hpp"
class Pcg32;
namespace RandomX {
struct Instruction {
uint8_t opcode;
uint8_t loca;
uint8_t rega;
uint8_t locb;
uint8_t regb;
uint8_t locc;
uint8_t regc;
uint8_t imm0;
uint32_t addr0;
union {
uint32_t addr1;
int32_t imm1;
};
};
static_assert(sizeof(Instruction) == 16, "Invalid alignment of struct Instruction");
class Program {
public:
Instruction& operator()(uint64_t pc) {
return programBuffer[pc];
}
const char* getName(Instruction& instr) {
return instrNames[instr.opcode];
}
void initialize(Pcg32& gen);
friend std::ostream& operator<<(std::ostream& os, const Program& p) {
p.print(os);
@ -60,7 +40,6 @@ namespace RandomX {
}
private:
void print(std::ostream&) const;
static const char* instrNames[256];
Instruction programBuffer[ProgramLength];
};
}

View File

@ -23,8 +23,6 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
namespace RandomX {
typedef convertible_t(*DatasetReadFunc)(addr_t, MemoryRegisters&);
class VirtualMachine {
public:
VirtualMachine(bool softAes);

View File

@ -104,7 +104,9 @@ namespace RandomX {
static_assert(sizeof(RegisterFile) == 2 * RegistersCount * sizeof(convertible_t), "Invalid alignment of struct RandomX::RegisterFile");
typedef convertible_t(*DatasetReadFunc)(addr_t, MemoryRegisters&);
extern "C" {
void executeProgram(RegisterFile& registerFile, convertible_t& scratchpad, MemoryRegisters& memory);
void executeProgram(RegisterFile& registerFile, MemoryRegisters& memory, DatasetReadFunc readFunc, convertible_t* scratchpad);
}
}

View File

@ -0,0 +1,10 @@
#include "common.hpp"
#include <stdexcept>
namespace RandomX {
extern "C" {
void executeProgram(RegisterFile& registerFile, MemoryRegisters& memory, DatasetReadFunc readFunc, convertible_t* scratchpad) {
throw std::runtime_error("not implemented");
}
}
}

View File

@ -0,0 +1,169 @@
; Copyright (c) 2018 tevador
;
; This file is part of RandomX.
;
; RandomX is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; RandomX is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with RandomX. If not, see<http://www.gnu.org/licenses/>.
PUBLIC executeProgram
.code
executeProgram PROC
; REGISTER ALLOCATION:
; rax -> temporary
; rbx -> "r0"
; rcx -> temporary
; rdx -> MemoryRegisters& memory
; rsi -> convertible_t& scratchpad
; rdi -> "ic" (instruction counter)
; rbp -> beginning of VM stack
; rsp -> end of VM stack
; r8 -> temporary
; r9 -> "r1"
; r10 -> "r2"
; r11 -> "r3"
; r12 -> "r4"
; r13 -> "r5"
; r14 -> "r6"
; r15 -> "r7"
; xmm0 -> temporary
; xmm1 -> "f1"
; xmm2 -> "f2"
; xmm3 -> "f3"
; xmm4 -> "f4"
; xmm5 -> "f5"
; xmm6 -> "f6"
; xmm7 -> "f7"
; xmm8 -> "f0"
; xmm9 -> temporary
; STACK STRUCTURE:
; |
; |
; | saved registers
; |
; v
; [rbp+8] RegisterFile& registerFile
; [rbp] DatasetReadFunc readFunc
; |
; |
; | VM stack
; |
; v
; [rsp] last element of VM stack
; store callee-saved registers
push rbx
push rbp
push rdi
push rsi
push r12
push r13
push r14
push r15
sub rsp, 64
movdqu xmmword ptr [rsp+48], xmm6
movdqu xmmword ptr [rsp+32], xmm7
movdqu xmmword ptr [rsp+16], xmm8
movdqu xmmword ptr [rsp+0], xmm9
; function arguments
push rcx ; RegisterFile& registerFile
; mov rdx, rdx ; MemoryRegisters& memory
push r8 ; DatasetReadFunc readFunc
mov rsi, r9 ; convertible_t& scratchpad
mov rbp, rsp ; beginning of VM stack
mov rdi, 1048576 ; number of VM instructions to execute
; load VM register values
mov rbx, qword ptr [rcx+0]
mov r9, qword ptr [rcx+8]
mov r10, qword ptr [rcx+16]
mov r11, qword ptr [rcx+24]
mov r12, qword ptr [rcx+32]
mov r13, qword ptr [rcx+40]
mov r14, qword ptr [rcx+48]
mov r15, qword ptr [rcx+56]
movd xmm8, qword ptr [rcx+64]
movd xmm1, qword ptr [rcx+72]
movd xmm2, qword ptr [rcx+80]
movd xmm3, qword ptr [rcx+88]
movd xmm4, qword ptr [rcx+96]
movd xmm5, qword ptr [rcx+104]
movd xmm6, qword ptr [rcx+112]
movd xmm7, qword ptr [rcx+120]
; program body
include program.inc
rx_finish:
; unroll the stack
mov rsp, rbp
add rsp, 16
; save VM register values
mov rcx, qword ptr [rbp+8]
mov qword ptr [rcx+0], rbx
mov qword ptr [rcx+8], r9
mov qword ptr [rcx+16], r10
mov qword ptr [rcx+24], r11
mov qword ptr [rcx+32], r12
mov qword ptr [rcx+40], r13
mov qword ptr [rcx+48], r14
mov qword ptr [rcx+56], r15
movd qword ptr [rcx+64], xmm8
movd qword ptr [rcx+72], xmm1
movd qword ptr [rcx+80], xmm2
movd qword ptr [rcx+88], xmm3
movd qword ptr [rcx+96], xmm4
movd qword ptr [rcx+104], xmm5
movd qword ptr [rcx+112], xmm6
movd qword ptr [rcx+120], xmm7
; load callee-saved registers
movdqu xmm9, xmmword ptr [rsp+0]
movdqu xmm8, xmmword ptr [rsp+16]
movdqu xmm7, xmmword ptr [rsp+32]
movdqu xmm6, xmmword ptr [rsp+48]
add rsp, 64
pop r15
pop r14
pop r13
pop r12
pop rsi
pop rdi
pop rbp
pop rbx
; return
ret 0
rx_read_dataset:
push rdx
push r9
push r10
push r11
sub rsp, 32
call qword ptr [rbp]
add rsp, 32
pop r11
pop r10
pop r9
pop rdx
ret 0
executeProgram ENDP
END

View File

@ -18,6 +18,8 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/
//#define TRACE
#include "InterpretedVirtualMachine.hpp"
#include "CompiledVirtualMachine.hpp"
#include "AssemblyGeneratorX86.hpp"
#include "Stopwatch.hpp"
#include "blake2/blake2.h"
#include <fstream>
@ -27,6 +29,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <cstring>
#include "Program.hpp"
#include <string>
#include "instructions.hpp"
const uint8_t seed[32] = { 191, 182, 222, 175, 249, 89, 134, 104, 241, 68, 191, 62, 162, 166, 61, 64, 123, 191, 227, 193, 118, 60, 188, 53, 223, 133, 175, 24, 123, 230, 55, 74 };
@ -73,16 +76,14 @@ std::ostream& operator<<(std::ostream& os, const RandomX::RegisterFile& rf) {
}
int main(int argc, char** argv) {
bool softAes, lightClient;
bool softAes, lightClient, genAsm, compiled;
int programCount;
readOption("--softAes", argc, argv, softAes);
readOption("--lightClient", argc, argv, lightClient);
readOption("--genAsm", argc, argv, genAsm);
readOption("--compiled", argc, argv, compiled);
readInt(argc, argv, programCount, 1000);
if (softAes)
std::cout << "Using software AES." << std::endl;
uint8_t hash[32];
char cumulative[32] = { 0 };
unsigned char blockTemplate[] = {
0x07, 0x07, 0xf7, 0xa4, 0xf0, 0xd6, 0x05, 0xb3, 0x03, 0x26, 0x08, 0x16, 0xba, 0x3f, 0x10, 0x90, 0x2e, 0x1a, 0x14,
0x5a, 0xc5, 0xfa, 0xd3, 0xaa, 0x3a, 0xf6, 0xea, 0x44, 0xc1, 0x18, 0x69, 0xdc, 0x4f, 0x85, 0x3f, 0x00, 0x2b, 0x2e,
@ -90,11 +91,34 @@ int main(int argc, char** argv) {
0xc3, 0x8b, 0xde, 0xd3, 0x4d, 0x2d, 0xcd, 0xee, 0xf9, 0x5c, 0xd2, 0x0c, 0xef, 0xc1, 0x2f, 0x61, 0xd5, 0x61, 0x09
};
int* nonce = (int*)(blockTemplate + 39);
RandomX::InterpretedVirtualMachine vm(softAes);
uint8_t hash[32];
if (genAsm) {
*nonce = programCount;
blake2b(hash, sizeof(hash), blockTemplate, sizeof(blockTemplate), nullptr, 0);
RandomX::AssemblyGeneratorX86 asmX86;
asmX86.generateProgram(hash);
asmX86.printCode(std::cout);
return 0;
}
if (softAes)
std::cout << "Using software AES." << std::endl;
char cumulative[32] = { 0 };
RandomX::VirtualMachine* vm;
if (compiled) {
vm = new RandomX::CompiledVirtualMachine(softAes);
}
else {
vm = new RandomX::InterpretedVirtualMachine(softAes);
}
try {
std::cout << "Initializing..." << std::endl;
Stopwatch sw(true);
vm.initializeDataset(seed, lightClient);
vm->initializeDataset(seed, lightClient);
if(lightClient)
std::cout << "Cache (64 MiB) initialized in " << sw.getElapsed() << " s" << std::endl;
else
@ -106,16 +130,15 @@ int main(int argc, char** argv) {
if (RandomX::trace) std::cout << "Nonce: " << i << " ";
blake2b(hash, sizeof(hash), blockTemplate, sizeof(blockTemplate), nullptr, 0);
int spIndex = hash[24] | ((hash[25] & 63) << 8);
vm.initializeScratchpad(spIndex);
vm->initializeScratchpad(spIndex);
//dump((const char *)vm.getScratchpad(), RandomX::ScratchpadSize, "scratchpad-before.txt");
//return 0;
vm.initializeProgram(hash);
vm.execute();
vm->initializeProgram(hash);
vm->execute();
/*std::string fileName("scratchpad-after-");
fileName = fileName + std::to_string(i) + ".txt";
dump((const char *)vm.getScratchpad(), RandomX::ScratchpadSize, fileName.c_str());*/
blake2b((void*)hash, sizeof(hash), &vm.getRegisterFile(), sizeof(RandomX::RegisterFile), nullptr, 0);
//std::cout << vm.getRegisterFile();
blake2b((void*)hash, sizeof(hash), &vm->getRegisterFile(), sizeof(RandomX::RegisterFile), nullptr, 0);
if (RandomX::trace) {
outputHex(std::cout, (char*)hash, sizeof(hash));
}
@ -124,9 +147,10 @@ int main(int argc, char** argv) {
((uint64_t*)cumulative)[2] ^= ((uint64_t*)hash)[2];
((uint64_t*)cumulative)[3] ^= ((uint64_t*)hash)[3];
}
double elapsed = sw.getElapsed();
std::cout << "Cumulative output hash: ";
outputHex(std::cout, cumulative, sizeof(cumulative));
std::cout << "Performance: " << programCount / sw.getElapsed() << " programs per second" << std::endl;
std::cout << "Performance: " << programCount / elapsed << " programs per second" << std::endl;
}
catch (std::exception& e) {
std::cout << "ERROR: " << e.what() << std::endl;

7033
src/program.inc Normal file

File diff suppressed because it is too large Load Diff