New FrontEnd
This page gathers all the information about the new front-end. See also #4138.
Debugging
Whether you use MDT (make sure you keep it updated) or OMEdit you need to compile the source code with CFLAGS=-g to be able to debug the compiler. Delete OMCompiler/Compiler/boot/build and then rebuild with CFLAGS=-g
Implementation details
The new frontend is divided into multiple (mostly) separate phases. The main entry point of the frontend is NFInst.instClassInProgram, which consists of four phases:
- Lookup the class to instantiate (lookup might do some limited instantiation).
- Instantiate the class.
- Type the class.
- Flatten the class.
So simply checking how far the instantiation gets in instClassInProgram will help narrow issues down considerably. We do of course want proper error messages, so feel free to add error messages, internal errors or just "IMPLEMENT ME" printouts as appropriate.
The instantiation itself is divided into three recursive phases:
- partialInstClass (creates a scope for the class, and separates out components etc.)
- expandClass (looks up and expands extends clauses)
- instClass (instantiates the components)
A class only needs to be expanded for lookup to be possible, so the lookup will expand classes if necessary. There are two helper functions for this, instantiate which does all three phases and expand which only does the first two.
Something important to know about the instantiation is that InstNodes are mutable, i.e. the class or component in them are stored in an array of length 1. Without mutable nodes it would be very difficult to maintain the trees. It also means that one has to be a bit careful with how the nodes are handled.
One example of this is that expanded classes are generic, i.e. they do not have modifiers and such applied. Whenever a class is expanded, by the instantiation or the lookup, it will therefore be updated in the class tree and reused. Similarly, whenever a class is fully instantiated, e.g. when instantiating a component, it will not be updated in the class tree since a fully instantiated class is unique for each component. It will instead be cloned, fully instantiated and then stored in the component whose type it is.
Also, the class tree and the instance tree are not actually separate trees, but rather one combined tree made up of InstNodes (e.g. each component knows its parent component, each components type knows its parent class, and so on).
Implementation directions
Core Features
The core features: Instantiation, Extends, Lookup, Modifiers, Redeclare, Imports are handled by Per and some of their details is explained above. If there are more things to discuss we can put them here.
Support for class extends needs to be implemented. Adrian has some work in progress code to be adapted and pushed in. See https://github.com/OpenModelica/OMCompiler/pull/1423.
Conditional Components
This needs constant evaluation of structural parameters (see below) to work to evaluate the condition. The components can stay in place and they can be filtered out together with the connects they are involved in.
Inner/Outer
The current front-end is handling the inner/outer via lookup and that poses problems with prefixing and handling of hierarchical inner outer. Bernhard has suggested to define alias equations for the inner outer pairs at the appropriate level which will also maintain a bit of the hierarchical structure to be analysed by the back-end.
Functions
Instantiation and typing of functions is partially implemented. We should extend the NFExpression.CALL to include a link to a unique inst node of the function that is to be called. Positional arguments and named arguments are supported. Vectorization of functions needs to be implemented. Prefixing is not yet handled correctly. Support for reductions needs to be implemented. Some way of storing functions during the instantiation is needed so that they end up in the DAE (we can probably use the component tree directly for this). The back-end needs the function tree to partially evaluate functions. Special care needs to be taken to properly initialize the array sizes in functions (topological sorting on dependencies is needed).
Records
The instantiation will instantiate the records as normal classes. Support for record constructors needs to be implemented, and other record things. We should (maybe) add a new expression type NFExpression.RECORD_CONSTRUCTOR which should (as function calls) include a link to a unique inst node of the records that is to be called/constructed. Special care needs to be taken to properly initialize the array sizes in records (topological sorting on dependencies is needed).
Builtin
As much as possible support for builtin functions should be implemented via ModelicaBuiltin.mo file.
Typing
Typing is partially implemented, but it needs to be expanded to handle all kinds of expressions and so on. Care is needed to handle bindings so that cycles can be detected during the evaluation of structural parameters such as dimensions. The binding have an isProcessing flag that can be set to detect cycles. On error all the components / bindings can be queried for this flag to produce at least the components / bindings involved in the cycles.
Type Checking
NFTypeCheck handles most of the type checking. NFFunction and NFRecord handle the type checking for functions respective records. Type checking is currently called from typing but it could be performed as a separate phase (remains to be seen if is possible)
Connection Handling
Handling of connect equations will be performed on the DAE and the instance tree can be use for additional information. Care is needed to handle in a scalable way:
- connection sets
- overconstrained connections (equalityConstraint type) and breakable branches
- expandable connectors
- inner/outer in the connection sets
Operator Overloading
The support for operator overloading from the current front-end (which was reimplemented by Martin for high scalability) could be partially reused for the new front-end.
Constant Evaluation
Constant evaluation of structural parameters (minimal) or any other constant/parameter bindings (not by default) should be included. Care should be taken to NOT evaluate anything that is not needed so that optimization of certain parameters can be done in the runtime. A new type of expression for the component reference that contains a link to the instantiated component can be use to do evaluation of bindings.
Function Evaluation
This works in connection to Constant Evaluation. To evaluate structural parameters one needs to (sometimes) evaluate functions. The current front-end is using an environment to interpret functions. This might not be needed for the new front-end as constant evaluation and substitution could be used instead.
Local balance checking
This has a connection to typing / type checking / constant evaluation. This will check the balance checking for each model so that errors can be detected sooner. We had no such support in the current front-end. It is highly desired that the balance checking is done symbolically using expressions for the number of equations and components. For the final check one could use constant evaluation of these size expressions.
Synchronous Features
The handling of the synchronous features (Clocks, etc) and state machines can be ported and adapted from the current front-end to the new front-end. The specification and the ideas from Modelica conference paper can be used. The inner/outer has a connection to this.
Iterators
Handling of iterators needs to be implemented. After some internal discussions the easiest way to do this is to extend the current class scope with a new node InstNode.SCOPE_NODE which can contain a stack of iterators. The lookup needs to be changed to handle this new node.
Reporting errors
For things that are not implemented yet, or additional error messages for debugging is good to add error messages using getInstanceName (as is easy to copy/paste without changing the name of the function). Example:
assert(false, getInstanceName() + " got unknown class");
Instead of using Error.addSourceMessage one could use Error.addInternalError using sourceInfo()
builtin function.
Example:
Error.addSourceMessage(Error.INTERNAL_ERROR, {"Graph.allReachableNodesInt failed."}, sourceInfo()); // better Error.addInternalError("Got internal hash table size " + intString(szBucket) + " <1", sourceInfo());
Known issues