Results 1 to 15 of 15

Thread: DAsmJit

  1. #1
    Join Date
    Sep 2006
    Posts
    6,089
    Mentioned
    77 Post(s)
    Quoted
    43 Post(s)

    Default 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. #2
    Join Date
    Feb 2006
    Posts
    3,044
    Mentioned
    4 Post(s)
    Quoted
    21 Post(s)

    Default

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

    ~Home

  3. #3
    Join Date
    Feb 2006
    Location
    Amsterdam
    Posts
    13,691
    Mentioned
    146 Post(s)
    Quoted
    130 Post(s)

    Default

    Very nice...



    The best way to contact me is by email, which you can find on my website: http://wizzup.org
    I also get email notifications of private messages, though.

    Simba (on Twitter | Group on Villavu | Website | Stable/Unstable releases
    Documentation | Source | Simba Bug Tracker on Github and Villavu )


    My (Blog | Website)

  4. #4
    Join Date
    Jul 2015
    Posts
    3
    Mentioned
    0 Post(s)
    Quoted
    1 Post(s)

    Default

    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. #5
    Join Date
    Sep 2006
    Posts
    6,089
    Mentioned
    77 Post(s)
    Quoted
    43 Post(s)

    Default

    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?
    Hup Holland Hup!

  6. #6
    Join Date
    Jul 2015
    Posts
    3
    Mentioned
    0 Post(s)
    Quoted
    1 Post(s)

    Default

    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
    Attached Images Attached Images

  7. #7
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    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 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.
    Last edited by Brandon; 08-01-2015 at 09:13 PM.
    I am Ggzz..
    Hackintosher

  8. #8
    Join Date
    Jul 2015
    Posts
    3
    Mentioned
    0 Post(s)
    Quoted
    1 Post(s)

    Default

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

    Default

    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. #10
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Quote Originally Posted by sn0w8321 View Post
    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.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  11. #11
    Join Date
    Apr 2016
    Posts
    3
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    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. #12
    Join Date
    May 2018
    Posts
    10
    Mentioned
    0 Post(s)
    Quoted
    1 Post(s)

    Default

    Wish I knew how to use this

  13. #13
    Join Date
    Dec 2016
    Location
    Michigan, USA
    Posts
    38
    Mentioned
    0 Post(s)
    Quoted
    8 Post(s)

    Default

    Quote Originally Posted by shiftlid View Post
    Wish I knew how to use this
    same

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

    Default

    Hi Niels - looks interesting but I can't find a download link. Is it still available?

    Thanks, Bob

  15. #15
    Join Date
    Apr 2016
    Posts
    3
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    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

Thread Information

Users Browsing this Thread

There are currently 2 users browsing this thread. (0 members and 2 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •