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 avoid pointer
which can be treated equivalently to a SymEnginebasic
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 thebasic_new_heap
function in the symbol table of theModule
node and then make aFunctionCall
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 theModule
node and then make aSubRoutineCall
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!