Advertisement
WarPie90

Untitled

Feb 2nd, 2025 (edited)
783
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 9.70 KB | None | 0 0
  1. unit xprCompiler;
  2. {
  3.   Author: Jarl K. Holta
  4.   License: GNU Lesser GPL (http://www.gnu.org/licenses/lgpl.html)
  5. }
  6. {$I header.inc}
  7. {$hints off}
  8.  
  9. interface
  10.  
  11. uses
  12.   SysUtils, xprTypes, xprBytecode, xprErrors,
  13.   Windows;
  14.  
  15. type
  16.   // static register-stack for performance and simplicty
  17.   // in the future we might find this size to be limiting
  18.   TRegister64k = array [0..$FFFF] of record
  19.     case Byte of
  20.       1: (b: Boolean;);
  21.       2: (f: Double;);
  22.       3: (i: Int64; );
  23.       4: (r: array[0..7] of byte; );
  24.   end;
  25.  
  26.   TStackPointer = array [0..$3FF] of SizeInt;
  27.  
  28.   TNativeCompiler = record
  29.     Bytecode: TByteCode;
  30.  
  31.     FProgram: Pointer;
  32.     FRegisters: TRegister64k;
  33.  
  34.     // stack pointer or a perhaps rather a register pointer
  35.     // disallow inf recursion, limited to 1K atm.
  36.     FSPStack: TStackPointer;
  37.     FSPSize: Int32;
  38.     FSP: ^SizeInt;
  39.  
  40.     function Get_MUL_Code(const Op: TInstruction): TBytes;
  41.     function Get_EQ_Code(const Op: TInstruction): TBytes;
  42.     function Get_LTE_Code(const Op: TInstruction): TBytes;
  43.     function Get_ASGN_Code(const Op: TInstruction): TBytes;
  44.     function Get_LADD_Code(const Op: TInstruction): TBytes;
  45.     function Get_LSUB_Code(const Op: TInstruction): TBytes;
  46.  
  47.     procedure PrepRegisters();
  48.     procedure LoadConstants(C: TConstantList);
  49.     function MakeExecutable(Code: TBytes; Size: Int32): TInstruction;
  50.     function Compile(BC: TByteCode): TByteCode;
  51.   end;
  52.  
  53.  
  54. implementation
  55.  
  56. uses
  57.   Math;
  58.  
  59.  
  60. {$DEFINE COPY_CODE_BINARY :=
  61.   if Op.Args[1].Typ in XprFloatTypes then
  62.   begin
  63.     Result := GetCode(@f_stack_stack, @body)
  64.   end else
  65.   begin
  66.     Result := GetCode(@i_stack_stack, @f_stack_stack)
  67.   end
  68. }
  69.  
  70. {$DEFINE COPY_CODE_ASSIGN :=
  71.   if Op.Args[1].Typ in XprFloatTypes then
  72.   begin
  73.     Result := GetCode(@f_stack_stack, @body)
  74.   end else
  75.   begin
  76.     Result := GetCode(@i_stack_stack, @f_stack_stack)
  77.   end
  78. }
  79.  
  80. function GetCode(start, stop: Pointer): TBytes;
  81. var sz: SizeInt;
  82. begin
  83.   sz := PtrUInt(stop) - PtrUInt(start);
  84.   SetLength(Result, sz);
  85.   Move(start^, Result[0], sz);
  86. end;
  87.  
  88. function TNativeCompiler.Get_MUL_Code(const Op: TInstruction): TBytes;
  89. label i_stack_stack, f_stack_stack, body;
  90. begin
  91.   goto body;
  92.   i_stack_stack: FRegisters[FSP^ + Op.Args[2].Arg].i := FRegisters[FSP^ + Op.Args[0].Arg].i * FRegisters[FSP^ + Op.Args[1].Arg].i;
  93.   f_stack_stack: FRegisters[FSP^ + Op.Args[2].Arg].f := FRegisters[FSP^ + Op.Args[0].Arg].f * FRegisters[FSP^ + Op.Args[1].Arg].f;
  94.   // handle globals where stackptr is 0, not needed for limited test.
  95.   body:
  96.   COPY_CODE_BINARY;
  97. end;
  98.  
  99.  
  100. function TNativeCompiler.Get_EQ_Code(const Op: TInstruction): TBytes;
  101. label i_stack_stack, f_stack_stack, body;
  102. begin
  103.   goto body;
  104.   i_stack_stack: FRegisters[FSP^ + Op.Args[2].Arg].b := FRegisters[FSP^ + Op.Args[0].Arg].i = FRegisters[FSP^ + Op.Args[1].Arg].i;
  105.   f_stack_stack: FRegisters[FSP^ + Op.Args[2].Arg].b := FRegisters[FSP^ + Op.Args[0].Arg].f = FRegisters[FSP^ + Op.Args[1].Arg].f;
  106.   // handle globals where stackptr is 0, not needed for limited test.
  107.   body:
  108.   COPY_CODE_BINARY;
  109. end;
  110.  
  111. function TNativeCompiler.Get_LTE_Code(const Op: TInstruction): TBytes;
  112. label i_stack_stack, f_stack_stack, body;
  113. begin
  114.   goto body;
  115.   i_stack_stack: FRegisters[FSP^ + Op.Args[2].Arg].b := FRegisters[FSP^ + Op.Args[0].Arg].i <= FRegisters[FSP^ + Op.Args[1].Arg].i;
  116.   f_stack_stack: FRegisters[FSP^ + Op.Args[2].Arg].b := FRegisters[FSP^ + Op.Args[0].Arg].f <= FRegisters[FSP^ + Op.Args[1].Arg].f;
  117.   // handle globals where stackptr is 0, not needed for limited test.
  118.   body:
  119.   COPY_CODE_BINARY;
  120. end;
  121.  
  122.  
  123. function TNativeCompiler.Get_ASGN_Code(const Op: TInstruction): TBytes;
  124. label i_stack_stack, f_stack_stack, body;
  125. begin
  126.   goto body;
  127.   i_stack_stack: FRegisters[FSP^ + Op.Args[0].Arg].i := FRegisters[FSP^ + Op.Args[1].Arg].i;
  128.   f_stack_stack: FRegisters[FSP^ + Op.Args[0].Arg].f := FRegisters[FSP^ + Op.Args[1].Arg].f;
  129.   // handle globals where stackptr is 0, not needed for limited test.
  130.   body:
  131.   COPY_CODE_ASSIGN;
  132. end;
  133.  
  134. function TNativeCompiler.Get_LADD_Code(const Op: TInstruction): TBytes;
  135. label i_stack_stack, f_stack_stack, body;
  136. begin
  137.   goto body;
  138.   i_stack_stack: FRegisters[FSP^ + Op.Args[0].Arg].i += FRegisters[FSP^ + Op.Args[1].Arg].i;
  139.   f_stack_stack: FRegisters[FSP^ + Op.Args[0].Arg].f += FRegisters[FSP^ + Op.Args[1].Arg].f;
  140.   // handle globals where stackptr is 0, not needed for limited test.
  141.   body:
  142.   COPY_CODE_ASSIGN;
  143. end;
  144.  
  145. function TNativeCompiler.Get_LSUB_Code(const Op: TInstruction): TBytes;
  146. label i_stack_stack, f_stack_stack, body;
  147. begin
  148.   goto body;
  149.   i_stack_stack: FRegisters[FSP^ + Op.Args[0].Arg].i -= FRegisters[FSP^ + Op.Args[1].Arg].i;
  150.   f_stack_stack: FRegisters[FSP^ + Op.Args[0].Arg].f -= FRegisters[FSP^ + Op.Args[1].Arg].f;
  151.   // handle globals where stackptr is 0, not needed for limited test.
  152.   body:
  153.   COPY_CODE_ASSIGN;
  154. end;
  155.  
  156.  
  157. procedure TNativeCompiler.PrepRegisters();
  158. begin
  159.   FillByte(Self.FRegisters, Length(FRegisters)*SizeOf(Int64), 0);
  160. end;
  161.  
  162. procedure TNativeCompiler.LoadConstants(C: TConstantList);
  163. var i,j,n: Int32;
  164. begin
  165.   for i:=0 to C.High() do
  166.     Self.FRegisters[i].r := C.Data[i].raw;
  167.  
  168.   // offset to a point after consts (and maybe globals in future)!
  169.   n := C.High()+1;
  170.   with Self.Bytecode do
  171.     for i:=0 to Code.High do
  172.        for j:=0 to High(Code.Data[i].Args) do
  173.           if Code.Data[i].Args[j].Pos = EMemPos.mpUnalloc then
  174.             Code.Data[i].Args[j].Arg += n;
  175. end;
  176.  
  177. function TNativeCompiler.MakeExecutable(Code: TBytes; Size: Int32): TInstruction;
  178. var ptr: PBYTE; i: Int32;
  179. begin
  180.   Code[Size] := $c3; // ret signal, may need to handle prologue
  181.  
  182.   ptr := VirtualAlloc(nil, Size+1, $00002000 or $00001000, $40);
  183.   for i:=0 to Size do ptr^ := Code[i];
  184.  
  185.   Result.Code := EByteCode.bcINVOKEX;
  186.   Result.nArgs:= 1;
  187.   Result.Args[0].Arg:=PtrUInt(ptr);
  188.   Result.Args[0].Pos:=EMemPos.mpRaw;
  189.   Result.Args[0].Typ:=EExpressBaseType.xtPointer;
  190. end;
  191.  
  192. function TNativeCompiler.Compile(BC: TByteCode): TByteCode;
  193. type
  194.   TJumpLocation = record From, Goal, NewGoal, NewFrom: Int32; end;
  195. var
  196.   i,j: Int32;
  197.   jumps: array of TJumpLocation;
  198.   jmp: TJumpLocation;
  199.  
  200.  
  201.   compiled, code: TBytes;
  202.   compiledTop: Int32;
  203.  
  204.   instruction: TInstruction;
  205.  
  206.   procedure AddJump(const From, Goal: PtrUInt);
  207.   begin
  208.     SetLength(jumps, Length(jumps)+1);
  209.     jumps[High(jumps)].From := From;
  210.     jumps[High(jumps)].Goal := Goal;
  211.     jumps[High(jumps)].NewGoal := $FFFFFF;
  212.     jumps[High(jumps)].NewFrom := $FFFFFF;
  213.   end;
  214. var
  215.   raw: TBytes;
  216.   isPlatform: Boolean;
  217. begin
  218.   Self.Bytecode := BC;
  219.   Self.Bytecode.Code.Free();
  220.  
  221.   //1. init stack!
  222.   Self.PrepRegisters();
  223.  
  224.   //2. move all constants into the stack:
  225.   Self.LoadConstants(BC.Constants);
  226.  
  227.  
  228.   for i:=0 to bc.Code.High do
  229.   begin
  230.     if bc.Code.data[i].Code = bcRELJMP then AddJump(i, i+bc.Code.data[i].Args[0].Arg);
  231.     if bc.Code.data[i].Code = bcJZ     then AddJump(i, bc.Code.data[i].Args[1].Arg);
  232.     if bc.Code.data[i].Code = bcJNZ    then AddJump(i, bc.Code.data[i].Args[1].Arg);
  233.   end;
  234.  
  235.   SetLength(compiled, 1024);
  236.   compiledTop := -1;
  237.  
  238.   Self.Bytecode.Init();
  239.   for i:=0 to bc.Code.High do
  240.   begin
  241.     isPlatform := False;
  242.  
  243.     // patch location
  244.     for j:=0 to High(jumps) do
  245.     begin
  246.       isPlatform := jumps[j].Goal = i;
  247.       if isPlatform then break;
  248.     end;
  249.  
  250.     // some jumps are actually landing zones, and we can't land inside compiled code!
  251.     // so we break up the compiled code!
  252.     if isPlatform then
  253.     begin
  254.       if compiledTop <> -1 then
  255.       begin
  256.         instruction := Self.MakeExecutable(Compiled, compiledTop+1);
  257.         Self.Bytecode.Code.Add(instruction);
  258.         compiledTop := -1;
  259.       end;
  260.     end;
  261.  
  262.     // guarded opcodes essentially, these will flow interpreted for now
  263.     if (bc.Code.Data[i].Code in [bcJZ, bcJNZ, bcRELJMP, RETURN]) then
  264.     begin
  265.       if compiledTop <> -1 then
  266.       begin
  267.         instruction := Self.MakeExecutable(Compiled, compiledTop+1);
  268.         Self.Bytecode.Code.Add(instruction);
  269.         compiledTop := -1;
  270.       end;
  271.  
  272.       instruction := bc.Code.data[i];
  273.       Self.Bytecode.Code.Add(instruction);
  274.  
  275.       // patch location
  276.       for j:=0 to High(jumps) do
  277.         if jumps[j].From = i then
  278.           jumps[j].NewFrom := Bytecode.Code.High();
  279.  
  280.     end
  281.     else
  282.     begin
  283.  
  284.       case bc.Code.Data[i].Code of
  285.         bcASGN: code := Get_ASGN_Code(bc.Code.data[i]);
  286.         bcLADD: code := Get_LADD_Code(bc.Code.data[i]);
  287.         bcLSUB: code := Get_LSUB_Code(bc.Code.data[i]);
  288.  
  289.         bcLTE:  code := Get_LTE_Code(bc.Code.data[i]);
  290.         bcEQ:   code := Get_EQ_Code (bc.Code.data[i]);
  291.         bcMUL:  code := Get_MUL_Code(bc.Code.data[i]);
  292.       end;
  293.  
  294.       if Length(code) > Length(compiled)-(compiledTop+10) then
  295.         SetLength(compiled, Length(compiled) + Length(code));
  296.       Move(code[0], compiled[compiledTop+1], Length(code));
  297.       compiledTop += Length(code);
  298.       //add code to the final compiled .. compiled.Extend(code)
  299.     end;
  300.  
  301.     for j:=0 to High(jumps) do
  302.       if jumps[j].Goal = i then
  303.         jumps[j].NewGoal := Bytecode.Code.High()+1;
  304.   end;
  305.  
  306.  
  307.   // patch jumps
  308.   for i:=0 to Self.Bytecode.Code.High do
  309.     if (Self.Bytecode.Code.Data[i].Code in [bcJZ, bcJNZ, bcRELJMP]) then
  310.     begin
  311.       for j:=0 to High(jumps) do
  312.       begin
  313.         if jumps[j].NewFrom = i then
  314.         begin
  315.  
  316.           if Self.Bytecode.Code.data[i].Code = bcRELJMP then Self.Bytecode.Code.data[i].Args[0].Arg := jumps[j].NewGoal - i;
  317.           if Self.Bytecode.Code.data[i].Code = bcJZ     then Self.Bytecode.Code.data[i].args[1].Arg := jumps[j].NewGoal;
  318.           if Self.Bytecode.Code.data[i].Code = bcJNZ    then Self.Bytecode.Code.data[i].args[1].Arg := jumps[j].NewGoal;
  319.         end;
  320.       end;
  321.     end;
  322. end;
  323.  
  324.  
  325.  
  326. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement