Skip to content

Commit 6b3c092

Browse files
committed
feat: Add native x64 code generation with in-memory execution
- Complete TinyPascal → native x64 machine code compilation - Arithmetic operations (ADD, SUB, MUL, DIV) working - Variable management with Windows x64 calling convention - Runtime function calls (PrintLn, IntToStr) integrated - In-memory execution of generated code - Test case: "10 + 5" compiles to 97 bytes, executes natively, prints "15" - Added debug output integration with TinyPascal.Common
1 parent 9c401e7 commit 6b3c092

File tree

7 files changed

+1299
-4
lines changed

7 files changed

+1299
-4
lines changed

examples/testbed/Testbed.dpr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ uses
5757
TinyPascal.VM in '..\..\src\TinyPascal.VM.pas',
5858
TinyPascal.BytecodeGen in '..\..\src\TinyPascal.BytecodeGen.pas',
5959
TinyPascal.Value in '..\..\src\TinyPascal.Value.pas',
60-
TinyPascal.Runtime in '..\..\src\TinyPascal.Runtime.pas';
60+
TinyPascal.Runtime in '..\..\src\TinyPascal.Runtime.pas',
61+
TinyPascal.X64Gen in '..\..\src\TinyPascal.X64Gen.pas',
62+
TinyPascal.Common in '..\..\src\TinyPascal.Common.pas';
6163

6264
begin
6365
ReportMemoryLeaksOnShutdown := True;

examples/testbed/Testbed.dproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<ProjectVersion>20.3</ProjectVersion>
55
<FrameworkType>None</FrameworkType>
66
<Base>True</Base>
7-
<Config Condition="'$(Config)'==''">Debug</Config>
7+
<Config Condition="'$(Config)'==''">Release</Config>
88
<Platform Condition="'$(Platform)'==''">Win64</Platform>
99
<ProjectName Condition="'$(ProjectName)'==''">Testbed</ProjectName>
1010
<TargetedPlatforms>2</TargetedPlatforms>
@@ -126,6 +126,8 @@
126126
<DCCReference Include="..\..\src\TinyPascal.BytecodeGen.pas"/>
127127
<DCCReference Include="..\..\src\TinyPascal.Value.pas"/>
128128
<DCCReference Include="..\..\src\TinyPascal.Runtime.pas"/>
129+
<DCCReference Include="..\..\src\TinyPascal.X64Gen.pas"/>
130+
<DCCReference Include="..\..\src\TinyPascal.Common.pas"/>
129131
<BuildConfiguration Include="Base">
130132
<Key>Base</Key>
131133
</BuildConfiguration>

examples/testbed/UTestbed.pas

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ implementation
5656
procedure RunTestbed();
5757
begin
5858
RunTPTests(False);
59+
Test_X64_ProofOfConcept();
5960
Pause();
6061
end;
6162

