JIT Compile asm in c backend

Everything else that doesn't fall into one of the other PB categories.
User avatar
idle
Always Here
Always Here
Posts: 5881
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

JIT Compile asm in c backend

Post by idle »

I was thinking about how to JIT asm in the c backend and came up with this and cool it works windows x64 c backend

Windows x64 c backend.

Code: Select all



Procedure JIT(asm.s)    
  Protected oldProtection;
  Protected fn,*asm 
  Protected fasmpath.s = #PB_Compiler_Home + "Compilers" + #PS$ + "FAsm.exe" 
  Inputfile.s = GetTemporaryDirectory() + "PBJit.txt"  
  outputfile.s = GetTemporaryDirectory() + "PBJit.bin"  
  fn = CreateFile(-1,outputfile) 
  CloseFile(fn) 
  fn = CreateFile(-1,Inputfile) 
  If fn 
    *asm= UTF8(asm) 
    WriteData(fn,*asm,MemorySize(*asm)) 
    CloseFile(fn) 
    OpenConsole()  
    fasm =  RunProgram(fasmpath,Inputfile,"", #PB_Program_Open | #PB_Program_Read)
    If fasm 
      While ProgramRunning(fasm)
        If AvailableProgramOutput(fasm) 
          PrintN(ReadProgramString(fasm))
        EndIf 
      Wend    
    EndIf    
    FreeMemory(*asm) 
    
    If FileSize(outputfile) 
      fn = OpenFile(-1,outputfile)    
      len = Lof(fn) 
      *asm = AllocateMemory(Len)
      ReadData(fn,*asm,len)
      CloseFile(fn) 
      
      Protected *buffer = VirtualAlloc_(#Null, len, #MEM_COMMIT, #PAGE_READWRITE);
      CopyMemory(*asm,*buffer,len)
      FreeMemory(*asm) ;
      VirtualProtect_(*buffer, len, #PAGE_EXECUTE_READ, @oldProtection)          ;
      ProcedureReturn *buffer 
      
    EndIf 
  EndIf   
      
 EndProcedure 
   
 Prototype FastHash64(*Buffer, Len, Seed.q=0)
 Global *FastHash64.FastHash64
 
 Procedure JiTFastHash64()
         
   ; FastHash64 algorithm by Zilong Tan ported by wilbert
   Protected asm.s 
   asm + "use64 " + #LF$
   asm + "MOV    qword [rsp+8],rcx"+ #LF$
   asm + "MOV    qword [rsp+16],rdx"+ #LF$
   asm + "MOV    qword [rsp+24],r8"+ #LF$
   asm + "PS20=48"+ #LF$
   asm + "SUB    rsp,40"+ #LF$
   
   asm + "p.v_Seed equ rsp+PS20+16" + #LF$
   asm + "p.v_Len equ rsp+PS20+8"+ #LF$
   asm + "p.p_Buffer equ rsp+PS20+0"+ #LF$
      
   asm + "mov r10, 0x2127599bf4325c37" + #LF$ 
   asm + "mov r11, 0x880355f21e6d1965" + #LF$ 
   asm + "mov rdx, [p.p_Buffer]" + #LF$ 
   asm + "mov rcx, [p.v_Len]" + #LF$ 
   asm + "mov rax, rcx" + #LF$       ; h = seed ^ (len * m);
   asm + "imul rax, r11" + #LF$ 
   asm + "xor rax, [p.v_Seed]" + #LF$ 
   asm + "sub rcx, 8" + #LF$ 
   asm + "jc .l1" + #LF$ 
   ; 8 byte loop  
   asm + ".l0: " + #LF$ 
   asm + "mov r8, [rdx]" + #LF$         ; v = *pos++;
   asm + "add rdx, 8" + #LF$ 
   ; -- mix(v) start --
   asm + "mov r9, r8" + #LF$ 
   asm + "shr r9, 23" + #LF$ 
   asm + "xor r8, r9" + #LF$ 
   asm + "imul r8, r10" + #LF$ 
   asm + "mov r9, r8" + #LF$ 
   asm + "shr r9, 47" + #LF$ 
   asm + "xor r8, r9" + #LF$ 
   ; -- mix end --
   asm + "xor rax, r8" + #LF$           ; h ^= mix(v);
   asm + "imul rax, r11" + #LF$         ; h *= m;
   asm + "sub rcx, 8" + #LF$ 
   asm + "jnc .l0" + #LF$ 
   ; remaining bytes
   asm + ".l1: " + #LF$ 
   asm + "add rcx, 8" + #LF$ 
   asm + "jz .l5" + #LF$ 
   asm + "xor r8, r8" + #LF$ 
   asm + "test rcx, 4" + #LF$ 
   asm + "jz .l2" + #LF$ 
   ; get 4 bytes
   asm + "mov r8d, [rdx]" + #LF$ 
   asm + "add rdx, 4" + #LF$ 
   asm + "ror r8, 32" + #LF$ 
   asm + ".l2: " + #LF$ 
   asm + "test rcx, 2" + #LF$ 
   asm + "jz .l3" + #LF$ 
   ; get 2 bytes
   asm + "movzx r9d, word [rdx]" + #LF$ 
   asm + "add rdx, 2" + #LF$ 
   asm + "xor r8, r9" + #LF$ 
   asm + "ror r8, 16" + #LF$ 
   asm + ".l3: " + #LF$ 
   asm + "test rcx, 1" + #LF$ 
   asm + "jz .l4" + #LF$ 
   ; get 1 byte
   asm + "movzx r9d, byte [rdx]" + #LF$ 
   asm + "xor r8, r9" + #LF$ 
   asm + "ror r8, 8" + #LF$ 
   asm + ".l4: ";  + #LF$ 
   asm + "and rcx, 7" + #LF$ 
   asm + "shl rcx, 3" + #LF$ 
   asm + "rol r8, cl" + #LF$ 
   ; -- mix(v) start --
   asm + "mov r9, r8" + #LF$ 
   asm + "shr r9, 23" + #LF$ 
   asm + "xor r8, r9" + #LF$ 
   asm + "imul r8, r10" + #LF$ 
   asm + "mov r9, r8" + #LF$ 
   asm + "shr r9, 47" + #LF$ 
   asm + "xor r8, r9" + #LF$ 
   ; -- mix end --
   asm + "xor rax, r8" + #LF$           ; h ^= mix(v);
   asm + "imul rax, r11" + #LF$         ; h *= m;
   ; -- mix(h) start --
   asm + ".l5: " + #LF$ 
   asm + "mov r9, rax" + #LF$ 
   asm + "shr r9, 23" + #LF$ 
   asm + "xor rax, r9" + #LF$ 
   asm + "imul rax, r10" + #LF$ 
   asm + "mov r9, rax" + #LF$ 
   asm + "shr r9, 47" + #LF$ 
   asm + "xor rax, r9" + #LF$ 
   asm + "add rsp,40"+ #LF$ 
   asm + "ret" + #LF$  
   
   ProcedureReturn JIT(asm)   
   
 EndProcedure
 
  Procedure.q FastHash64C(*buf,len,Seed.q=0)
    Protected result.q  
    ;FastHash64 algorithm by Zilong Tan
    !typedef unsigned long long uint64_t; 
    
    !#define mix(h) ({				      	  \
    !			(h) ^= (h) >> 23;		          \
    !			(h) *= 0x2127599bf4325c37ULL;	\
    !			(h) ^= (h) >> 47; })
    !
    
    !	const uint64_t m = 0x880355f21e6d1965ULL;
    !	const uint64_t *pos = (const uint64_t *)p_buf;
    !	const uint64_t *end = pos + (v_len / 8);
    !	const unsigned char *pos2;
    !	uint64_t h = v_seed ^ (v_len * m);
    !	uint64_t v;
    ! uint64_t result; 
    
    !	while (pos != end) {
    !		v  = *pos++;
    !		h ^= mix(v);
    !		h *= m;
    !	}
    
    !	pos2 = (const unsigned char*)pos;
    !	v = 0;
    
    !	switch (v_len & 7) {
    !	case 7: v ^= (uint64_t)pos2[6] << 48;
    !	case 6: v ^= (uint64_t)pos2[5] << 40;
    !	case 5: v ^= (uint64_t)pos2[4] << 32;
    !	case 4: v ^= (uint64_t)pos2[3] << 24;
    !	case 3: v ^= (uint64_t)pos2[2] << 16;
    !	case 2: v ^= (uint64_t)pos2[1] << 8;
    !	case 1: v ^= (uint64_t)pos2[0];
    !		h ^= mix(v);
    !		h *= m;
    !	}
    !
    !	v_result = mix(h);
    
    ProcedureReturn result 
  EndProcedure  
 
 Global str.s = "Hello JIT function" 
 
 *FastHash64 = JiTFastHash64()
 If *FastHash64 
   Debug *FastHash64(@str,StringByteLength(str)) 
 EndIf 
 
 Debug FastHash64C(@str,StringByteLength(str)) 
 





While this isn't really particularly useful as it is, it wouldn't be to hard to turn it into a compiler tool to embed the binary into a datasection and execute it from the virtualmemory and the same mechanism can be used on linux.
https://burnttoys.blogspot.com/2011/04/ ... ry-on.html
User avatar
Skipper
User
User
Posts: 46
Joined: Thu Dec 19, 2024 1:26 pm
Location: NW-Europe

Re: JIT Compile asm in c backend

Post by Skipper »

Idle, I really like how you think outside the box. Creative idea, great code to study! :D
User avatar
idle
Always Here
Always Here
Posts: 5881
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: JIT Compile asm in c backend

Post by idle »

Skipper wrote: Fri Feb 07, 2025 9:16 am Idle, I really like how you think outside the box. Creative idea, great code to study! :D
turning it into a compiler tool that runs when you compile
parse the source strip into c and asm procedures
call pbcompiler with /ASM flag
extract the generates asm from the assembly, merge any used libs in main source
compile procedure with fasm
emit the binary to datasection in the main source
emit the function prototype and loader
and then compile the result with pbcompilerC

It would take a couple of days to do I expect and scratch an itch. as long as the ASM is in a procedure it would be ok
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: JIT Compile asm in c backend

Post by wilbert »

idle wrote: Fri Feb 07, 2025 4:28 am I was thinking about how to JIT asm in the c backend and came up with this and cool it works windows x64 c backend
That's nice :D
I was surprised I didn't get a warning about executing something from memory when I tried your code. :shock:
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
skywalk
Addict
Addict
Posts: 4214
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: JIT Compile asm in c backend

Post by skywalk »

Whoa! Is this a path to edit and continue within PB IDE?
Hot code reload 8)
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
idle
Always Here
Always Here
Posts: 5881
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: JIT Compile asm in c backend

