Skip to content

Commit 69b826e

Browse files
--jit option to execute without creation of executable file (#2564)
* Add --jit option to execute without creation of executable file * execute global_init & global_stmts after checking their existence remove the try catch block around the execution of global_init & global_stmts * CI fix & raise error for non-llvm backend with jit * combined `compile_python_to_object_file` and `execute_python_using_jit` to `compile_python_using_llvm` removed duplicated code * Add testing mechanism * Testing with cpython and symengine * skipping jit tests with external dependency * skipping testing JIT that depend on syms or cpython * support to use cpython and symengine with JIT * Trigger CI/CD * windows fix * WASM ci fix * allow few tests to fail under `--jit` * fix for #2595 * factored out loading cpython and symengine libraries * wasm related fix * windows ci fix * windows ci fix * windows ci fix * updated for consistency Co-authored-by: Shaikh Ubaid <[email protected]> * updated for consistency * Apply suggestions from code review Co-authored-by: Shaikh Ubaid <[email protected]> --------- Co-authored-by: Shaikh Ubaid <[email protected]>
1 parent fce0b35 commit 69b826e

File tree

5 files changed

+191
-29
lines changed

5 files changed

+191
-29
lines changed

integration_tests/CMakeLists.txt

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ message("LPYTHON_RTLIB_DIR: ${LPYTHON_RTLIB_DIR}")
7676
message("LPYTHON_RTLIB_LIBRARY: ${LPYTHON_RTLIB_LIBRARY}")
7777

7878

79-
macro(RUN_UTIL RUN_FAIL RUN_NAME RUN_FILE_NAME RUN_LABELS RUN_EXTRAFILES RUN_NOMOD RUN_EXTRA_ARGS RUN_COPY_TO_BIN)
79+
macro(RUN_UTIL RUN_FAIL RUN_NAME RUN_FILE_NAME RUN_LABELS RUN_EXTRAFILES RUN_NOMOD RUN_EXTRA_ARGS RUN_COPY_TO_BIN RUN_SKIP_ON_JIT)
8080
set(fail ${${RUN_FAIL}})
81+
set(skip_jit ${${RUN_SKIP_ON_JIT}})
8182
set(name ${${RUN_NAME}})
8283
set(file_name ${${RUN_FILE_NAME}})
8384
set(labels ${${RUN_LABELS}})
@@ -101,11 +102,21 @@ macro(RUN_UTIL RUN_FAIL RUN_NAME RUN_FILE_NAME RUN_LABELS RUN_EXTRAFILES RUN_NOM
101102
set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C)
102103
target_link_libraries(${name} lpython_rtlib)
103104
add_test(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name})
105+
add_test(
106+
NAME "${name}_jit"
107+
COMMAND ${LPYTHON} --jit ${extra_args} ${CMAKE_CURRENT_SOURCE_DIR}/${file_name}.py)
104108
if (labels)
105109
set_tests_properties(${name} PROPERTIES LABELS "${labels}")
110+
set_tests_properties("${name}_jit" PROPERTIES LABELS "${labels}")
106111
endif()
107112
if (${fail})
108113
set_tests_properties(${name} PROPERTIES WILL_FAIL TRUE)
114+
set_tests_properties("${name}_jit" PROPERTIES WILL_FAIL TRUE)
115+
endif()
116+
if (${skip_jit})
117+
set_tests_properties("${name}_jit" PROPERTIES SKIP_RETURN_CODE ${skip_jit})
118+
elseif (DEFINED ${RUN_EXTRAFILES} AND NOT ${RUN_FAIL})
119+
set_tests_properties("${name}_jit" PROPERTIES SKIP_RETURN_CODE 1)
109120
endif()
110121
elseif (KIND STREQUAL "llvm_py")
111122
add_custom_command(
@@ -118,11 +129,21 @@ macro(RUN_UTIL RUN_FAIL RUN_NAME RUN_FILE_NAME RUN_LABELS RUN_EXTRAFILES RUN_NOM
118129
set_target_properties(${name} PROPERTIES LINKER_LANGUAGE C)
119130
target_link_libraries(${name} lpython_rtlib Python::Python)
120131
add_test(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name})
132+
add_test(
133+
NAME "${name}_jit"
134+
COMMAND ${LPYTHON} --jit ${extra_args} ${CMAKE_CURRENT_SOURCE_DIR}/${file_name}.py)
121135
if (labels)
122136
set_tests_properties(${name} PROPERTIES LABELS "${labels}")
137+
set_tests_properties("${name}_jit" PROPERTIES LABELS "${labels}")
123138
endif()
124139
if (${fail})
125140
set_tests_properties(${name} PROPERTIES WILL_FAIL TRUE)
141+
set_tests_properties("${name}_jit" PROPERTIES WILL_FAIL TRUE)
142+
endif()
143+
if (${skip_jit})
144+
set_tests_properties("${name}_jit" PROPERTIES SKIP_RETURN_CODE ${skip_jit})
145+
elseif (DEFINED ${RUN_EXTRAFILES} AND NOT ${RUN_FAIL})
146+
set_tests_properties("${name}_jit" PROPERTIES SKIP_RETURN_CODE 1)
126147
endif()
127148
elseif(KIND STREQUAL "llvm_sym")
128149
add_custom_command(
@@ -139,11 +160,21 @@ macro(RUN_UTIL RUN_FAIL RUN_NAME RUN_FILE_NAME RUN_LABELS RUN_EXTRAFILES RUN_NOM
139160
endif()
140161
target_link_libraries(${name} lpython_rtlib ${SYMENGINE_LIB})
141162
add_test(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name})
163+
add_test(
164+
NAME "${name}_jit"
165+
COMMAND ${LPYTHON} --enable-symengine --jit ${extra_args} ${CMAKE_CURRENT_SOURCE_DIR}/${file_name}.py)
142166
if (labels)
143167
set_tests_properties(${name} PROPERTIES LABELS "${labels}")
168+
set_tests_properties("${name}_jit" PROPERTIES LABELS "${labels}")
144169
endif()
145170
if (${fail})
146171
set_tests_properties(${name} PROPERTIES WILL_FAIL TRUE)
172+
set_tests_properties("${name}_jit" PROPERTIES WILL_FAIL TRUE)
173+
endif()
174+
if (${skip_jit})
175+
set_tests_properties("${name}_jit" PROPERTIES SKIP_RETURN_CODE ${skip_jit})
176+
elseif (DEFINED ${RUN_EXTRAFILES} AND NOT ${RUN_FAIL})
177+
set_tests_properties("${name}_jit" PROPERTIES SKIP_RETURN_CODE 1)
147178
endif()
148179
elseif(KIND STREQUAL "c")
149180
add_custom_command(
@@ -312,7 +343,7 @@ endmacro(RUN_UTIL)
312343

313344
macro(RUN)
314345
set(options FAIL NOFAST NOMOD ENABLE_CPYTHON LINK_NUMPY NO_WARNINGS)
315-
set(oneValueArgs NAME IMPORT_PATH COPY_TO_BIN REQ_PY_VER)
346+
set(oneValueArgs NAME IMPORT_PATH COPY_TO_BIN REQ_PY_VER SKIP_ON_JIT)
316347
set(multiValueArgs LABELS EXTRAFILES)
317348
cmake_parse_arguments(RUN "${options}" "${oneValueArgs}"
318349
"${multiValueArgs}" ${ARGN} )
@@ -351,14 +382,14 @@ macro(RUN)
351382
endif()
352383

353384
if (NOT FAST)
354-
RUN_UTIL(RUN_FAIL RUN_NAME RUN_FILE_NAME RUN_LABELS RUN_EXTRAFILES RUN_NOMOD RUN_EXTRA_ARGS RUN_COPY_TO_BIN)
385+
RUN_UTIL(RUN_FAIL RUN_NAME RUN_FILE_NAME RUN_LABELS RUN_EXTRAFILES RUN_NOMOD RUN_EXTRA_ARGS RUN_COPY_TO_BIN RUN_SKIP_ON_JIT)
355386
endif()
356387

357388
if ((FAST) AND (NOT RUN_NOFAST))
358389
set(RUN_EXTRA_ARGS ${RUN_EXTRA_ARGS} --fast)
359390
set(RUN_NAME "${RUN_NAME}_FAST")
360391
list(REMOVE_ITEM RUN_LABELS cpython cpython_sym) # remove cpython, cpython_sym, from --fast test
361-
RUN_UTIL(RUN_FAIL RUN_NAME RUN_FILE_NAME RUN_LABELS RUN_EXTRAFILES RUN_NOMOD RUN_EXTRA_ARGS RUN_COPY_TO_BIN)
392+
RUN_UTIL(RUN_FAIL RUN_NAME RUN_FILE_NAME RUN_LABELS RUN_EXTRAFILES RUN_NOMOD RUN_EXTRA_ARGS RUN_COPY_TO_BIN RUN_SKIP_ON_JIT)
362393
endif()
363394
endmacro(RUN)
364395

@@ -806,7 +837,7 @@ RUN(NAME comp_01 LABELS cpython llvm c wasm wasm_x64)
806837
RUN(NAME bit_operations_i32 LABELS cpython llvm c wasm wasm_x64)
807838
RUN(NAME bit_operations_i64 LABELS cpython llvm c wasm)
808839

809-
RUN(NAME test_argv_01 LABELS cpython llvm NOFAST)
840+
RUN(NAME test_argv_01 LABELS cpython llvm NOFAST SKIP_ON_JIT 1)
810841
RUN(NAME global_syms_01 LABELS cpython llvm c)
811842
RUN(NAME global_syms_02 LABELS cpython llvm c)
812843
RUN(NAME global_syms_03_b LABELS cpython llvm c)

src/bin/lpython.cpp

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -792,13 +792,17 @@ int emit_llvm(const std::string &infile,
792792
return 0;
793793
}
794794

795-
int compile_python_to_object_file(
795+
/*
796+
Compiles python to object file, if `to_jit` is false
797+
otherwise execute python code using llvm JIT
798+
*/
799+
int compile_python_using_llvm(
796800
const std::string &infile,
797801
const std::string &outfile,
798802
const std::string &runtime_library_dir,
799803
LCompilers::PassManager& pass_manager,
800804
CompilerOptions &compiler_options,
801-
bool time_report, bool arg_c=false)
805+
bool time_report, bool arg_c=false, bool to_jit=false)
802806
{
803807
Allocator al(4*1024);
804808
LCompilers::diag::Diagnostics diagnostics;
@@ -869,7 +873,6 @@ int compile_python_to_object_file(
869873
}
870874
LCompilers::PythonCompiler fe(compiler_options);
871875
LCompilers::LLVMEvaluator e(compiler_options.target);
872-
std::unique_ptr<LCompilers::LLVMModule> m;
873876
auto asr_to_llvm_start = std::chrono::high_resolution_clock::now();
874877
LCompilers::Result<std::unique_ptr<LCompilers::LLVMModule>>
875878
res = fe.get_llvm3(*asr, pass_manager, diagnostics, infile);
@@ -882,12 +885,55 @@ int compile_python_to_object_file(
882885
print_time_report(times, time_report);
883886
return 3;
884887
}
885-
m = std::move(res.result);
886-
auto llvm_start = std::chrono::high_resolution_clock::now();
887-
e.save_object_file(*(m->m_m), outfile);
888-
auto llvm_end = std::chrono::high_resolution_clock::now();
889-
times.push_back(std::make_pair("LLVM to binary", std::chrono::duration<double, std::milli>(llvm_end - llvm_start).count()));
890-
print_time_report(times, time_report);
888+
std::unique_ptr<LCompilers::LLVMModule> m = std::move(res.result);
889+
890+
if (to_jit) {
891+
LCompilers::LPython::DynamicLibrary cpython_lib;
892+
LCompilers::LPython::DynamicLibrary symengine_lib;
893+
894+
if (compiler_options.enable_cpython) {
895+
LCompilers::LPython::open_cpython_library(cpython_lib);
896+
}
897+
if (compiler_options.enable_symengine) {
898+
LCompilers::LPython::open_symengine_library(symengine_lib);
899+
}
900+
901+
auto llvm_start = std::chrono::high_resolution_clock::now();
902+
903+
bool call_init = false;
904+
bool call_stmts = false;
905+
if (m->get_return_type("__module___main_____main__global_init") == "void") {
906+
call_init = true;
907+
}
908+
if (m->get_return_type("__module___main_____main__global_stmts") == "void") {
909+
call_stmts = true;
910+
}
911+
912+
e.add_module(std::move(m));
913+
if (call_init) {
914+
e.voidfn("__module___main_____main__global_init");
915+
}
916+
if (call_stmts) {
917+
e.voidfn("__module___main_____main__global_stmts");
918+
}
919+
920+
if (compiler_options.enable_cpython) {
921+
LCompilers::LPython::close_cpython_library(cpython_lib);
922+
}
923+
if (compiler_options.enable_symengine) {
924+
LCompilers::LPython::close_symengine_library(symengine_lib);
925+
}
926+
927+
auto llvm_end = std::chrono::high_resolution_clock::now();
928+
times.push_back(std::make_pair("LLVM JIT execution", std::chrono::duration<double, std::milli>(llvm_end - llvm_start).count()));
929+
print_time_report(times, time_report);
930+
} else {
931+
auto llvm_start = std::chrono::high_resolution_clock::now();
932+
e.save_object_file(*(m->m_m), outfile);
933+
auto llvm_end = std::chrono::high_resolution_clock::now();
934+
times.push_back(std::make_pair("LLVM to binary", std::chrono::duration<double, std::milli>(llvm_end - llvm_start).count()));
935+
print_time_report(times, time_report);
936+
}
891937
return 0;
892938
}
893939

@@ -1560,6 +1606,7 @@ int main(int argc, char *argv[])
15601606
bool print_rtl_header_dir = false;
15611607
bool print_rtl_dir = false;
15621608
bool separate_compilation = false;
1609+
bool to_jit = false;
15631610

15641611
std::string arg_fmt_file;
15651612
// int arg_fmt_indent = 4;
@@ -1593,6 +1640,7 @@ int main(int argc, char *argv[])
15931640
app.add_option("-I", compiler_options.import_paths, "Specify the paths"
15941641
"to look for the module")->allow_extra_args(false);
15951642
// app.add_option("-J", arg_J, "Where to save mod files");
1643+
app.add_flag("--jit", to_jit, "Execute the program using just-in-time (JIT) compiler");
15961644
app.add_flag("-g", compiler_options.emit_debug_info, "Compile with debugging information");
15971645
app.add_flag("--debug-with-line-column", compiler_options.emit_debug_line_column,
15981646
"Convert the linear location info into line + column in the debugging information");
@@ -1894,10 +1942,10 @@ int main(int argc, char *argv[])
18941942
}
18951943
}
18961944

1897-
if (arg_c) {
1945+
if (arg_c && !to_jit) {
18981946
if (backend == Backend::llvm) {
18991947
#ifdef HAVE_LFORTRAN_LLVM
1900-
return compile_python_to_object_file(arg_file, outfile, runtime_library_dir, lpython_pass_manager, compiler_options, time_report,
1948+
return compile_python_using_llvm(arg_file, outfile, runtime_library_dir, lpython_pass_manager, compiler_options, time_report,
19011949
arg_c);
19021950
#else
19031951
std::cerr << "The -c option requires the LLVM backend to be enabled. Recompile with `WITH_LLVM=yes`." << std::endl;
@@ -1911,6 +1959,23 @@ int main(int argc, char *argv[])
19111959
if (endswith(arg_file, ".py"))
19121960
{
19131961
int err = 0;
1962+
if (to_jit) {
1963+
#ifdef HAVE_LFORTRAN_LLVM
1964+
if (backend != Backend::llvm) {
1965+
std::cerr << "JIT option is only available with LLVM backend" << std::endl;
1966+
return 1;
1967+
}
1968+
compiler_options.emit_debug_info = false;
1969+
compiler_options.emit_debug_line_column = false;
1970+
compiler_options.generate_object_code = false;
1971+
return compile_python_using_llvm(arg_file, "", runtime_library_dir,
1972+
lpython_pass_manager, compiler_options, time_report, false, true);
1973+
#else
1974+
std::cerr << "Just-In-Time Compilation of Python files requires the LLVM backend to be enabled."
1975+
" Recompile with `WITH_LLVM=yes`." << std::endl;
1976+
return 1;
1977+
#endif
1978+
}
19141979
if (backend == Backend::x86) {
19151980
err = compile_to_binary_x86(arg_file, outfile,
19161981
runtime_library_dir, compiler_options, time_report);
@@ -1931,7 +1996,7 @@ int main(int argc, char *argv[])
19311996
} else if (backend == Backend::llvm) {
19321997
#ifdef HAVE_LFORTRAN_LLVM
19331998
std::string tmp_o = outfile + ".tmp.o";
1934-
err = compile_python_to_object_file(arg_file, tmp_o, runtime_library_dir,
1999+
err = compile_python_using_llvm(arg_file, tmp_o, runtime_library_dir,
19352000
lpython_pass_manager, compiler_options, time_report);
19362001
if (err != 0) return err;
19372002
err = link_executable({tmp_o}, outfile, runtime_library_dir,

src/libasr/codegen/llvm_utils.cpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,17 +2120,15 @@ namespace LCompilers {
21202120
throw LCompilersException("list for " + type_code + " not declared yet.");
21212121
}
21222122
int32_t type_size = std::get<1>(typecode2listtype[type_code]);
2123-
llvm::Value* arg_size = llvm::ConstantInt::get(context,
2124-
llvm::APInt(32, type_size * initial_capacity));
2125-
2126-
llvm::Value* list_data = LLVM::lfortran_malloc(context, module, *builder,
2127-
arg_size);
2123+
llvm::Value* llvm_type_size = llvm::ConstantInt::get(context, llvm::APInt(32, type_size));
2124+
llvm::Value* current_capacity = llvm::ConstantInt::get(context, llvm::APInt(32, initial_capacity));
2125+
llvm::Value* list_data = LLVM::lfortran_calloc(context, module, *builder,
2126+
current_capacity, llvm_type_size);
21282127
llvm::Type* el_type = std::get<2>(typecode2listtype[type_code]);
21292128
list_data = builder->CreateBitCast(list_data, el_type->getPointerTo());
21302129
llvm::Value* list_data_ptr = get_pointer_to_list_data(list);
21312130
builder->CreateStore(list_data, list_data_ptr);
21322131
llvm::Value* current_end_point = llvm::ConstantInt::get(context, llvm::APInt(32, n));
2133-
llvm::Value* current_capacity = llvm::ConstantInt::get(context, llvm::APInt(32, initial_capacity));
21342132
builder->CreateStore(current_end_point, get_pointer_to_current_end_point(list));
21352133
builder->CreateStore(current_capacity, get_pointer_to_current_capacity(list));
21362134
}
@@ -2143,8 +2141,8 @@ namespace LCompilers {
21432141
}
21442142
int32_t type_size = std::get<1>(typecode2listtype[type_code]);
21452143
llvm::Value* llvm_type_size = llvm::ConstantInt::get(context, llvm::APInt(32, type_size));
2146-
llvm::Value* arg_size = builder->CreateMul(llvm_type_size, initial_capacity);
2147-
llvm::Value* list_data = LLVM::lfortran_malloc(context, module, *builder, arg_size);
2144+
llvm::Value* list_data = LLVM::lfortran_calloc(context, module, *builder,
2145+
initial_capacity, llvm_type_size);
21482146

21492147
llvm::Type* el_type = std::get<2>(typecode2listtype[type_code]);
21502148
list_data = builder->CreateBitCast(list_data, el_type->getPointerTo());
@@ -2288,10 +2286,9 @@ namespace LCompilers {
22882286
builder->CreateStore(src_capacity, dest_capacity_ptr);
22892287
llvm::Value* src_data = LLVM::CreateLoad(*builder, get_pointer_to_list_data(src));
22902288
int32_t type_size = std::get<1>(typecode2listtype[src_type_code]);
2291-
llvm::Value* arg_size = builder->CreateMul(llvm::ConstantInt::get(context,
2292-
llvm::APInt(32, type_size)), src_capacity);
2293-
llvm::Value* copy_data = LLVM::lfortran_malloc(context, *module, *builder,
2294-
arg_size);
2289+
llvm::Value* llvm_type_size = llvm::ConstantInt::get(context, llvm::APInt(32, type_size));
2290+
llvm::Value* copy_data = LLVM::lfortran_calloc(context, *module, *builder,
2291+
src_capacity, llvm_type_size);
22952292
llvm::Type* el_type = std::get<2>(typecode2listtype[src_type_code]);
22962293
copy_data = builder->CreateBitCast(copy_data, el_type->getPointerTo());
22972294

@@ -2346,6 +2343,8 @@ namespace LCompilers {
23462343
// end
23472344
llvm_utils->start_new_block(loopend);
23482345
} else {
2346+
llvm::Value* arg_size = builder->CreateMul(llvm::ConstantInt::get(context,
2347+
llvm::APInt(32, type_size)), src_capacity);
23492348
builder->CreateMemCpy(copy_data, llvm::MaybeAlign(), src_data,
23502349
llvm::MaybeAlign(), arg_size);
23512350
builder->CreateStore(copy_data, get_pointer_to_list_data(dest));

src/lpython/utils.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#define NOMINMAX
44
#endif // NOMINMAX
55
#include <windows.h>
6+
#else
7+
#include <dlfcn.h>
68
#endif
79

810
#include <fstream>
@@ -126,6 +128,58 @@ bool path_exists(std::string path) {
126128
}
127129
}
128130

131+
#ifdef HAVE_LFORTRAN_LLVM
132+
133+
void open_cpython_library(DynamicLibrary &l) {
134+
std::string conda_prefix = std::getenv("CONDA_PREFIX");
135+
#if defined (__linux__)
136+
l.l = dlopen((conda_prefix + "/lib/libpython3.so").c_str(), RTLD_DEEPBIND | RTLD_GLOBAL | RTLD_NOW);
137+
#elif defined (__APPLE__)
138+
l.l = dlopen((conda_prefix + "/lib/libpython3.dylib").c_str(), RTLD_GLOBAL | RTLD_NOW);
139+
#else
140+
l.l = LoadLibrary((conda_prefix + "\\python3.dll").c_str());
141+
#endif
142+
143+
if (l.l == nullptr)
144+
throw "Could not open CPython library";
145+
}
146+
147+
void close_cpython_library(DynamicLibrary &l) {
148+
#if (defined (__linux__)) or (defined (__APPLE__))
149+
dlclose(l.l);
150+
l.l = nullptr;
151+
#else
152+
FreeLibrary((HMODULE)l.l);
153+
l.l = nullptr;
154+
#endif
155+
}
156+
157+
void open_symengine_library(DynamicLibrary &l) {
158+
std::string conda_prefix = std::getenv("CONDA_PREFIX");
159+
#if defined (__linux__)
160+
l.l = dlopen((conda_prefix + "/lib/libsymengine.so").c_str(), RTLD_DEEPBIND | RTLD_GLOBAL | RTLD_NOW);
161+
#elif defined (__APPLE__)
162+
l.l = dlopen((conda_prefix + "/lib/libsymengine.dylib").c_str(), RTLD_GLOBAL | RTLD_NOW);
163+
#else
164+
l.l = LoadLibrary((conda_prefix + "\\Library\\bin\\symengine-0.11.dll").c_str());
165+
#endif
166+
167+
if (l.l == nullptr)
168+
throw "Could not open SymEngine library";
169+
}
170+
171+
void close_symengine_library(DynamicLibrary &l) {
172+
#if (defined (__linux__)) or (defined (__APPLE__))
173+
dlclose(l.l);
174+
l.l = nullptr;
175+
#else
176+
FreeLibrary((HMODULE)l.l);
177+
l.l = nullptr;
178+
#endif
179+
}
180+
181+
#endif
182+
129183
// Decodes the exit status code of the process (in Unix)
130184
// See `WEXITSTATUS` for more information.
131185
// https://stackoverflow.com/a/27117435/15913193

0 commit comments

Comments
 (0)