stfc / PSyclone

Domain-specific compiler and code transformation system for Finite Difference/Volume/Element Earth-system models in Fortran
BSD 3-Clause "New" or "Revised" License
104 stars 28 forks source link

Generate the PSy-layer with PSyIR Fortran backend #1010

Open sergisiso opened 3 years ago

sergisiso commented 3 years ago

Currently the workflow of a psyclone (generate.py) is:

1) create PSy (using API factory to choose __init___)
      |-> create (__init__) Invokes
             |->  create (__init__) Invoke
                       |-> create part of the Schedule (using nodes.__init__'s and Call/BuiltInFactories)

2) execute script (psy -> psy):
      def trans(psy):
             s = psy.invokes[0].invoke.schedule
             modify(s)
             return psy

3) Generate f2pygen 
f2pygen = psy.gen() # Generates remaining Schedule (e.g. Loop bounds) and Symbols, lowers PSyIR and DSL concepts to f2pygen

4) Generate code
code = str(f2pygen)

There are two main issues here:

1) One issue is that psy.gen() still generates part of the PSy-layer after the PSyclone script has been ran, this means:

2) The second issue is the the generation of Fortran is hardcoded into generate.py. There should be a way to choose the PSy-layer target (by command-line, config, transformation, ...)

I think the solution to the second issue is easier, we just have to agree in the way to choose the language and do:

if f2pygen:
     code = str(psy.gen())
elif fortranbackend:
    generic_psyir = psy.lower()
    code = FortranWriter(psy)
elif cppbackend:
    generic_psyir = psy.lower()
    bindings_layer, psy_layer  = generic_psyir.create_bindings_layer()
    code1 = FortranWriter(bindings_layer)
    code2 = CppWriter(psy_layer)
endif

The first issue is more complex because we have to decouple what is generation of PSy-layer (which imo should be all done before the script) from the lowering of DSL to generic concepts (which should be done after trans(psy) (note we can have an optional trans_generic(psy)) step if we need)(see #851 ). I think resolving this issue can be done as we go along and hit the issues in each node.

sergisiso commented 3 years ago

Note that this changes may be disruptive (which makes me uncomfortable as there are a few open questions yet). So I think it would be sensible to start creating the proposed interface (if we agree) but keep the changes inside a transformation until we are more confident with the solution. Something like:

1) create PSy (using API factory to choose __init___)
      |-> create (__init__) Invokes
             |->  create (__init__) Invoke
                       |-> create part of the Schedule (using nodes.__init__'s and Call/BuiltInFactories)

2) execute script (psy -> psy):
      def trans(psy):
             s = psy.invokes[0].invoke.generate_complete_schedule()
             modify(s)
             generic_psyir = s.lower() # or GenericPSyIRWriter(s) ?
             bindings_layer, psy_layer  = generic_psyir.create_bindings_layer()
             code1 = FortranWriter(bindings_layer)
             code2 = CppWriter(psy_layer)
             return psy

3) Generate f2pygen 
f2pygen = psy.gen()

4) Generate code
code = str(f2pygen)
arporter commented 3 years ago

Following on from our discussion on Teams, I think the relevant issue is #896. I've restructured so that a KernelSchedule now subclasses Routine (and this is on master). I'm in the process of doing a similar restructuring for InvokeSchedule (see the comments in #896).

sergisiso commented 3 years ago

@arporter I have also made InvokeShcedule a subclass of subroutine in #1029 (and thankfully in the exact same way as you in the 896_mv_invoke_schedule adding name as first argument of all InvokeSchedules). Is this something you plan to merge soon, or should I continue it myself in #1029 ?

arporter commented 3 years ago

It would be great if you could carry on with it in #1029! Thanks :-)

arporter commented 3 years ago

In trying to finish off #363 I hit problems when running the GOcean examples. To fix them I needed to populate the type of the Symbol representing the field object in the PSy layer. This revealed (unless I'm missing something) that there's currently no direct link between the Container that we create for the PSy layer and the Schedules of each of the Invokes. This then means that attempting to lookup a symbol that exists in the symbol_table of the Container fails. From within a GOLoop, I had to do self.root.invoke.invokes.psy.container.symbol_table to get hold of the symbol table where the field_mod module is declared. Since each Invoke represents a subroutine, I think these should be children of the Container.

sergisiso commented 3 years ago

@arporter This is something that I am solving at #1080 by adding an infere_type() to the Argument class to provide the datatype and a psyir_expression() to generate the "membership accessors expression". If it is what you need you can just change the todo the #1010

arporter commented 3 years ago

Thanks Sergi - I'd already put in a TODO #1010 :-)

sergisiso commented 3 years ago

Oh I see you created a _grid_property_reference(self, grid_property) that is what I am missing in my PR. Looks good, go ahead with this and I will look how to merge this with my implementation.

sergisiso commented 3 years ago

With #1084 and #1087 the GOcean example 8 can fully generate its PSy-layer using only the backends