src/TinyPascal.Common.pas

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{==============================================================================
2+
_____ _ ___ _ ™
3+
|_ _(_)_ _ _ _| _ \__ _ ___ __ __ _| |
4+
| | | | ' \ || | _/ _` (_-</ _/ _` | |
5+
|_| |_|_||_\_, |_| \__,_/__/\__\__,_|_|
6+
|__/ Pascal in your Pocket!
7+
8+
Copyright © 2025-present tinyBigGAMES™ LLC
9+
All Rights Reserved.
10+
11+
https://github.com/tinyBigGAMES/TinyPascal
12+
13+
BSD 3-Clause License
14+
15+
Copyright (c) 2025-present, tinyBigGAMES LLC
16+
17+
Redistribution and use in source and binary forms, with or without
18+
modification, are permitted provided that the following conditions are met:
19+
20+
1. Redistributions of source code must retain the above copyright notice, this
21+
list of conditions and the following disclaimer.
22+
23+
2. Redistributions in binary form must reproduce the above copyright notice,
24+
this list of conditions and the following disclaimer in the documentation
25+
and/or other materials provided with the distribution.
26+
27+
3. Neither the name of the copyright holder nor the names of its
28+
contributors may be used to endorse or promote products derived from
29+
this software without specific prior written permission.
30+
31+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
32+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
35+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
37+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
39+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41+
==============================================================================}
42+
43+
unit TinyPascal.Common;
44+
45+
{$I TinyPascal.Defines.inc}
46+
47+
interface
48+
49+
uses
50+
System.SysUtils;
51+
52+
// Debug output control - no global variables exposed
53+
procedure SetDebugOutput(const AEnabled: Boolean);
54+
function GetDebugOutput(): Boolean;
55+
56+
// Debug output routines - automatically check enabled state
57+
procedure DebugWriteLn(const AMessage: string); overload;
58+
procedure DebugWrite(const AMessage: string);
59+
procedure DebugWriteLn(const AFormat: string; const AArgs: array of const); overload;
60+
61+
implementation
62+
63+
var
64+
LDebugEnabled: Boolean = True; // Private to unit - default enabled for development
65+
66+
{ Debug Control Functions }
67+
68+
procedure SetDebugOutput(const AEnabled: Boolean);
69+
begin
70+
LDebugEnabled := AEnabled;
71+
end;
72+
73+
function GetDebugOutput(): Boolean;
74+
begin
75+
Result := LDebugEnabled;
76+
end;
77+
78+
{ Debug Output Functions }
79+
80+
procedure DebugWriteLn(const AMessage: string);
81+
begin
82+
if not LDebugEnabled then Exit; // Early exit for performance
83+
WriteLn(AMessage);
84+
end;
85+
86+
procedure DebugWrite(const AMessage: string);
87+
begin
88+
if not LDebugEnabled then Exit; // Early exit for performance
89+
Write(AMessage);
90+
end;
91+
92+
procedure DebugWriteLn(const AFormat: string; const AArgs: array of const);
93+
begin
94+
if not LDebugEnabled then Exit; // Early exit for performance
95+
WriteLn(Format(AFormat, AArgs));
96+
end;
97+
98+
end.

src/TinyPascal.Runtime.pas

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ interface
6060
rtError // General runtime error
6161
);
6262