Post by idle »

skywalk wrote: Sun Feb 09, 2025 5:02 pm Whoa! Is this a path to edit and continue within PB IDE?
Hot code reload 8)
I actually want it for my database engine and yes it opens up interesting options.
User avatar
idle
Always Here
Always Here
Posts: 5881
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: JIT Compile asm in c backend

Post by idle »

wilbert wrote: Sun Feb 09, 2025 3:11 pm
idle wrote: Fri Feb 07, 2025 4:28 am I was thinking about how to JIT asm in the c backend and came up with this and cool it works windows x64 c backend
That's nice :D
I was surprised I didn't get a warning about executing something from memory when I tried your code. :shock:
It would probably increase av alerts but it's totally valid, it's how jits work.
User avatar
idle
Always Here
Always Here
Posts: 5881
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: JIT Compile asm in c backend

Post by idle »

skywalk wrote: Sun Feb 09, 2025 5:02 pm Whoa! Is this a path to edit and continue within PB IDE?
Hot code reload 8)
Hot reload would be a very cool feature when a bug is encountered the debugger halts with error, you correct and hit continue and it reruns the procedure, I could have used that the other day where I was getting a bug 3/4 the way of processing files and it was taking an eternity to run with the debugger.
User avatar
skywalk
Addict
Addict
Posts: 4214
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: JIT Compile asm in c backend

Post by skywalk »

Hate to say it, but I used that feature debugging large VB6 apps. Changing a simple state variable, modify logic of an if..endif, and not having to stop and recompile was a big time saver.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
idle
Always Here
Always Here
Posts: 5881
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: JIT Compile asm in c backend

Post by idle »

skywalk wrote: Tue Feb 11, 2025 12:55 am Hate to say it, but I used that feature debugging large VB6 apps. Changing a simple state variable, modify logic of an if..endif, and not having to stop and recompile was a big time saver.
I'd forgotten that.
Post Reply