1. ## DAsmJit

DAsmJit, pronounced as "The Awesome JIT", is a x86 assembler for Delphi and FPC. It's a port from the 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 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:

pascal Code:
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:

pascal Code:
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 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

2. Nice Well i don't know much of this but this really looks nice

~Home

3. Very nice...

4. Registered User
Join Date
Jul 2015
Posts
3
Mentioned
0 Post(s)
Quoted
1 Post(s)
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.

5. 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?

6. Registered User
Join Date
Jul 2015
Posts
3
Mentioned
0 Post(s)
Quoted
1 Post(s)
Once you choose to compile for x64 the errors begin in the DAsmJit_CpuInfo unit.

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

So i went ahead and replaced them:

Code:
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: Code: // [DAsmJit - Architecture] {$IFDEF CPU64}
{$DEFINE ASMJIT_X64} // x86-64 {$ELSE}
{$DEFINE ASMJIT_X86} {$ENDIF}
to

Code:
// [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 7. Translation for on-lookers: Simba Code: 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 referencesend; 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. Last edited by Brandon; 08-01-2015 at 09:13 PM. 8. Registered User Join Date Jul 2015 Posts 3 Mentioned 0 Post(s) Quoted 1 Post(s) 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: Code: 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; Last edited by sn0w8321; 08-02-2015 at 06:33 PM. 9. Registered User Join Date Apr 2016 Posts 3 Mentioned 0 Post(s) Quoted 0 Post(s) 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? 10. Originally Posted by sn0w8321 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: Code: 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. 11. Registered User Join Date Apr 2016 Posts 3 Mentioned 0 Post(s) Quoted 0 Post(s) There's a parameter error that causes this: find _emitX86RM($C7 in DAsmJit_Assembler.pas and change the 5th parameter from imm to reg.

12. Registered User
Join Date
May 2018
Posts
10
Mentioned
0 Post(s)
Quoted
1 Post(s)
Wish I knew how to use this

13. SRL Junior Member
Join Date
Dec 2016
Location
Michigan, USA
Posts
38
Mentioned
0 Post(s)
Quoted
8 Post(s)
Originally Posted by shiftlid
Wish I knew how to use this
same

14. Registered User
Join Date
Jul 2018
Posts
1
Mentioned
0 Post(s)
Quoted
0 Post(s)

Thanks, Bob

15. Registered User
Join Date
Apr 2016
Posts
3
Mentioned
0 Post(s)
Quoted
0 Post(s)
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