63-
// Runtime functions - simple approach
63+
// VM Runtime functions - simple approach
6464
function Runtime_Print(const AValue: TValue; const AStringPool: TStringList): TRuntimeResult;
6565
function Runtime_PrintLn(const AValue: TValue; const AStringPool: TStringList): TRuntimeResult;
6666
function Runtime_PrintLn_Empty(): TRuntimeResult;
@@ -71,8 +71,19 @@ function Runtime_FloatToStr(const AValue: TValue; const AStringPool: TStringList
7171
function Runtime_StrToFloat(const AValue: TValue; const AStringPool: TStringList): TValue;
7272
function Runtime_ResultToString(const AResult: TRuntimeResult): string;
7373

74+
// Native X64-callable runtime functions
75+
procedure Runtime_Print_X64(const AValue: Int64); cdecl;
76+
procedure Runtime_PrintLn_X64(const AValue: Int64); cdecl;
77+
procedure Runtime_PrintLn_Empty_X64(); cdecl;
78+
function Runtime_IntToStr_X64(const AValue: Int64): PAnsiChar; cdecl;
79+
function Runtime_StrToInt_X64(const AString: PAnsiChar): Int64; cdecl;
80+
procedure Runtime_PrintString_X64(const AString: PAnsiChar); cdecl;
81+
procedure Runtime_PrintLnString_X64(const AString: PAnsiChar); cdecl;
82+
7483
implementation
7584

85+
{ VM Runtime Functions - Original Implementation }
86+
7687
function Runtime_Print(const AValue: TValue; const AStringPool: TStringList): TRuntimeResult;
7788
var
7889
LText: string;
@@ -199,4 +210,86 @@ function Runtime_ResultToString(const AResult: TRuntimeResult): string;
199210
end;
200211
end;
201212

213+
{ Native X64 Runtime Functions }
214+
215+
procedure Runtime_Print_X64(const AValue: Int64); cdecl;
216+
begin
217+
try
218+
System.Write(IntToStr(AValue));
219+
except
220+
// Ignore errors in native calls
221+
end;
222+
end;
223+
224+
procedure Runtime_PrintLn_X64(const AValue: Int64); cdecl;
225+
begin
226+
try
227+
System.WriteLn(IntToStr(AValue));
228+
except
229+
// Ignore errors in native calls
230+
end;
231+
end;
232+
233+
procedure Runtime_PrintLn_Empty_X64(); cdecl;
234+
begin
235+
try
236+
System.WriteLn;
237+
except
238+
// Ignore errors in native calls
239+
end;
240+
end;
241+
242+
procedure Runtime_PrintString_X64(const AString: PAnsiChar); cdecl;
243+
begin
244+
try
245+
if Assigned(AString) then
246+
System.Write(string(AnsiString(AString)));
247+
except
248+
// Ignore errors in native calls
249+
end;
250+
end;
251+
252+
procedure Runtime_PrintLnString_X64(const AString: PAnsiChar); cdecl;
253+
begin
254+
try
255+
if Assigned(AString) then
256+
System.WriteLn(string(AnsiString(AString)));
257+
except
258+
// Ignore errors in native calls
259+
end;
260+
end;
261+
262+
var
263+
LTempStringBuffer: array[0..255] of AnsiChar;
264+
265+
function Runtime_IntToStr_X64(const AValue: Int64): PAnsiChar; cdecl;
266+
var
267+
LStr: AnsiString;
268+
begin
269+
try
270+
LStr := AnsiString(IntToStr(AValue));
271+
if Length(LStr) < Length(LTempStringBuffer) then
272+
begin
273+
StrPCopy(LTempStringBuffer, LStr);
274+
Result := @LTempStringBuffer[0];
275+
end
276+
else
277+
Result := nil;
278+
except
279+
Result := nil;
280+
end;
281+
end;
282+
283+
function Runtime_StrToInt_X64(const AString: PAnsiChar): Int64; cdecl;
284+
begin
285+
try
286+
if Assigned(AString) then
287+
Result := StrToInt64(string(AnsiString(AString)))
288+
else
289+
Result := 0;
290+
except
291+
Result := 0;
292+
end;
293+
end;
294+
202295
end.

src/TinyPascal.Tests.pas

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ interface
4949
procedure RunTPTests(const APause: Boolean=True);
5050
procedure Pause(const APause: Boolean=True);
5151

52+
procedure Test_X64_ProofOfConcept();
53+
5254
implementation
5355

5456
uses
@@ -61,7 +63,8 @@ implementation
6163
TinyPascal.AST,
6264
TinyPascal.Value,
6365
TinyPascal.Bytecode,
64-
TinyPascal.VM;
66+
TinyPascal.VM,
67+
TinyPascal.X64Gen;
6568

6669
procedure Pause(const APause: Boolean);
6770
begin
@@ -323,6 +326,119 @@ procedure Test15_AdvancedIfElseChain();
323326
ExecuteProgram(LSource, 'Test 15');
324327
end;
325328

329+
procedure Test_X64_ProofOfConcept();
330+
var
331+
LParser: TParser;
332+
LProgram: TProgramNode;
333+
LBytecodeGen: TBytecodeGenerator;
334+
LGenResult: TBytecodeGenResult;
335+
LX64Compiler: TX64Compiler;
336+
LX64Result: TX64CompileResult;
337+
LSource: UTF8String;
338+
begin
339+
WriteLn('=== X64 PROOF OF CONCEPT TEST ===');
340+
WriteLn('Testing native compilation with REAL runtime calls...');
341+
WriteLn;
342+
343+
// Test with PrintLn to see actual output!
344+
LSource := 'program X64RuntimeTest; var result: Int; begin result := 10 + 5; PrintLn(IntToStr(result)); end.';
345+
346+
WriteLn('Source: ', string(LSource));
347+
WriteLn('Expected: Should print "15" from native x64 code!');
348+
WriteLn;
349+
350+
try
351+
// Parse (same as always)
352+
LParser := TParser.CreateFromSource(LSource);
353+
try
354+
LProgram := LParser.Parse();
355+
WriteLn('✓ Parsed successfully');
356+
finally
357+
LParser.Free();
358+
end;
359+
360+
// Generate bytecode (same as always)
361+
LBytecodeGen := TBytecodeGenerator.Create();
362+
try
363+
LGenResult := LBytecodeGen.Generate(LProgram);
364+
try
365+
if LGenResult.Success then
366+
begin
367+
WriteLn('✓ Generated bytecode successfully');
368+
WriteLn(' Instructions: ', LGenResult.Program_.GetInstructionCount());
369+
WriteLn(' Constants: ', LGenResult.Program_.GetConstantCount());
370+
WriteLn(' Variables: ', LGenResult.Program_.GetVariableCount());
371+
WriteLn;
372+
373+
// X64 compilation with REAL function calls!
374+
LX64Compiler := TX64Compiler.Create();
375+
try
376+
WriteLn('--- ATTEMPTING X64 COMPILATION WITH RUNTIME CALLS ---');
377+
LX64Result := LX64Compiler.CompileProgram(LGenResult.Program_);
378+
try
379+
if LX64Result.Success then
380+
begin
381+
WriteLn('🎉 X64 RUNTIME COMPILATION SUCCESS!');
382+
WriteLn('Generated ', LX64Result.GeneratedCodeSize, ' bytes of x64 machine code');
383+
WriteLn;
384+
385+
if LX64Result.CanExecuteInMemory then
386+
begin
387+
WriteLn('🚀 ATTEMPTING IN-MEMORY EXECUTION...');
388+
WriteLn('This should actually print "15" from native x64 code!');
389+
WriteLn;
390+
391+
if LX64Compiler.ExecuteInMemory() then
392+
begin
393+
WriteLn;
394+
WriteLn('🔥🔥🔥 COMPLETE SUCCESS! 🔥🔥🔥');
395+
WriteLn('You just executed compiled TinyPascal code natively!');
396+
end
397+
else
398+
begin
399+
WriteLn('❌ In-memory execution failed: ', LX64Compiler.ErrorMessage);
400+
end;
401+
end
402+
else
403+
begin
404+
WriteLn('Code compiled but not ready for in-memory execution');
405+
end;
406+
end
407+
else
408+
begin
409+
WriteLn('❌ X64 compilation failed: ', LX64Result.ErrorMessage);
410+
end;
411+
finally
412+
LX64Result.Free();
413+
end;
414+
finally
415+
LX64Compiler.Free();
416+
end;
417+
418+
end
419+
else
420+
begin
421+
WriteLn('❌ Bytecode generation failed: ', LGenResult.ErrorMessage);
422+
end;
423+
finally
424+
LGenResult.Free();
425+
end;
426+
finally
427+
LBytecodeGen.Free();
428+
LProgram.Free();
429+
end;
430+
431+
except
432+
on E: Exception do
433+
begin
434+
WriteLn('❌ Exception: ', E.Message);
435+
end;
436+
end;
437+
438+
WriteLn;
439+
WriteLn('=== END X64 RUNTIME TEST ===');
440+
end;
441+
326442
procedure RunTPTests(const APause: Boolean);
327443
begin
328444
WriteLn('TinyPascal - COMPLETE LANGUAGE TEST SUITE');

0 commit comments

Comments
 (0)