Xbyak - x86, x64 JIT assembler -
an ultimate optimization for x86(IA-32) and x64(AMD64, x86-64)
What's this?
This is a header file which enables dynamically to assemble x86(IA-32), x64(AMD64, x86-64)
mnemonic. Because we can generate binary a program while code is running, we can get the flexibility of optimazation(ex.
quantization,
polynomial calcuration).
application for fast encryption(
High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves)
Download
Feature
- header file only
- You can use Xbyak's functions at once if xbyak.h is included.
- support Windows Xp(32bit, 64bit), Vista/Linux(32bit, 64bit)/Intel Mac
- Xbyak runs on Visual Studio C++ 2005 Express Edition, VS2008 Pro, VC2010, mingw and gcc.
#"-fno-operator-names" option is required on gcc to avoid analyzing "and", "or", etc. as operators.
Or, define XBYAK_NO_OP_NAMES to use and_(), or_() instead.
- support almost all mnemonics of Pentium for user application
- MMX/MMX2/SSE/SSE2/SSE3/SSSE3/SSE4/FPU(partially)/AVX are available.
- Output small binary code if possible
- "cmp(eax, 5);" means "cmp eax, byte 5" on NASM.
License
How to use
On Linux,
>sudo make install
or copy xbyak.h, xbyak_mnemonic.h and xbyak_bin2hex.h into the same directory(ex. /usr/local/include/xbyak/),
and specify the directory under compiling your source(-I/usr/local/include/).
New Feature
AutoGrow mode is a mode that Xbyak grows memory automatically if necessary.
Call ready() before calling getCode() to calc address of jmp.
struct Code : Xbyak::CodeGenerator {
Code()
: Xbyak::CodeGenerator(<default memory size>, Xbyak::AutoGrow)
{
...
}
};
Code c;
c.ready(); // Don't forget to call this function
- Don't use the address returned by getCurr() before calling ready().
It may be invalid address.
- RESTRICTION : rip addressing is not supported in AutoGrow
Syntax
Create your class inheriting Xbyak::CodeGenerator and write x86, x64 mnemonics in your class method.
After calling the method and call Xbyak::getCode() and cast the return value into function pointer as you like.
NASM Xbyak
mov eax, ebx --> mov(eax, ebx);
inc ecx --> inc(ecx);
ret --> ret();
ADDRESSING
(ptr|dword|word|byte) [base + index * (1|2|4|8) + displacement]
[rip + 32bit disp] ; x64 only
Selector is not supported.
dword, word, byte are class variables, then don't use these name
NASM Xbyak
mov eax, [ebx+ecx] --> mov (eax, ptr[ebx+ecx]);
test byte [esp], 4 --> test (byte [esp], 4);
AVX
You can omit a destination for almost 3-op mnemonics.
vaddps(xmm1, xmm2, xmm3); // xmm1 <- xmm2 + xmm3
vaddps(xmm2, xmm3); // xmm2 <- xmm2 + xmm3
LABEL
L(string);
Specify the string when you want to jump.
Use T_NEAR when a relative address offset is bigger than 8bit.
Otherwise ERR_LABEL_IS_TOO_FAR exception will occur.
Example:
L("L1");
jmp ("L1");
jmp ("L2");
...
(small code)
...
L("L2");
jmp ("L3", T_NEAR);
...
(large code)
...
L("L3");
Code Size
The maximum default code size is 2048 bytes.
If you want bigger size, then specify CodeGenerator(int maxSize).
SAMPLE
Tiny samples
sample 1
Generating add function
#include <stdio.h>
#include "xbyak/xbyak.h"
struct AddFunc : public Xbyak::CodeGenerator {
AddFunc(int y)
{
mov(eax, ptr[esp+4]);
add(eax, y);
ret();
}
};
int main()
{
AddFunc a(3);
int (*add3)(int) = (int (*)(int))a.getCode();
printf("3 + 2 = %d\n", add3(2));
}
The content indicated by the function pointer is the following.
mov eax, dword ptr [esp+4]
add eax, 3
ret
sample 2
How to use jmp
/*!
sum from 1 to n
*/
class Sample : public Xbyak::CodeGenerator {
public:
Sample(int n)
{
mov(ecx, n); // -- (A)
xor(eax, eax); // sum
test(ecx, ecx);
jbe("exit");
xor(edx, edx); // i
L("lp");
add(eax, edx);
inc(edx);
cmp(edx, ecx);
jbe("lp");
L("exit");
ret();
}
};
int main(int argc, char *argv[])
{
int n = argc < 2 ? 100 : atoi(argv[1]);
try {
Sample s(n);
printf("1 + ... + %d = %d\n", n, ((int (*)())s.getCode())());
} catch (Xbyak::Error err) {
printf("ERR:%s(%d)\n", Xbyak::ConvertErrorToString(err), err);
} catch (...) {
printf("unkwon error\n");
}
return 0;
}
In Sample() constructor, Xbyak generates the function that outputs the sum from 1 to n.
When a part of (A) is called, the value of n is determinated, then Xbyak can assemble it.
Introduction to Xbyak
Where Xbyak should be used?
Xbyak is quite different from inline assembler.
For example, if you write the following code by inline assembler,
func(int n)
{
__asm {
mov eax, n
}
}
then, maybe a complier will automatically generate stack frame like
push ebp
mov ebp, esp
mov eax, [ebp+8] .
But Xbyak does nothing like this.
You must make stack frame if necessary by yourself.
On the other hand, you can't write "mov eax, n" when n is not determinated on
inline assembler or standard assembler.
You must write "mov ebx, [pointer to n] / mov eax, ebx".
quantize.cpp is an example for Quantization,
which is used in encoding process of JPEG or MPEG.
Quantization is an operation that devides a given array by a given other array.
void quantize(uint32 dest[64],
const uint32 src[64], const uint32 qTbl[64])
{
for (int i = 0; i < N; i++) {
dest[i] = src[i] / qTbl[i];
}
}
qTbl[] is fixed value in encoding JPEG, but the content is variable by quality parameter.
By the way, division is very more heavy operation thatn add/sub/mul, so we want to optimize
it by avoiding division.
For example, VC++ generates the following code.
// C
uint32 func(uint32 n)
{
return n / 10;
}
// asm
mov eax, cccccccdH
mul DWORD PTR _n$[esp-4]
mov eax, edx
shr eax, 3
ret
But this technique can be possible divisor is fixed in compiling, then we can't use
it in quantize().
Then we do it while code is running by using Xbyak.
Quantize::udiv() function generates the optimized division code for a given divior.
Benchmark on Pentium D 2.8GHz + VC2005 Express Edition(unit:second).
Compare ordinary quantization with optimized quantization by Xbyak(second)
quantization speed
quality | q = 1(low) | q = 10 | q = 50 | q = 100(high) |
VC2005 | 8.0 | 8.0 | 8.0 | 8.0 |
Xbyak | 1.6 | 0.8 | 0.5 | 0.5 |
The socre of ordinary quantization is constant.
It takes 8.0 * 2.8 * 109 / 64 / 107 = 35 clocks per one division.
On the other hand, Xbyak is fast.
; generated code on q = 1.
push esi
push edi
mov edi,dword ptr [esp+0Ch]
mov esi,dword ptr [esp+10h]
mov eax,dword ptr [esi]
shr eax,4
mov dword ptr [edi],eax ; / 16
mov eax,dword ptr [esi+4]
mov edx,0BA2E8BA3h
mul eax,edx
shr edx,3 ; / 11
...
; generated code on q = 100
push esi
push edi
mov edi,dword ptr [esp+0Ch]
mov esi,dword ptr [esp+10h]
mov eax,dword ptr [esi]
mov dword ptr [edi],eax
mov eax,dword ptr [esi+4] ; / 1
mov dword ptr [edi+4],eax
mov eax,dword ptr [esi+8]
mov dword ptr [edi+8],eax ; / 1
mov eax,dword ptr [esi+0Ch]
mov dword ptr [edi+0Ch],eax
...
polynomial calculation
calc.cpp is a tiny polynomial calculation sample with
boost::spirit.
Create actions according to constant/variable x/add/sub/mul/div etc.
void genPush(double n)
{
if (constTblPos_ == MAX_CONST_NUM) throw;
constTbl_[constTblPos_] = static_cast<float>(n);
if (regIdx_ == 7) throw;
movss(Xbyak::Xmm(++regIdx_), ptr[edx+constTblPos_*sizeof(float)]);
constTblPos_++;
}
void genSub(const char*, const char*)
{
subss(Xbyak::Xmm(regIdx_ - 1), Xbyak::Xmm(regIdx_)); regIdx_--;
}
struct Grammar : public boost::spirit::grammar<Grammar> {
FuncGen& f_;
Grammar(FuncGen& f) : f_(f) { }
template<typename ScannerT>
struct definition {
boost::spirit::rule<ScannerT> exp0, exp1, exp2, val;
definition(const Grammar& self)
{
using namespace boost;
using namespace boost::spirit;
exp0 = exp1 >> *(('+' >> exp1)[bind(&FuncGen::genAdd, ref(self.f_), _1, _2)]
| ('-' >> exp1)[bind(&FuncGen::genSub, ref(self.f_), _1, _2)]);
exp1 = exp2 >> *(('*' >> exp2)[bind(&FuncGen::genMul, ref(self.f_), _1, _2)]
| ('/' >> exp2)[bind(&FuncGen::genDiv, ref(self.f_), _1, _2)]);
val = ch_p('x')[bind(&FuncGen::genX, ref(self.f_))];
exp2 = real_p[bind(&FuncGen::genPush, ref(self.f_), _1)]
| val
| '(' >> exp0 >> ')';
}
const boost::spirit::rule<ScannerT>& start() const { return exp0; }
};
};
Output values by generated function.
void (*func)(float *ret, const float *x) = (void (*)(float *, const float*))funcGen.getCode();
for (float x = 0; x < 10; x += 0.7f) {
float y;
func(&y, &x);
printf("f(%f)=%f\n", x, y);
}
...
For example, you type "x+2*(x*x+3/x)", then
you can get the code when program is running.
; @param y [out] f(x)
; @param x [in] x
; void func(float *y, const float *x);
mov eax,dword ptr [esp+8]
mov edx,12FEA8h
movss xmm0,dword ptr [eax]
movss xmm1,dword ptr [edx]
movss xmm2,dword ptr [eax]
movss xmm3,dword ptr [eax]
mulss xmm2,xmm3
movss xmm3,dword ptr [edx+4]
movss xmm4,dword ptr [eax]
divss xmm3,xmm4
addss xmm2,xmm3
mulss xmm1,xmm2
addss xmm0,xmm1
mov eax,dword ptr [esp+4]
movss dword ptr [eax],xmm0
ret
>calc "x*x+3*x+5"
f(0.000000)=5.000000
f(0.700000)=7.590000
f(1.400000)=11.160000
f(2.100000)=15.709999
f(2.800000)=21.240000
f(3.500000)=27.750000
f(4.200000)=35.239998
f(4.900000)=43.709995
f(5.599999)=53.159992
f(6.299999)=63.589989
f(6.999999)=74.999985
f(7.699999)=87.389977
f(8.399999)=100.759972
f(9.099998)=115.109970
f(9.799998)=130.439957
1st:2007/1/17, last update:2016/08/29
mailto:MITSUNARI Shigeo<herumi@nifty.com>