Skip to content

Commit 4b06d70

Browse files
BindPython ABI (#2796)
* importing `cpython_bindings.py` when using `BindPython` ABI cpython_bindings.py will contain declarations for all the CPython C API function required * BindPython for no args - no return functions in LLVM backend * BindPython support for args of int and float types * refactored "from ... import ..." AST to ASR code * BindPython support for args of str and bool types * BindPython support for return type of str, bool, integer & real types * refactored python_bind split one `pass_python_bind` function into `generate_body`, `native_to_cpython`, `cpython_to_native`, and `pass_python_bind` * fix for CI * fix for failing test * add integration test for llvm backend * refactor: importing cpython_bindings separate out into function * skip python_bind ASR pass when using C backend * changes according to code review * generating CPython related function declarations in python_bind pass * fix for failing CI * remove use of `PyRun_SimpleString` to set python path some refactoring * clean up unwanted comment * refactored `declare_functions` to asr_utils.cpp * Update src/libasr/pass/python_bind.cpp Co-authored-by: Shaikh Ubaid <shaikhubaid769@gmail.com> * skipping python_bind ASR pass if `--enable-cpython` flag not used * fix related to previous commit --------- Co-authored-by: Shaikh Ubaid <shaikhubaid769@gmail.com>
1 parent 9374feb commit 4b06d70

File tree

11 files changed

+615
-13
lines changed

11 files changed

+615
-13
lines changed

integration_tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ RUN(NAME bindc_05 LABELS llvm c
660660
EXTRAFILES bindc_05b.c)
661661
RUN(NAME bindc_06 LABELS llvm c
662662
EXTRAFILES bindc_06b.c)
663-
RUN(NAME bindpy_01 LABELS cpython c_py EXTRA_ARGS --enable-cpython NOFAST COPY_TO_BIN bindpy_01_module.py)
663+
RUN(NAME bindpy_01 LABELS cpython llvm_py c_py EXTRA_ARGS --enable-cpython NOFAST COPY_TO_BIN bindpy_01_module.py)
664664
RUN(NAME bindpy_02 LABELS cpython c_py EXTRA_ARGS --link-numpy COPY_TO_BIN bindpy_02_module.py)
665665
RUN(NAME bindpy_03 LABELS cpython c_py EXTRA_ARGS --link-numpy NOFAST COPY_TO_BIN bindpy_03_module.py)
666666
RUN(NAME bindpy_04 LABELS cpython c_py EXTRA_ARGS --link-numpy NOFAST COPY_TO_BIN bindpy_04_module.py)

integration_tests/bindpy_05.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def PyObject_CallObject(a: CPtr, b: CPtr) -> CPtr:
4141
pass
4242

4343
@ccall(header="Python.h")
44-
def PyLong_AsLongLong(a: CPtr) -> i32:
44+
def PyLong_AsLongLong(a: CPtr) -> i64:
4545
pass
4646

4747
def my_f():
@@ -62,9 +62,9 @@ def my_f():
6262
_Py_DecRef(pArgs)
6363
assert bool(pValue), "Call to my_f failed\n"
6464

65-
ans: i32 = PyLong_AsLongLong(pValue)
65+
ans: i64 = PyLong_AsLongLong(pValue)
6666
print("Ans is", ans)
67-
assert ans == 5
67+
assert ans == i64(5)
6868

6969

7070
def main0():

src/bin/lpython.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ int emit_c(const std::string &infile,
321321
pass_manager.use_default_passes(true);
322322
compiler_options.po.always_run = true;
323323
compiler_options.po.run_fun = "f";
324+
compiler_options.po.c_skip_bindpy_pass = true;
324325

325326
pass_manager.apply_passes(al, asr, compiler_options.po, diagnostics);
326327

@@ -370,6 +371,7 @@ int emit_c_to_file(const std::string &infile, const std::string &outfile,
370371

371372
compiler_options.po.run_fun = "f";
372373
compiler_options.po.always_run = true;
374+
compiler_options.po.c_skip_bindpy_pass = true;
373375

374376
pass_manager.use_default_passes(true);
375377
pass_manager.apply_passes(al, asr, compiler_options.po, diagnostics);
@@ -1130,7 +1132,7 @@ int compile_python_using_llvm(
11301132
LCompilers::LPython::DynamicLibrary cpython_lib;
11311133
LCompilers::LPython::DynamicLibrary symengine_lib;
11321134

1133-
if (compiler_options.enable_cpython) {
1135+
if (compiler_options.po.enable_cpython) {
11341136
LCompilers::LPython::open_cpython_library(cpython_lib);
11351137
}
11361138
if (compiler_options.enable_symengine) {
@@ -1149,7 +1151,7 @@ int compile_python_using_llvm(
11491151
e.execfn<void>("__module___main_____main__global_stmts");
11501152
}
11511153

1152-
if (compiler_options.enable_cpython) {
1154+
if (compiler_options.po.enable_cpython) {
11531155
LCompilers::LPython::close_cpython_library(cpython_lib);
11541156
}
11551157
if (compiler_options.enable_symengine) {
@@ -1533,7 +1535,7 @@ int link_executable(const std::vector<std::string> &infiles,
15331535
cmd += " -L$CONDA_PREFIX/lib -Wl,-rpath -Wl,$CONDA_PREFIX/lib -lsymengine";
15341536
}
15351537

1536-
if (compiler_options.enable_cpython) {
1538+
if (compiler_options.po.enable_cpython) {
15371539
std::string py_version = "3.10";
15381540
std::string py_flags = R"(-I $CONDA_PREFIX/include/python)" + py_version + R"( -L$CONDA_PREFIX/lib -Wl,-rpath -Wl,$CONDA_PREFIX/lib -lpython)" + py_version + R"()";
15391541
if (compiler_options.link_numpy) {
@@ -1592,7 +1594,7 @@ int link_executable(const std::vector<std::string> &infiles,
15921594
if (compiler_options.enable_symengine) {
15931595
cmd += " -L$CONDA_PREFIX/lib -Wl,-rpath -Wl,$CONDA_PREFIX/lib -lsymengine";
15941596
}
1595-
if (compiler_options.enable_cpython) {
1597+
if (compiler_options.po.enable_cpython) {
15961598
std::string py_version = "3.10";
15971599
std::string py_flags = R"(-I $CONDA_PREFIX/include/python)" + py_version + R"( -L$CONDA_PREFIX/lib -Wl,-rpath -Wl,$CONDA_PREFIX/lib -lpython)" + py_version + R"()";
15981600
if (compiler_options.link_numpy) {
@@ -1919,7 +1921,7 @@ int main(int argc, char *argv[])
19191921
app.add_flag("--dump-all-passes", compiler_options.po.dump_all_passes, "Apply all the passes and dump the ASR into a file");
19201922
app.add_flag("--dump-all-passes-fortran", compiler_options.po.dump_fortran, "Apply all passes and dump the ASR after each pass into fortran file");
19211923
app.add_flag("--cumulative", compiler_options.po.pass_cumulative, "Apply all the passes cumulatively till the given pass");
1922-
app.add_flag("--enable-cpython", compiler_options.enable_cpython, "Enable CPython runtime");
1924+
app.add_flag("--enable-cpython", compiler_options.po.enable_cpython, "Enable CPython runtime");
19231925
app.add_flag("--enable-symengine", compiler_options.enable_symengine, "Enable Symengine runtime");
19241926
app.add_flag("--link-numpy", compiler_options.link_numpy, "Enable NumPy runtime (implies --enable-cpython)");
19251927
app.add_flag("--separate-compilation", separate_compilation, "Generates unique names for all the symbols");
@@ -1981,7 +1983,7 @@ int main(int argc, char *argv[])
19811983
}
19821984

19831985
if (compiler_options.link_numpy) {
1984-
compiler_options.enable_cpython = true;
1986+
compiler_options.po.enable_cpython = true;
19851987
}
19861988

19871989
if (arg_version) {

src/libasr/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ set(SRC
3737
pass/for_all.cpp
3838
pass/while_else.cpp
3939
pass/global_stmts.cpp
40+
pass/python_bind.cpp
4041
pass/select_case.cpp
4142
pass/init_expr.cpp
4243
pass/implied_do_loops.cpp

src/libasr/asr_utils.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,6 +1707,97 @@ void append_error(diag::Diagnostics& diag, const std::string& msg,
17071707
//Initialize pointer to zero so that it can be initialized in first call to get_instance
17081708
ASRUtils::LabelGenerator* ASRUtils::LabelGenerator::label_generator = nullptr;
17091709

1710+
ASR::expr_t *type_enum_to_asr_expr(Allocator &al, enum TTYPE_T t, const Location &l, std::string n,
1711+
SymbolTable *current_scope, ASR::intentType intent) {
1712+
ASR::ttype_t *type = nullptr;
1713+
1714+
Str s;
1715+
s.from_str(al, n);
1716+
1717+
switch (t) {
1718+
case VOID:
1719+
return nullptr;
1720+
case I1:
1721+
type = ASRUtils::TYPE(ASR::make_Logical_t(al, l, 4));
1722+
break;
1723+
case I8:
1724+
type = ASRUtils::TYPE(ASR::make_Integer_t(al, l, 1));
1725+
break;
1726+
case I32:
1727+
type = ASRUtils::TYPE(ASR::make_Integer_t(al, l, 4));
1728+
break;
1729+
case I64:
1730+
type = ASRUtils::TYPE(ASR::make_Integer_t(al, l, 8));
1731+
break;
1732+
case U8:
1733+
type = ASRUtils::TYPE(ASR::make_UnsignedInteger_t(al, l, 1));
1734+
break;
1735+
case U32:
1736+
type = ASRUtils::TYPE(ASR::make_UnsignedInteger_t(al, l, 4));
1737+
break;
1738+
case U64:
1739+
type = ASRUtils::TYPE(ASR::make_UnsignedInteger_t(al, l, 8));
1740+
break;
1741+
case F32:
1742+
type = ASRUtils::TYPE(ASR::make_Real_t(al, l, 4));
1743+
break;
1744+
case F64:
1745+
type = ASRUtils::TYPE(ASR::make_Real_t(al, l, 8));
1746+
break;
1747+
case STR:
1748+
type = ASRUtils::TYPE(ASR::make_Character_t(al, l, 1, -2, nullptr));
1749+
break;
1750+
case PTR:
1751+
type = ASRUtils::TYPE(ASR::make_CPtr_t(al, l));
1752+
break;
1753+
case PTR_TO_PTR:
1754+
type = ASRUtils::TYPE(ASR::make_Pointer_t(al, l, ASRUtils::TYPE(ASR::make_CPtr_t(al, l))));
1755+
break;
1756+
}
1757+
LCOMPILERS_ASSERT(type);
1758+
ASR::symbol_t *v = ASR::down_cast<ASR::symbol_t>(ASR::make_Variable_t(al, l, current_scope, s.c_str(al), nullptr,
1759+
0, intent, nullptr, nullptr, ASR::storage_typeType::Default,
1760+
type, nullptr, ASR::abiType::BindC, ASR::Public,
1761+
ASR::presenceType::Required, true));
1762+
current_scope->add_symbol(n, v);
1763+
return ASRUtils::EXPR(ASR::make_Var_t(al, l, v));
1764+
}
1765+
1766+
void declare_function(Allocator &al, ASRFunc fn, const Location &l, SymbolTable *parent_scope,
1767+
std::string header_name) {
1768+
Str s;
1769+
char *c_header = nullptr;
1770+
if (header_name != "") {
1771+
s.from_str(al, header_name);
1772+
c_header = s.c_str(al);
1773+
}
1774+
s.from_str(al, fn.m_name);
1775+
Vec<ASR::expr_t*> args;
1776+
args.reserve(al, fn.args.size());
1777+
SymbolTable *current_scope = al.make_new<SymbolTable>(parent_scope);
1778+
int c = 0;
1779+
for (auto j: fn.args) {
1780+
args.push_back(al, type_enum_to_asr_expr(al, j, l, fn.m_name + std::to_string(++c), current_scope,
1781+
ASRUtils::intent_in));
1782+
}
1783+
ASR::expr_t *retval = type_enum_to_asr_expr(al, fn.retvar, l, "_lpython_return_variable", current_scope,
1784+
ASRUtils::intent_return_var);
1785+
char *fn_name = s.c_str(al);
1786+
ASR::asr_t *f = ASRUtils::make_Function_t_util(al, l, current_scope, fn_name, nullptr, 0, args.p, args.n,
1787+
nullptr, 0, retval, ASR::abiType::BindC, ASR::accessType::Public,
1788+
ASR::deftypeType::Interface, nullptr, false, false, false, false, false, nullptr, 0,
1789+
false, false, false, c_header);
1790+
1791+
parent_scope->add_symbol(fn.m_name, ASR::down_cast<ASR::symbol_t>(f));
1792+
}
1793+
1794+
void declare_functions(Allocator &al, std::vector<ASRFunc> fns, const Location &l, SymbolTable *parent_scope,
1795+
std::string header_name) {
1796+
for (auto i: fns) {
1797+
declare_function(al, i, l, parent_scope, header_name);
1798+
}
1799+
}
1800+
17101801
} // namespace ASRUtils
17111802

17121803

src/libasr/asr_utils.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5415,6 +5415,41 @@ static inline bool is_argument_of_type_CPtr(ASR::expr_t *var) {
54155415
return is_argument;
54165416
}
54175417

5418+
enum TTYPE_T {
5419+
VOID,
5420+
I1,
5421+
I8,
5422+
STR,
5423+
I32,
5424+
I64,
5425+
U8,
5426+
U32,
5427+
U64,
5428+
F32,
5429+
F64,
5430+
PTR,
5431+
PTR_TO_PTR,
5432+
};
5433+
5434+
typedef struct {
5435+
std::string m_name;
5436+
std::vector<enum TTYPE_T> args;
5437+
enum TTYPE_T retvar;
5438+
} ASRFunc;
5439+
5440+
// Create a variable with name `n` and type `t` and add the symbol into `currect_scope`
5441+
// Returns the variable
5442+
ASR::expr_t *type_enum_to_asr_expr(Allocator &al, enum TTYPE_T t, const Location &l, std::string n,
5443+
SymbolTable *current_scope, ASR::intentType intent);
5444+
5445+
// created a BindC Interface function decleration in the `parent_scope`
5446+
void declare_function(Allocator &al, ASRFunc fn, const Location &l, SymbolTable *parent_scope,
5447+
std::string header_name="");
5448+
5449+
// created a BindC Interface functions decleration in the `parent_scope`
5450+
void declare_functions(Allocator &al, std::vector<ASRFunc> fns, const Location &l, SymbolTable *parent_scope,
5451+
std::string header_name="");
5452+
54185453
} // namespace ASRUtils
54195454

54205455
} // namespace LCompilers

src/libasr/codegen/asr_to_c.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ R"(
820820
}
821821

822822
std::string body;
823-
if (compiler_options.enable_cpython) {
823+
if (compiler_options.po.enable_cpython) {
824824
headers.insert("Python.h");
825825
body += R"(
826826
Py_Initialize();
@@ -851,7 +851,7 @@ R"( // Initialise Numpy
851851
body += src;
852852
}
853853

854-
if (compiler_options.enable_cpython) {
854+
if (compiler_options.po.enable_cpython) {
855855
body += R"(
856856
if (Py_FinalizeEx() < 0) {
857857
fprintf(stderr,"BindPython: Unknown Error\n");

src/libasr/pass/pass_manager.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include <libasr/pass/replace_print_struct_type.h>
5555
#include <libasr/pass/promote_allocatable_to_nonallocatable.h>
5656
#include <libasr/pass/replace_function_call_in_declaration.h>
57+
#include <libasr/pass/python_bind.h>
5758
#include <libasr/codegen/asr_to_fortran.h>
5859
#include <libasr/asr_verify.h>
5960
#include <libasr/pickle.h>
@@ -79,6 +80,7 @@ namespace LCompilers {
7980
{"do_loops", &pass_replace_do_loops},
8081
{"while_else", &pass_while_else},
8182
{"global_stmts", &pass_wrap_global_stmts},
83+
{"python_bind", &pass_python_bind},
8284
{"implied_do_loops", &pass_replace_implied_do_loops},
8385
{"array_op", &pass_replace_array_op},
8486
{"symbolic", &pass_replace_symbolic},
@@ -206,6 +208,7 @@ namespace LCompilers {
206208
PassManager(): apply_default_passes{false},
207209
c_skip_pass{false} {
208210
_passes = {
211+
"python_bind",
209212
"nested_vars",
210213
"global_stmts",
211214
"transform_optional_argument_functions",

0 commit comments

Comments
 (0)