PDA

View Full Version : DAsmJit



nielsie95
05-03-2010, 03:10 PM
DAsmJit, pronounced as "The Awesome JIT", is a x86 assembler for Delphi and FPC. It's a port from the asmjit (http://code.google.com/p/asmjit/) project.

DAsmJit takes x86 assembly and then generates the machine code for it. This will enable you to compile functions at runtime and use them as if they're native Delphi functions. This way you can create optimized functions for specific CPU's (MMX, SSE, SSE2 support etc.). For more information I would like to forward you to the original (http://code.google.com/p/asmjit/) project.

I decided to port a jit project to Delphi, because I couldn't find one. I've chosen for asmjit, because it was the smallest library I could find. Unfortunately, the author is working on a revamped version (version 1.0) of the library which comes with a lot of changes and I don't know if I'll update this project accordingly.. However, the current version is stable and does what it is supposed to :)

Here's an example on how to use the Assembler class:


procedure TForm1.dbg(s: string);
begin
Memo1.Lines.Text := Memo1.Lines.Text + s;
end;

procedure TForm1.Button1Click(Sender: TObject);
type
IncProc = function(i: Integer): Integer;
var
a: TAssembler;
b: IncProc;
p: Pointer;
begin
a := TAssembler.Create;
a.Logger := TCallBackLogger.Create(@dbg);

//a.inc_(nax);
a.push(nbx);
a.mov(nbx, imm(5));
a.imul(nbx);
a.sub(nax, imm(50));
a.pop(nbx);
a.ret;

p := a.Make;
b := IncProc(p);
ShowMessage(IntToStr(b(5)));

TMemoryManager.Global.FreeAddress(p);
a.Logger.Free;
a.Free;
end;


And how to use the compiler class:


procedure TForm1.Button8Click(Sender: TObject);
type
MyFn = procedure(a, b: Integer; c: PBoolean);
const
args: array[0..2] of UInt32 = (VARIABLE_TYPE_SYSINT, VARIABLE_TYPE_SYSINT, VARIABLE_TYPE_PTR);
var
c: TCompiler;
f: TFunction;
src0, src1, dst0: TPtrRef;
p: Pointer;
b: MyFn;
x: Boolean;
begin
c := TCompiler.Create;
c.Logger := TCallBackLogger.Create(@dbg);

f := c.NewFunction_(CALL_CONV_BORLANDFASTCALL, @args, 3);

src0.Create(f.Argument(0));
src1.Create(f.Argument(1));
dst0.Create(f.Argument(2));

c.cmp(src0.c, src1.c);
c.setz(byte_ptr(dst0.c));

c.EndFunction;

p := c.Make;
b := MyFn(p);

b(1, 1, @x);
ShowMessage('1, 1 = ' + IntToStr(Integer(x)));

TMemoryManager.Global.FreeAddress(p);
c.Logger.Free;
c.Free;
end;


The compiler class handles things like calling convention, function prolog/epilog and variables. This way it's easier to create portable code.

I've also ported the example project MathPresso (http://code.google.com/p/mathpresso/) the author wrote. It is a simple mathematical expression evaluator that compiles the expression into SSE code. It's pretty fast (most of the time it's faster than compiled Delphi code). Unfortunately, because there are only 8 SSE registers there is a limit to the difficulty of the expression.

In the future I'd like to write my own expression evaluator with it and I want it to produce code equal to Delphi or even better (SSE/MMX optimizations). Maybe I will also try to keep this up to date with the c++ version, if version 1.0 turns out to be a great improvement.

It should compile on both Delphi and Lazarus. I haven't tried it with 64 bit nor Linux. 64 bit should work, but for Linux I need to know the correct FPC equivalents to the c++ code.

~Nielsie95

Home
05-06-2010, 08:46 AM
Nice :) Well i don't know much of this but this really looks nice :)

~Home

Wizzup?
05-08-2010, 09:36 PM
Very nice... :)

sn0w8321
08-01-2015, 06:39 PM
Thank you very much for the port.

Unfortunately it doesn't compile for x64 in the latest Delphi Version XE8.

I enjoyed using it for 32bit as it is the best thing that compares to asmjit in Pascal / Delphi.

nielsie95
08-01-2015, 07:22 PM
Good to hear someone is using this! :)

I currently do not have access to an XE8 installation. Could you perhaps elaborate on the errors you are getting?

sn0w8321
08-01-2015, 07:48 PM
Once you choose to compile for x64 the errors begin in the DAsmJit_CpuInfo unit.


https://villavu.com/forum/attachment.php?attachmentid=26551&d=1438457470


I guessed that's cause the x64 registers begin with r instead of e.

So i went ahead and replaced them:





procedure cpuId(varIn: NativeUInt; varOut: PCpuId); assembler;
asm

{$IFDEF ASMJIT_X64}

//x64
push rax
push rbx
push rcx
push rdx
push rdi
mov rax, varIn
mov rdi, varOut
cpuid
mov dword64 ptr[rdi + 0], rax
mov dword64 ptr[rdi + 4], rbx
mov dword64 ptr[rdi + 8], rcx
mov dword64 ptr[rdi + 12], rdx
pop rdi
pop rdx
pop rcx
pop rbx
pop rax

{$ELSE}

//x86
push eax
push ebx
push ecx
push edx
push edi
mov eax, varIn
mov edi, varOut
cpuid
mov dword ptr[edi + 0], eax
mov dword ptr[edi + 4], ebx
mov dword ptr[edi + 8], ecx
mov dword ptr[edi + 12], edx
pop edi
pop edx
pop ecx
pop ebx
pop eax

{$ENDIF}

end;



I also had to replace the declaration to use the NativeUInt instead of
UInt32 and change DWORD to DWORD64.

Furthermore i changed DAsmJit.inc from:




// [DAsmJit - Architecture]
{$IFDEF CPU64}
{$DEFINE ASMJIT_X64} // x86-64
{$ELSE}
{$DEFINE ASMJIT_X86}
{$ENDIF}

to



// [DAsmJit - Architecture]
{$IFDEF CPUX64}
{$DEFINE ASMJIT_X64} // x86-64
{$ELSE}
{$DEFINE ASMJIT_X86}
{$ENDIF}

like mentioned here: docwiki.embarcadero.com/RADStudio/XE8/en/Conditional_compilation_%28Delphi%29



I am learning asm with delphi and no expert by any means.

Those changes i made could be (and likely are) totally wrong.

I would be very happy if we could get it running on x64.

Also thanks for your fast answer :)

Brandon
08-01-2015, 08:11 PM
Translation for on-lookers:


procedure GetCPUID(varIn: UInt32, var varOut: array[0..4] of LongInt);
var
rax, rbx, rcx, rdx: Integer;
begin
rax := varIn;
cpuid(rax, rbx, rcx, rdx); //all parameters are "var" aka references.
varOut[0] := rax;
varOut[1] := rbx;
varOut[2] := rcx;
varOut[3] := rdx;
end;

//or:

procedure GetCPUID(varIn: UInt32, var varOut: array[0..4] of LongInt);
begin
varOut[0] := varIn;
cpuid(varOut[0], varOut[1], varOut[2], varOut[3]); //all parameters are "var" aka references
end;


Where varOut will contain your CPUID and varIn contains the flags to pass to the CPUID function. Ex: varIn aka EAX/RAX would be 0 to get vendor id which will be stored in varOut.

sn0w8321
08-02-2015, 06:48 AM
Spent the whole night trying to make it x64 compatible. I think i'll have to pass :(

I'll have a look at it when i am more familar with x64 ASM.

If anyone else would have a look over x64 compability that would be much appreciated.

To be more specific the Assert in the function always fails in x64:



procedure TAssembler._emitModRM(opReg: UInt8; const op: TOperand; immSize: SysInt);
begin
Assert((op.op = OP_REG) or (op.op = OP_MEM));

if (op.op = OP_REG) then
_emitModR(opReg, TBaseReg(op).Code)
else
_emitModM(opReg, TMem(op), immSize);
end;

fimkold
04-27-2016, 03:37 PM
nielsie95: Thanks for the port! Did you consider publishing it on Github for example?

sn0w8321: With minor modifications it works in 64bit mode with recent Freepascal. However I never experienced the issue with emit method - it was just the cpuid + 2 other small modifications that were enough to make it work. Maybe because I tried just some simple cases - what is the specific code you have issue with?

R0b0t1
01-31-2017, 08:31 PM
Spent the whole night trying to make it x64 compatible. I think i'll have to pass :(

I'll have a look at it when i am more familar with x64 ASM.

If anyone else would have a look over x64 compability that would be much appreciated.

To be more specific the Assert in the function always fails in x64:



procedure TAssembler._emitModRM(opReg: UInt8; const op: TOperand; immSize: SysInt);
begin
Assert((op.op = OP_REG) or (op.op = OP_MEM));

if (op.op = OP_REG) then
_emitModR(opReg, TBaseReg(op).Code)
else
_emitModM(opReg, TMem(op), immSize);
end;


The only other type of operand is immediate. Are you trying to assemble an invalid instruction? If not, that would be why the error is being produced. No time to diagnose at the moment.

fimkold
05-14-2018, 12:39 PM
There's a parameter error that causes this: find _emitX86RM($C7 in DAsmJit_Assembler.pas and change the 5th parameter from imm to reg.

shiftlid
05-20-2018, 12:57 AM
Wish I knew how to use this

kyle12308
05-21-2018, 07:06 AM
Wish I knew how to use this

same

rdevine
07-16-2018, 02:16 PM
Hi Niels - looks interesting but I can't find a download link. Is it still available?

Thanks, Bob

fimkold
11-29-2018, 08:16 PM
As I see some value in the code and Nielsie seems to be MIA, I uploaded the code with some small fixes (mostly what's mentioned in previous posts to fix 64bit) to github.com/dpethes/dasmjit