dynarmic/sample/toyvm.cpp
MerryMage 080b4b3aff Squashed 'externals/xbyak/' changes from 671fc805..4a6fac8a
4a6fac8a update version to 5.77
801cf3fd cosmetic change of getNumCores
d397e824 fix number of cores that share LLC cache
a669e092 support non-intel-cpu visual studio
af5f422e Merge branch 'fenghaitao-guard_x86' into develop
9b98dc17 Guard x86 specific codes with "#if defined(__i386__) || defined(__x86_64__)"
dd4173e1 move some member variables input private
f72646a7 update version
4612528f format change
4b95e862 Merge branch 'shelleygoel-master'
4c262fa6 add functionality to get num of cores using x2APIC ID
bc70e7e1 recover Xbyak::CastTo
d09a230f unlink Label when LabelManager is destroyed
973e8597 update version
afdb9fe9 Xbyak::CastTo is removed
b011aca4 add RegRip +/- int
acae93cd increase max temp regs for StackFrame
ea4e3562 util::StackFrame uses push/pop instead of mov
42462ef9 use evex encoding for vpslld/vpslldq/vpsraw/...(reg, mem, imm);
da9117a9 update version of readme.md
d35f4fb7 fix the encoding of vinsertps for disp8N
1de435ed bf uses Label class
613922bd add Label L() for convenience
43e15583 fix typo
93579ee6 add protect-re.cpp
60004b5c fix url of protect-re.cpp
348b2709 fix typo of doc
f34f6ed5 update manual
232110be update test
82b78bf0 add setProtectMode
dd8b290f put warning message if pageSize != 4096
64775ca2 a little refactoring
7c3e7b85 fix wrong VSIB encoding with idx >= 16

git-subtree-dir: externals/xbyak
git-subtree-split: 4a6fac8ade404f667b94170f713367fe7da2a852
2020-04-22 20:59:14 +01:00

380 lines
7 KiB
C++

/*
toy vm
register A, B : 32bit
PC : program counter
mem_ 4byte x 65536
すべての命令は4byte固定
即値は全て16bit
R = A or B
vldiR, imm ; R = imm
vldR, idx ; R = mem_[idx]
vstR, idx ; mem_[idx] = R
vaddiR, imm ; R += imm
vsubiR, imm ; R -= imm
vaddR, idx ; R += mem_[idx]
vsubR, idx ; R -= mem_[idx]
vputR ; print R
vjnzR, offset; if (R != 0) then jmp(PC += offset(signed))
*/
#if defined(_MSC_VER) && (_MSC_VER <= 1200)
#pragma warning(disable:4514)
#pragma warning(disable:4786)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <vector>
#define XBYAK_NO_OP_NAMES
#include "xbyak/xbyak.h"
#include "xbyak/xbyak_util.h"
#define NUM_OF_ARRAY(x) (sizeof(x) / sizeof(x[0]))
#ifdef XBYAK64
#error "only 32bit"
#endif
using namespace Xbyak;
class ToyVm : public Xbyak::CodeGenerator {
typedef std::vector<uint32> Buffer;
public:
enum Reg {
A, B
};
enum Code {
LD, LDI, ST, ADD, ADDI, SUB, SUBI, PUT, JNZ,
END_OF_CODE
};
ToyVm()
: mark_(0)
{
::memset(mem_, 0, sizeof(mem_));
}
void vldi(Reg r, uint16 imm) { encode(LDI, r, imm); }
void vld(Reg r, uint16 idx) { encode(LD, r, idx); }
void vst(Reg r, uint16 idx) { encode(ST, r, idx); }
void vadd(Reg r, uint16 idx) { encode(ADD, r, idx); }
void vaddi(Reg r, uint16 imm) { encode(ADDI, r, imm); }
void vsub(Reg r, uint16 idx) { encode(SUB, r, idx); }
void vsubi(Reg r, uint16 imm) { encode(SUBI, r, imm); }
void vjnz(Reg r, int offset) { encode(JNZ, r, static_cast<uint16>(offset)); }
void vput(Reg r) { encode(PUT, r); }
void setMark()
{
mark_ = (int)code_.size();
}
int getMarkOffset()
{
return mark_ - (int)code_.size() - 1;
}
void run()
{
bool debug = false;//true;
uint32 reg[2] = { 0, 0 };
const size_t end = code_.size();
uint32 pc = 0;
for (;;) {
uint32 x = code_[pc];
uint32 code, r, imm;
decode(code, r, imm, x);
if (debug) {
printf("---\n");
printf("A %08x B %08x\n", reg[0], reg[1]);
printf("mem_[] = %08x %08x %08x\n", mem_[0], mem_[1], mem_[2]);
printf("pc=%4d, code=%02x, r=%d, imm=%04x\n", pc, code, r, imm);
}
switch (code) {
case LDI:
reg[r] = imm;
break;
case LD:
reg[r] = mem_[imm];
break;
case ST:
mem_[imm] = reg[r];
break;
case ADD:
reg[r] += mem_[imm];
break;
case ADDI:
reg[r] += imm;
break;
case SUB:
reg[r] -= mem_[imm];
break;
case SUBI:
reg[r] -= imm;
break;
case PUT:
printf("%c %8d(0x%08x)\n", 'A' + r, reg[r], reg[r]);
break;
case JNZ:
if (reg[r] != 0) pc += static_cast<signed short>(imm);
break;
default:
assert(0);
break;
}
pc++;
if (pc >= end) break;
} // for (;;)
}
void recompile()
{
using namespace Xbyak;
/*
esi : A
edi : B
ebx : mem_
for speed up
mem_[0] : eax
mem_[1] : ecx
mem_[2] : edx
*/
push(ebx);
push(esi);
push(edi);
const Reg32 reg[2] = { esi, edi };
const Reg32 mem(ebx);
const Reg32 memTbl[] = { eax, ecx, edx };
const size_t memTblNum = NUM_OF_ARRAY(memTbl);
for (size_t i = 0; i < memTblNum; i++) xor_(memTbl[i], memTbl[i]);
xor_(esi, esi);
xor_(edi, edi);
mov(mem, (size_t)mem_);
const size_t end = code_.size();
uint32 pc = 0;
uint32 labelNum = 0;
for (;;) {
uint32 x = code_[pc];
uint32 code, r, imm;
decode(code, r, imm, x);
L(Label::toStr(labelNum++));
switch (code) {
case LDI:
mov(reg[r], imm);
break;
case LD:
if (imm < memTblNum) {
mov(reg[r], memTbl[imm]);
} else {
mov(reg[r], ptr[mem + imm * 4]);
}
break;
case ST:
if (imm < memTblNum) {
mov(memTbl[imm], reg[r]);
} else {
mov(ptr [mem + imm * 4], reg[r]);
}
break;
case ADD:
if (imm < memTblNum) {
add(reg[r], memTbl[imm]);
} else {
add(reg[r], ptr [mem + imm * 4]);
}
break;
case ADDI:
add(reg[r], imm);
break;
case SUB:
if (imm < memTblNum) {
sub(reg[r], memTbl[imm]);
} else {
sub(reg[r], ptr [mem + imm * 4]);
}
break;
case SUBI:
sub(reg[r], imm);
break;
case PUT:
{
static const char *str = "%c %8d(0x%08x)\n";
push(eax);
push(edx);
push(ecx);
push(reg[r]);
push(reg[r]);
push('A' + r);
push((int)str);
call(reinterpret_cast<const void*>(printf));
add(esp, 4 * 4);
pop(ecx);
pop(edx);
pop(eax);
}
break;
case JNZ:
test(reg[r], reg[r]);
jnz(Label::toStr(labelNum + static_cast<signed short>(imm)));
break;
default:
assert(0);
break;
}
pc++;
if (pc >= end) break;
} // for (;;)
pop(edi);
pop(esi);
pop(ebx);
ret();
}
private:
uint32 mem_[65536];
Buffer code_;
int mark_;
void decode(uint32& code, uint32& r, uint32& imm, uint32 x)
{
code = x >> 24;
r = (x >> 16) & 0xff;
imm = x & 0xffff;
}
void encode(Code code, Reg r, uint16 imm = 0)
{
uint32 x = (code << 24) | (r << 16) | imm;
code_.push_back(x);
}
};
class Fib : public ToyVm {
public:
Fib(int n)
{
if (n >= 65536) {
fprintf(stderr, "current version support only imm16\n");
return;
}
/*
A : c
B : temporary
mem_[0] : p
mem_[1] : t
mem_[2] : n
*/
vldi(A, 1); // c
vst(A, 0); // p(1)
vldi(B, static_cast<uint16>(n));
vst(B, 2); // n
// lp
setMark();
vst(A, 1); // t = c
vadd(A, 0); // c += p
vld(B, 1);
vst(B, 0); // p = t
// vput(A);
vld(B, 2);
vsubi(B, 1);
vst(B, 2); // n--
vjnz(B, getMarkOffset());
vput(A);
}
void runByJIT()
{
getCode<void (*)()>();
}
};
void fibC(uint32 n)
{
uint32 p, c, t;
p = 1;
c = 1;
lp:
t = c;
c += p;
p = t;
n--;
if (n != 0) goto lp;
printf("c=%d(0x%08x)\n", c, c);
}
int main()
{
try {
const int n = 10000;
Fib fib(n);
fib.recompile();
{
Xbyak::util::Clock clk;
clk.begin();
fib.run();
clk.end();
printf("vm %.2fKclk\n", clk.getClock() * 1e-3);
}
{
Xbyak::util::Clock clk;
clk.begin();
fib.runByJIT();
clk.end();
printf("jit %.2fKclk\n", clk.getClock() * 1e-3);
}
{
Xbyak::util::Clock clk;
clk.begin();
fibC(n);
clk.end();
printf("native C %.2fKclk\n", clk.getClock() * 1e-3);
}
} catch (std::exception& e) {
printf("ERR:%s\n", e.what());
} catch (...) {
printf("unknown error\n");
}
return 0;
}
/*
the code generated by Xbyak
push ebx
push esi
push edi
xor eax,eax
xor ecx,ecx
xor edx,edx
xor esi,esi
xor edi,edi
mov ebx,0EFF58h
mov esi,1
mov eax,esi
mov edi,2710h
mov edx,edi
.lp:
mov ecx,esi
add esi,eax
mov edi,ecx
mov eax,edi
mov edi,edx
sub edi,1
mov edx,edi
test edi,edi
jne .lp
push eax
push edx
push ecx
push esi
push esi
push 41h
push 42C434h
call printf (409342h)
add esp,10h
pop ecx
pop edx
pop eax
pop edi
pop esi
pop ebx
ret
*/