Skip to content

Setup online lpython #2328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,21 +170,7 @@ jobs:
run: |
set -ex
source $HOME/ext/emsdk/emsdk_env.sh # Activate Emscripten
./build0.sh
emcmake cmake . -GNinja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS_DEBUG="-Wall -Wextra -fexceptions" \
-DWITH_LLVM=no \
-DLPYTHON_BUILD_TO_WASM=yes \
-DLFORTRAN_BUILD_ALL=yes \
-DWITH_STACKTRACE=no \
-DWITH_RUNTIME_STACKTRACE=no \
-DCMAKE_PREFIX_PATH="$CONDA_PREFIX" \
-DCMAKE_INSTALL_PREFIX=`pwd`/inst \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache

cmake --build . -j16 --target install
./build_to_wasm.sh

- name: Test built lpython.wasm
shell: bash -l {0}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ output
*.smod
*.js
*.wasm
*.data
/.ccls-cache/
.cache/
ext/
Expand Down
5 changes: 5 additions & 0 deletions build_to_wasm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
set -e
set -x

mkdir -p src/bin/asset_dir
cp src/runtime/*.py src/bin/asset_dir
cp -r src/runtime/lpython src/bin/asset_dir

./build0.sh
emcmake cmake \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS_DEBUG="-Wall -Wextra -fexceptions" \
Expand Down
1 change: 1 addition & 0 deletions integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ RUN(NAME expr_20 LABELS cpython llvm c)
RUN(NAME expr_21 LABELS cpython llvm c)
RUN(NAME expr_22 LABELS cpython llvm c)
RUN(NAME expr_23 LABELS cpython llvm c)
RUN(NAME expr_24 LABELS cpython wasm) # mandelbrot

RUN(NAME expr_01u LABELS cpython llvm c NOFAST)
RUN(NAME expr_02u LABELS cpython llvm c NOFAST)
Expand Down
77 changes: 77 additions & 0 deletions integration_tests/expr_24.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from lpython import i32, f64, TypeVar, Const
from numpy import empty, int32

h = TypeVar("h")
w = TypeVar("w")

def show_img(w: i32, h: i32, A: i32[h, w]):
print(w, h)
print(A[0, 0])
print(A[h - 1, w - 1])

assert w == 600
assert h == 450
assert A[0, 0] == 254
assert A[h - 1, w - 1] == 254

def show_img_color(w: i32, h: i32, A: i32[h, w, 4]):
print(w, h)
print(A[0, 0, 0])
print(A[h - 1, w - 1, 3])

assert w == 600
assert h == 450
assert A[0, 0, 0] == 214
assert A[h - 1, w - 1, 3] == 255

def main0():
Nx: Const[i32] = 600; Ny: Const[i32] = 450; Nz: Const[i32] = 4; n_max: i32 = 255

xcenter: f64 = f64(-0.5); ycenter: f64 = f64(0.0)
width: f64 = f64(4); height: f64 = f64(3)
dx_di: f64 = width/f64(Nx); dy_dj: f64 = -height/f64(Ny)
x_offset: f64 = xcenter - f64(Nx+1)*dx_di/f64(2.0)
y_offset: f64 = ycenter - f64(Ny+1)*dy_dj/f64(2.0)

i: i32; j: i32; n: i32; idx: i32
x: f64; y: f64; x_0: f64; y_0: f64; x_sqr: f64; y_sqr: f64

image: i32[450, 600] = empty([Ny, Nx], dtype=int32)
image_color: i32[450, 600, 4] = empty([Ny, Nx, Nz], dtype=int32)
palette: i32[4, 3] = empty([4, 3], dtype=int32)

for j in range(Ny):
y_0 = y_offset + dy_dj * f64(j + 1)
for i in range(Nx):
x_0 = x_offset + dx_di * f64(i + 1)
x = 0.0; y = 0.0; n = 0
while(True):
x_sqr = x ** 2.0
y_sqr = y ** 2.0
if (x_sqr + y_sqr > f64(4) or n == n_max):
image[j,i] = 255 - n
break
y = y_0 + f64(2.0) * x * y
x = x_0 + x_sqr - y_sqr
n = n + 1

palette[0,0] = 0; palette[0,1] = 135; palette[0,2] = 68
palette[1,0] = 0; palette[1,1] = 87; palette[1,2] = 231
palette[2,0] = 214; palette[2,1] = 45; palette[2,2] = 32
palette[3,0] = 255; palette[3,1] = 167; palette[3,2] = 0

for j in range(Ny):
for i in range(Nx):
idx = image[j,i] - i32(image[j,i]/4)*4
image_color[j,i,0] = palette[idx,0] # Red
image_color[j,i,1] = palette[idx,1] # Green
image_color[j,i,2] = palette[idx,2] # Blue
image_color[j,i,3] = 255 # Alpha

print("The Mandelbrot image in color:")
show_img_color(Nx, Ny, image_color)
print("The Mandelbrot image in grayscale:")
show_img(Nx, Ny, image)
print("Done.")

main0()
31 changes: 13 additions & 18 deletions src/bin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,24 @@ set_target_properties(lpython PROPERTIES
)

if (HAVE_BUILD_TO_WASM)
# set(WASM_LINK_FLAGS
# "-g0" # Store no debugging information in the generated wasm file. This helps reduce generated file size
# "-Oz" # Optimize for size. With this code size ~ 2.4mb. Without this code size ~49mb
# "-fexceptions" # Enable Cpp exception support
# "--no-entry" # No start function to execute
# "-s ASSERTIONS" # Compile with Assertions which (as per docs) are helpful to debug compilation process
# "-s ALLOW_MEMORY_GROWTH" # Allow dynamic memory growth upto the maximum page size limit
# "-s WASM_BIGINT" # Allow use of i64 integers. ASR is needing this option to be enabled.
# "-s EXPORTED_RUNTIME_METHODS=['cwrap']" # Export cwarp. cwarp helps us to call our EMSCRIPTEN_KEEPALIVE functions
# )

# Some extra flags below that we may need in future. But these may/might increase the code size
# "--preload-file ./asset_dir"
# "-s SAFE_HEAP=1"
# "-s \"EXPORTED_RUNTIME_METHODS=['ccall']\""
# "-s EXPORTED_FUNCTIONS=\"['_free', '_malloc']\""
# "-g0": Store no debugging information in the generated wasm file. This helps reduce generated file size
# "-Oz": Optimize for size. With this code size ~ 2.4mb. Without this code size ~49mb
# "-fexceptions": Enable Cpp exception support
# "--no-entry": No start function to execute
# "-s ASSERTIONS": Compile with Assertions which (as per docs) are helpful to debug compilation process
# "-s ALLOW_MEMORY_GROWTH": Allow dynamic memory growth upto the maximum page size limit
# "-s WASM_BIGINT": Allow use of i64 integers. ASR is needing this option to be enabled.
# "-s EXPORTED_RUNTIME_METHODS=['cwrap']": Export cwarp. cwarp helps us to call our EMSCRIPTEN_KEEPALIVE functions
# "-fsanitize=undefined": Clang's Undefined Behaviour Sanitizer. The LPython parser segfaults.
# This option is for debugging, but currently helps avoid the segfault in the parser.
# "-s INITIAL_MEMORY=536870912": Start the wasm linear memory with sufficiently large size 512Mb.

# Notes:
# STANDALONE_WASM is disabling support for exceptions, so it is currently omitted
# In build_to_wasm.sh, we need CMAKE_CXX_FLAGS_DEBUG="-Wall -Wextra -fexceptions" flags for exception support
set(WASM_COMPILE_FLAGS "-g0 -fexceptions")
set(WASM_COMPILE_FLAGS "-g0 -fexceptions -fsanitize=undefined")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LPython's parser segfaults in many cases when LPython is build to wasm. It seems LPython uses a different parser as compared to LFortran. I think LPython uses LR(1) parser where as LFortran uses GLR parser.

The above -fsanitize flag is actually a debugging flag, but it seems to make LPython's parser to work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, LPython uses LALR(1), which should be an easier parser.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With a previous version of emscripten, the LPython parser would abort with error in the lines of "large memory ~3.5Gbs requested which is more than the available ~2.4Gbs". With the latest version of emscripten, it says "aborted, runtime error". (I updated my installed emscripten to be sure the errors are not due to some old version. )

set(WASM_LINK_FLAGS
"-g0 -Oz -fexceptions -Wall -Wextra --no-entry -s ASSERTIONS -s ALLOW_MEMORY_GROWTH=1 -s WASM_BIGINT -s \"EXPORTED_RUNTIME_METHODS=['cwrap']\""
"-g0 -Oz -fexceptions -fsanitize=undefined --preload-file asset_dir -Wall -Wextra --no-entry -sASSERTIONS=1 -s INITIAL_MEMORY=536870912 -s ALLOW_MEMORY_GROWTH=1 -s WASM_BIGINT -s \"EXPORTED_RUNTIME_METHODS=['cwrap']\""
)
set_target_properties(lpython PROPERTIES COMPILE_FLAGS ${WASM_COMPILE_FLAGS})
set_target_properties(lpython PROPERTIES LINK_FLAGS ${WASM_LINK_FLAGS})
Expand Down
5 changes: 5 additions & 0 deletions src/libasr/ASR.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ presence = Required | Optional
-- LPython manages the conversion of arguments to be passed to such symbols
-- and also converts the return values from such symbols.

-- abi=BindJS: the symbol's implementation is
-- available with Javascript.
-- This abi type is to be mainly used with the WASM Backend.

-- abi=Interactive: the symbol's implementation has been provided by the
-- previous REPL execution (e.g., if LLVM backend is used for the interactive
-- mode, the previous execution generated machine code for this symbol's
Expand All @@ -164,6 +168,7 @@ abi -- External ABI
| GFortranModule -- Yes GFortran
| BindC -- Yes C
| BindPython -- Yes Python
| BindJS -- Yes Javascript
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a new decorator for calls to Javascript functions. We previously used the (BindC + implementation=interface) for recognizing the functions to be imported. But it seems some functions from the numpy intrinsic module use these exact parameters (BindC + interface) and thus we are unable to distinguish between these numpy intrinsic functions and the functions that call into Javascript.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's fine. The same should be done in LFortran.

| Interactive -- Yes Unspecified
| Intrinsic -- Yes Unspecified

Expand Down
37 changes: 18 additions & 19 deletions src/libasr/codegen/asr_to_wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ class ASRToWASMVisitor : public ASR::BaseVisitor<ASRToWASMVisitor> {
main_func = nullptr;
avail_mem_loc = 0;

min_no_pages = 100; // fixed 6.4 Mb memory currently
max_no_pages = 100; // fixed 6.4 Mb memory currently
min_no_pages = 1000; // fixed 64 Mb memory currently
max_no_pages = 1000; // fixed 64 Mb memory currently

m_compiler_globals.resize(GLOBAL_VARS_CNT);
m_import_func_idx_map.resize(IMPORT_FUNCS_CNT);
Expand All @@ -160,10 +160,7 @@ class ASRToWASMVisitor : public ASR::BaseVisitor<ASRToWASMVisitor> {
}

void import_function(ASR::Function_t* fn) {
if (ASRUtils::get_FunctionType(fn)->m_abi != ASR::abiType::BindC) return;
if (ASRUtils::get_FunctionType(fn)->m_deftype != ASR::deftypeType::Interface) return;
if (ASRUtils::get_FunctionType(fn)->m_abi != ASR::abiType::BindC) return;
if (ASRUtils::is_intrinsic_function2(fn)) return;
if (ASRUtils::get_FunctionType(fn)->m_abi != ASR::abiType::BindJS) return;

emit_function_prototype(*fn);
m_wa.emit_import_fn("js", fn->m_name,
Expand All @@ -189,6 +186,14 @@ class ASRToWASMVisitor : public ASR::BaseVisitor<ASRToWASMVisitor> {
import_function(fn);
}
}
} else if (ASR::is_a<ASR::Module_t>(*item.second)) {
ASR::Module_t *m = ASR::down_cast<ASR::Module_t>(item.second);
for (auto &item : m->m_symtab->get_scope()) {
if (ASR::is_a<ASR::Function_t>(*item.second)) {
ASR::Function_t *fn = ASR::down_cast<ASR::Function_t>(item.second);
import_function(fn);
}
}
} else if (ASR::is_a<ASR::Function_t>(*item.second)) {
ASR::Function_t *fn = ASR::down_cast<ASR::Function_t>(item.second);
import_function(fn);
Expand Down Expand Up @@ -1152,13 +1157,12 @@ class ASRToWASMVisitor : public ASR::BaseVisitor<ASRToWASMVisitor> {
bool is_unsupported_function(const ASR::Function_t &x) {
if (strcmp(x.m_name, "_start") == 0) return false;

if (ASRUtils::get_FunctionType(x)->m_abi == ASR::abiType::BindC &&
ASRUtils::get_FunctionType(x)->m_deftype == ASR::deftypeType::Interface) {
if (ASRUtils::is_intrinsic_function2(&x)) {
diag.codegen_warning_label(
"WASM: C Intrinsic Functions not yet supported",
{x.base.base.loc}, std::string(x.m_name));
}
if (ASRUtils::get_FunctionType(x)->m_abi == ASR::abiType::BindJS) {
return true;
}

if (ASRUtils::get_FunctionType(x)->m_abi == ASR::abiType::BindC) {
// Skip C Intrinsic Functions
return true;
}
for (size_t i = 0; i < x.n_body; i++) {
Expand All @@ -1167,13 +1171,8 @@ class ASRToWASMVisitor : public ASR::BaseVisitor<ASRToWASMVisitor> {
ASR::Function_t *s = ASR::down_cast<ASR::Function_t>(
ASRUtils::symbol_get_past_external(sub_call.m_name));
if (ASRUtils::get_FunctionType(s)->m_abi == ASR::abiType::BindC &&
ASRUtils::get_FunctionType(s)->m_deftype == ASR::deftypeType::Interface &&
ASRUtils::is_intrinsic_function2(s)) {
diag.codegen_warning_label(
"WASM: Calls to C Intrinsic Functions are not yet "
"supported",
{x.m_body[i]->base.loc},
"Function: calls " + std::string(s->m_name));
// Skip functions that call into C Intrinsic Functions
return true;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4230,6 +4230,8 @@ class SymbolTableVisitor : public CommonVisitor<SymbolTableVisitor> {
} else if (name == "pythoncall" || name == "pythoncallable") {
current_procedure_abi_type = ASR::abiType::BindPython;
current_procedure_interface = (name == "pythoncall");
} else if (name == "jscall") {
current_procedure_abi_type = ASR::abiType::BindJS;
} else if (name == "overload") {
overload = true;
} else if (name == "interface") {
Expand Down
3 changes: 3 additions & 0 deletions src/lpython/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ void get_executable_path(std::string &executable_path, int &dirname_length)

std::string get_runtime_library_dir()
{
#ifdef HAVE_BUILD_TO_WASM
return "asset_dir";
#endif
char *env_p = std::getenv("LFORTRAN_RUNTIME_LIBRARY_DIR");
if (env_p) return env_p;

Expand Down
5 changes: 0 additions & 5 deletions tests/reference/asr-elemental_01-b58df26.stderr

This file was deleted.

8 changes: 0 additions & 8 deletions tests/reference/asr-modules_02-ec92e6f.stderr

This file was deleted.

8 changes: 0 additions & 8 deletions tests/reference/asr_json-modules_02-53952e6.stderr

This file was deleted.

2 changes: 1 addition & 1 deletion tests/reference/wat-bool1-234bcd1.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "wat-bool1-234bcd1.stdout",
"stdout_hash": "2ad7e7fd37dbdc380ed1fb9e4879efed1cf87dc9472196189215487b",
"stdout_hash": "1ac138f5c0fd3b21a75e6bf4e0024d5f0df2f0a6b195defd472677c2",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/wat-bool1-234bcd1.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
end
return
)
(memory (;0;) 100 100)
(memory (;0;) 1000 1000)
(export "memory" (memory 0))
(export "__main__global_stmts" (func 2))
(export "test_bool" (func 3))
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/wat-expr14-5e0cb96.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "wat-expr14-5e0cb96.stdout",
"stdout_hash": "731682ff49eaab392c46e72e575f595873b89b30309c62c75cc6e36b",
"stdout_hash": "f8ff7eb9eb4bc533fc4d36c58a0378df6dfbf135313962dbf379d69a",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/wat-expr14-5e0cb96.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
call 0
return
)
(memory (;0;) 100 100)
(memory (;0;) 1000 1000)
(export "memory" (memory 0))
(export "_start" (func 2))
(data (;0;) (i32.const 4) "\0c\00\00\00\01\00\00\00")
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/wat-expr2-8b17723.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "wat-expr2-8b17723.stdout",
"stdout_hash": "731682ff49eaab392c46e72e575f595873b89b30309c62c75cc6e36b",
"stdout_hash": "f8ff7eb9eb4bc533fc4d36c58a0378df6dfbf135313962dbf379d69a",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/wat-expr2-8b17723.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
call 0
return
)
(memory (;0;) 100 100)
(memory (;0;) 1000 1000)
(export "memory" (memory 0))
(export "_start" (func 2))
(data (;0;) (i32.const 4) "\0c\00\00\00\01\00\00\00")
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/wat-expr9-f73afd1.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "wat-expr9-f73afd1.stdout",
"stdout_hash": "24c9e7908c6c26b7b02f35db04295334fd8a45c90aa411146cf1cb16",
"stdout_hash": "169634bcbf991ebd3a4315e3824320762aea19971ac0a85026095e6c",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/wat-expr9-f73afd1.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
call 0
return
)
(memory (;0;) 100 100)
(memory (;0;) 1000 1000)
(export "memory" (memory 0))
(export "__main__global_stmts" (func 2))
(export "main0" (func 3))
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/wat-loop1-e0046d4.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "wat-loop1-e0046d4.stdout",
"stdout_hash": "b8c4cf026606e374e199425dede3419e41b30f935b6a1c71add8f508",
"stdout_hash": "186355413b04732159a4ea9cf108245721b5a41d5a6630a4f8cc7785",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/wat-loop1-e0046d4.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
call 0
return
)
(memory (;0;) 100 100)
(memory (;0;) 1000 1000)
(export "memory" (memory 0))
(export "__main__global_stmts" (func 2))
(export "main0" (func 3))
Expand Down
Loading