Week 8

Summarizing where I had left off last week, I had just started working on framing a symbolic ASR pass. The goal behind framing such an ASR->ASR pass is that, currently we’ve added support for dealing with symbolic expressions & operations for the C backend only. Hence the next step here should be to lift this symbolic support from the C backend and extend its functionality to all other backends like LLVM. This is where the pass comes in.

I think this could be explained better through an example. Consider the following program and the ASR for it.

(lf) anutosh491@spbhat68:~/lpython/lpython$ cat examples/expr.py
from sympy import pi

def main0():
    x: S = pi
    print(x)

main0()

(lf) anutosh491@spbhat68:~/lpython/lpython$ lpython --show-asr examples/expr.py
        main0:
            (Function
                (SymbolTable
                    2
                    {
                        x:
                            (Variable
                                2
                                x
                                []
                                Local
                                ()
                                ()
                                Default
                                (SymbolicExpression)
                                ()
                                Source
                                Public
                                Required
                                .false.
                            )
                    })
                main0
                (FunctionType
                    []
                    ()
                    Source
                    Implementation
                    ()
                    .false.
                    .false.
                    .false.
                    .false.
                    .false.
                    []
                    []
                    .false.
                )
                []
                []
                [(=
                    (Var 2 x)
                    (IntrinsicFunction
                        SymbolicPi
                        []
                        0
                        (SymbolicExpression)
                        ()
                    )
                    ()
                )
                (Print
                    ()
                    [(Var 2 x)]
                    ()
                    ()
                )]
                ()
                Public
                .false.
                .false.
                ()
            )
(lf) anutosh491@spbhat68:~/lpython/lpython$ lpython --backend=c --enable-symengine examples/expr.py
pi

As you can see this is only being supported through the C backend, but what we want to do is as follows. We want to make use of the bindC functionality available in Lpython and whenever we encounter a symbolic function like basic_add or basic_new_stack we would end up making C calls to symengine’s C wrapper header file where all these functions have been defined. Which means that although we are linking the symengine library, we are essentially trying to replicate the above symbolic operation with whatever is already implemented in LPython

  • Like using the CPtr type which in the backend would represent a void pointer which can be treated equivalently to a SymEngine basic type.
  • And using the @ccall decorator with the symengine cwrapper header file passed into it.

Hence we want to transform the original ASR to the ASR of the following program

(lf) anutosh491@spbhat68:~/lpython/lpython$ cat examples/expr.py
from lpython import ccall

@ccall(header="symengine/cwrapper.h")
def basic_new_heap() -> CPtr:
    pass

@ccall(header="symengine/cwrapper.h")
def basic_const_pi(x: CPtr) -> None:
    pass

@ccall(header="symengine/cwrapper.h")
def basic_str(x: CPtr) -> str:
    pass

def main0():
    x: CPtr = basic_new_heap()
    basic_const_pi(x)
    print(basic_str(x))

main0()

(lf) anutosh491@spbhat68:~/lpython/lpython$ lpython --enable-symengine examples/expr.py
pi
(lf) anutosh491@spbhat68:~/lpython/lpython$ lpython --backend=c --enable-symengine examples/expr.py
pi

As you can see the above program works well with both the LLVM & the C backend. Hence we would like to update the original ASR to the ASR of the program above. As I haven’t framed an ASR pass from scratch before I am still working on it with the help of Thirumalai Shaktivel and Shaikh Ubaid. They are helping me understand terminologies like CallReplacerOnExpressionsVisitor, BaseExprReplacer and other terms which would come in while implementing the pass.

As per my understanding currently, the transformation would happen something like the following

  • Let’s say we encounter an assignment statement as follows
    (=
        (Var 2 x)
        (IntrinsicFunction
            SymbolicPi
            []
            0
            (SymbolicExpression)
            ()
        )
        ()
    )
    
  • Here once we encounter a variable of type SymbolicExpression, we would have to define the basic_new_heap function in the symbol table of the Module node and then make a FunctionCall to the function.
  • Once we encounter the target variable which might be an IntrinsicFunction, we would have to decipher the intrinsic function id and then define the corresponding function from SymEngine Cwrapper (basic_const_pi in this case) in the symbol table of the Module node and then make a SubRoutineCall for the same.
    (Module
        (SymbolTable
            8
            {
                basic_const_pi:
                    (Function
                        (SymbolTable
                            3
                            {
                                x:
                                    (Variable
                                        3
                                        x
                                        []
                                        In
                                        ()
                                        ()
                                        Default
                                        (CPtr)
                                        ()
                                        BindC
                                        Public
                                        Required
                                        .true.
                                    )
                            })
                        basic_const_pi
                        (FunctionType
                            [(CPtr)]
                            ()
                            BindC
                            Interface
                            ()
                            .false.
                            .false.
                            .false.
                            .false.
                            .false.
                            []
                            []
                            .false.
                        )
                        []
                        [(Var 3 x)]
                        []
                        ()
                        Public
                        .false.
                        .false.
                        "symengine/cwrapper.h"
                    ),
                basic_new_heap:
                    (Function
                        (SymbolTable
                            2
                            {
                                _lpython_return_variable:
                                    (Variable
                                        2
                                        _lpython_return_variable
                                        []
                                        ReturnVar
                                        ()
                                        ()
                                        Default
                                        (CPtr)
                                        ()
                                        BindC
                                        Public
                                        Required
                                        .false.
                                    )
                            })
                        basic_new_heap
                        (FunctionType
                            []
                            (CPtr)
                            BindC
                            Interface
                            ()
                            .false.
                            .false.
                            .false.
                            .false.
                            .false.
                            []
                            []
                            .false.
                        )
                        []
                        []
                        []
                        (Var 2 _lpython_return_variable)
                        Public
                        .false.
                        .false.
                        "symengine/cwrapper.h"
                    )
    # In the main0 function 
    
    (=
        (Var 5 x)
        (FunctionCall
            8 basic_new_heap
            ()
            []
            (CPtr)
            ()
            ()
        )
        ()
    )
    (SubroutineCall
        8 basic_const_pi
        ()
        [((Var 5 x))]
        ()
    )
    

I am still working on framing the above pass and hopefully I should be having a concrete implementation by the end of the coming week.

Talking about my plans for the next week.

  • Have a concrete implementation for the pass we discussed in this blog.

Thank You for going through the blog. I hope you like it. Stick around for what’s next to come. Moving into Week 9!

Address

Mumbai, Maharashtra, India