Sign in Join
Wiki copytemporary

FPC JVM/Internals

English · Русский

Compiler

Compile-time code generation

The implementation of several language features on the JVM target require the compiler to generate wrappers of all sorts:

While it is possible to generate all the internal definitions/nodes to implement all this functionality, plain Pascal code is much quicker and easier to write and maintain. For this reason the new symcreat unit supports parsing Pascal code injected into the source code stream at compile time. There is currently functionality to parse both method declarations and implementations, and also for typed const declarations.

Once a method declaration has been parsed or otherwise generated, you can set the corresponding procdef's synthetickind field to any of the tsk_* constants defined in the symdef unit to indicate what kind of code should be generated for it. The symcreat.add_synthetic_method_implementations_for_struct() routine handles calling the appropriate helper routine to generate the actual Pascal code, parse it and generate the machine/byte code.

Caveats:

Typed constants

There are no generic data sections in Java class files. As a result, data initialization must be performed using explicit assignments in the class initialization (~unit initialization).

Most of the functionality of the ptconst unit has been split off into ngtcon. Generating the typed constant initialization is now handled by a ttypedconstbuilder class. This abstract class has two child classes. tasmlisttypedconstbuilder handles the typed constant initialization for native targets. tnodetreetypedconstbuilder handles the typed constant generation for the JVM target and possible other future targets where typed constants have to be initialized via explicit assignments.

Both classes can still be further overridden by target-specific classes. Depending on whether or not the current target is in the systems.systems_typed_constants_node_init set, the asmlist or nodetree class is instantiated.

Nested routines

The JVM has no stack or frame pointer that can be passed around, so another way is required to support accessing local variables or parameters from an outer routine in an inner routine. This has been solved by moving such parameters and local variables in a local record in the outer routine. This record is called the parentfpstruct.

A pointer to this parentfpstruct is passed as hidden parameter to any child routine, so they can access the variables and parameters that way. In case a nested routine calls a more deeply nested nested routine, the parentfpstruct parameter itself will also be migrated to the new parentfpstruct, so that via daisy chaining all parentfpstructs are reachable from everywhere.

If a target requires that nested routines are handled this way rather than via the default framepointer technique, all it has to do is use the ncgnstld (instead of ncgld) and ncgnstmm (instead of ncgmem) units, and/or derive from the classes defined in those units rather than those defined in their generic equivalents.

Code generator

Overview

There is now a high level code generator class, thlcgobj (hlcgobj.pas). The difference with the existing code generator is that all size descriptions are tdefs rather tcgsizes.

The low level and high level code generators can be seamlessly mixed for targets that have a complete implementation of the low level one (all but the JVM target for now). There is a tcghl2ll (hlcg2ll.pas) class that translates all high level hlcgobj calls into low level tcg calls, which is used for all non-JVM targets.

Apart from tcgsize -> tdef changes, a number of thlcgobj methods also received extra size parameters in case tlocations are involved. The reason is that a tlocation has a tcgsize member, and replacing it with a tdef is not possible without messing up the layer separations in the compiler (a different kind of high level location would be required).

Several ncg*.pas units have been wholly or partly converted to use the new high level code generator infrastructure, so they can be used by both the JVM target and the existing targets.

ncgutil

A number of routines from ncgutil.pas has been moved to thlcgobj.pas so they can be overridden by different targets.

ngenutil

A new unit ngenutil has been added that contains node-related routines from other units (mainly from nutils and pmodule) that can now be overridden with target-specific versions (e.g., jvm/njvmutil)

Limitations

The high level code generator currently does not support the following features due to the inability of expressing them using high level types (tdefs):

Since the JVM target does not require these features and since the other targets use the existing low level code generator for them, this can be solved in the future.

RTL

General

Much of the existing RTL code is not usable for the JVM target, because it makes heavy use of pointers to arbitrary data, move and fillchar, none of which are available on the JVM platform.

Rather than immediately putting the existing RTL files full of ifdefs, several files from rtl/inc have been duplicated into the rtl/java directory and modified there. As development progressed, some became more like the original ones and in one case (generic.inc) an original file is even already used.

Some details:

Debugging

Because the plain Java class format does not support line number information for include files, it is currently not possible to generate line number information for any of the code in the RTL include files. If you really need to debug some code in an include file, the only workaround right now is to temporarily copy its contents into java/system.pp


From the Free Pascal wiki, page "FPC JVM/Internals", as it stood on 2013-07-11 after a change by Ggeldenhuys, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 1, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.