From e49eaa03f243e25eee0578611a2c890b7ae08441 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Fri, 30 Sep 2022 08:09:19 +0530 Subject: [PATCH 1/8] Supported goto in CPython by re-using python-goto project The source code of src/runtime/ltypes/goto.py is coming from https://github.com/snoack/python-goto/blob/acbe736221d2238df3d09beab457d0bb19d05812/goto.py. The license of the python-goto project at the time of inclusion in LPython is available at https://github.com/snoack/python-goto/blob/acbe736221d2238df3d09beab457d0bb19d05812/LICENSE. --- src/runtime/ltypes/goto.py | 241 +++++++++++++++++++++++++++++++++++ src/runtime/ltypes/ltypes.py | 3 +- 2 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 src/runtime/ltypes/goto.py diff --git a/src/runtime/ltypes/goto.py b/src/runtime/ltypes/goto.py new file mode 100644 index 0000000000..55c10759b0 --- /dev/null +++ b/src/runtime/ltypes/goto.py @@ -0,0 +1,241 @@ +""" +The source code of this file is coming from +https://github.com/snoack/python-goto/blob/acbe736221d2238df3d09beab457d0bb19d05812/goto.py. +The license of the python-goto project at the time of inclusion in LPython is available at +https://github.com/snoack/python-goto/blob/acbe736221d2238df3d09beab457d0bb19d05812/LICENSE. +""" +import dis +import struct +import array +import types +import functools + + +try: + _array_to_bytes = array.array.tobytes +except AttributeError: + _array_to_bytes = array.array.tostring + + +class _Bytecode: + def __init__(self): + code = (lambda: x if x else y).__code__.co_code + opcode, oparg = struct.unpack_from('BB', code, 2) + + # Starting with Python 3.6, the bytecode format has changed, using + # 16-bit words (8-bit opcode + 8-bit argument) for each instruction, + # as opposed to previously 24 bit (8-bit opcode + 16-bit argument) + # for instructions that expect an argument and otherwise 8 bit. + # https://bugs.python.org/issue26647 + if dis.opname[opcode] == 'POP_JUMP_IF_FALSE': + self.argument = struct.Struct('B') + self.have_argument = 0 + # As of Python 3.6, jump targets are still addressed by their + # byte unit. This is matter to change, so that jump targets, + # in the future might refer to code units (address in bytes / 2). + # https://bugs.python.org/issue26647 + self.jump_unit = 8 // oparg + else: + self.argument = struct.Struct('= _BYTECODE.have_argument: + oparg = extended_arg | _BYTECODE.argument.unpack_from(code, pos)[0] + pos += _BYTECODE.argument.size + + if opcode == dis.EXTENDED_ARG: + extended_arg = oparg << _BYTECODE.argument_bits + extended_arg_offset = offset + continue + + extended_arg = 0 + extended_arg_offset = None + yield (dis.opname[opcode], oparg, offset) + + +def _get_instruction_size(opname, oparg=0): + size = 1 + + extended_arg = oparg >> _BYTECODE.argument_bits + if extended_arg != 0: + size += _get_instruction_size('EXTENDED_ARG', extended_arg) + oparg &= (1 << _BYTECODE.argument_bits) - 1 + + opcode = dis.opmap[opname] + if opcode >= _BYTECODE.have_argument: + size += _BYTECODE.argument.size + + return size + + +def _get_instructions_size(ops): + size = 0 + for op in ops: + if isinstance(op, str): + size += _get_instruction_size(op) + else: + size += _get_instruction_size(*op) + return size + + +def _write_instruction(buf, pos, opname, oparg=0): + extended_arg = oparg >> _BYTECODE.argument_bits + if extended_arg != 0: + pos = _write_instruction(buf, pos, 'EXTENDED_ARG', extended_arg) + oparg &= (1 << _BYTECODE.argument_bits) - 1 + + opcode = dis.opmap[opname] + buf[pos] = opcode + pos += 1 + + if opcode >= _BYTECODE.have_argument: + _BYTECODE.argument.pack_into(buf, pos, oparg) + pos += _BYTECODE.argument.size + + return pos + + +def _write_instructions(buf, pos, ops): + for op in ops: + if isinstance(op, str): + pos = _write_instruction(buf, pos, op) + else: + pos = _write_instruction(buf, pos, *op) + return pos + + +def _find_labels_and_gotos(code): + labels = {} + gotos = [] + + block_stack = [] + block_counter = 0 + + opname1 = oparg1 = offset1 = None + opname2 = oparg2 = offset2 = None + opname3 = oparg3 = offset3 = None + + for opname4, oparg4, offset4 in _parse_instructions(code.co_code): + if opname1 in ('LOAD_GLOBAL', 'LOAD_NAME'): + if opname2 == 'LOAD_ATTR' and opname3 == 'POP_TOP': + name = code.co_names[oparg1] + if name == 'label': + if oparg2 in labels: + raise SyntaxError('Ambiguous label {0!r}'.format( + code.co_names[oparg2] + )) + labels[oparg2] = (offset1, + offset4, + tuple(block_stack)) + elif name == 'goto': + gotos.append((offset1, + offset4, + oparg2, + tuple(block_stack))) + elif opname1 in ('SETUP_LOOP', + 'SETUP_EXCEPT', 'SETUP_FINALLY', + 'SETUP_WITH', 'SETUP_ASYNC_WITH'): + block_counter += 1 + block_stack.append(block_counter) + elif opname1 == 'POP_BLOCK' and block_stack: + block_stack.pop() + + opname1, oparg1, offset1 = opname2, oparg2, offset2 + opname2, oparg2, offset2 = opname3, oparg3, offset3 + opname3, oparg3, offset3 = opname4, oparg4, offset4 + + return labels, gotos + + +def _inject_nop_sled(buf, pos, end): + while pos < end: + pos = _write_instruction(buf, pos, 'NOP') + + +def _patch_code(code): + labels, gotos = _find_labels_and_gotos(code) + buf = array.array('B', code.co_code) + + for pos, end, _ in labels.values(): + _inject_nop_sled(buf, pos, end) + + for pos, end, label, origin_stack in gotos: + try: + _, target, target_stack = labels[label] + except KeyError: + raise SyntaxError('Unknown label {0!r}'.format( + code.co_names[label] + )) + + target_depth = len(target_stack) + if origin_stack[:target_depth] != target_stack: + raise SyntaxError('Jump into different block') + + ops = [] + for i in range(len(origin_stack) - target_depth): + ops.append('POP_BLOCK') + ops.append(('JUMP_ABSOLUTE', target // _BYTECODE.jump_unit)) + + if pos + _get_instructions_size(ops) > end: + # not enough space, add code at buffer end and jump there + buf_end = len(buf) + + go_to_end_ops = [('JUMP_ABSOLUTE', buf_end // _BYTECODE.jump_unit)] + + if pos + _get_instructions_size(go_to_end_ops) > end: + # not sure if reachable + raise SyntaxError('Goto in an incredibly huge function') + + pos = _write_instructions(buf, pos, go_to_end_ops) + _inject_nop_sled(buf, pos, end) + + buf.extend([0] * _get_instructions_size(ops)) + _write_instructions(buf, buf_end, ops) + else: + pos = _write_instructions(buf, pos, ops) + _inject_nop_sled(buf, pos, end) + + return _make_code(code, _array_to_bytes(buf)) + + +def with_goto(func_or_code): + if isinstance(func_or_code, types.CodeType): + return _patch_code(func_or_code) + + return functools.update_wrapper( + types.FunctionType( + _patch_code(func_or_code.__code__), + func_or_code.__globals__, + func_or_code.__name__, + func_or_code.__defaults__, + func_or_code.__closure__, + ), + func_or_code + ) diff --git a/src/runtime/ltypes/ltypes.py b/src/runtime/ltypes/ltypes.py index 4b9d3cd387..e53b08fe08 100644 --- a/src/runtime/ltypes/ltypes.py +++ b/src/runtime/ltypes/ltypes.py @@ -3,11 +3,12 @@ import ctypes import platform from dataclasses import dataclass +from goto import with_goto # TODO: this does not seem to restrict other imports __slots__ = ["i8", "i16", "i32", "i64", "f32", "f64", "c32", "c64", "CPtr", "overload", "ccall", "TypeVar", "pointer", "c_p_pointer", "Pointer", - "p_c_pointer", "vectorize", "inline", "Union", "static"] + "p_c_pointer", "vectorize", "inline", "Union", "static", "with_goto"] # data-types From cc9bf94be5a964d30e373e244f3880a71f651f45 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Fri, 30 Sep 2022 08:11:25 +0530 Subject: [PATCH 2/8] Added test cases for goto statement Co-authored-by: Ron Nuriel --- integration_tests/test_01_goto.py | 35 +++++++++++++++++++++++++++++++ tests/errors/test_goto.py | 12 +++++++++++ 2 files changed, 47 insertions(+) create mode 100644 integration_tests/test_01_goto.py create mode 100644 tests/errors/test_goto.py diff --git a/integration_tests/test_01_goto.py b/integration_tests/test_01_goto.py new file mode 100644 index 0000000000..2857c01768 --- /dev/null +++ b/integration_tests/test_01_goto.py @@ -0,0 +1,35 @@ +from ltypes import with_goto, i32 + +@with_goto +def f() -> i32: + i:i32 + for i in range(10): + if i == 5: + goto .end + + label .end + assert i == 5 + return i + +@with_goto +def g(size: i32) -> i32: + i:i32 + + i = 0 + label .loop + if i >= size: + goto .end + i += 1 + goto .loop + + label .end + return i + +def test_goto(): + print(f()) + print(g(10)) + print(g(20)) + assert g(30) == 30 + assert g(40) == 40 + +test_goto() diff --git a/tests/errors/test_goto.py b/tests/errors/test_goto.py new file mode 100644 index 0000000000..5c027d350c --- /dev/null +++ b/tests/errors/test_goto.py @@ -0,0 +1,12 @@ +from ltypes import with_goto, goto, label, i32 + +@with_goto +def f(): + i:i32 + for i in range(10): + if i == 5: + goto .end + + assert i == 5 + +f() From a0991ec616379528003472925b15a294df4f2c6d Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Fri, 30 Sep 2022 08:12:32 +0530 Subject: [PATCH 3/8] Transform goto and label in AST to GoTo and GoToTarget in ASR --- src/lpython/semantics/python_ast_to_asr.cpp | 57 +++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index e654d4ca99..38af499f03 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -2843,7 +2844,6 @@ class SymbolTableVisitor : public CommonVisitor { bool current_procedure_interface = false; bool overload = false; bool vectorize = false, is_inline = false, is_static = false; - Vec tps; tps.reserve(al, x.m_args.n_args); bool is_restriction = false; @@ -2866,6 +2866,8 @@ class SymbolTableVisitor : public CommonVisitor { vectorize = true; } else if (name == "restriction") { is_restriction = true; + } else if (name == "with_goto") { + // TODO: Use goto attribute in function? } else if (name == "inline") { is_inline = true; } else if (name == "static") { @@ -3211,11 +3213,14 @@ class BodyVisitor : public CommonVisitor { public: ASR::asr_t *asr; + std::map> goto_name2id; + int64_t gotoids; BodyVisitor(Allocator &al, ASR::asr_t *unit, diag::Diagnostics &diagnostics, bool main_module, std::map &ast_overload) - : CommonVisitor(al, nullptr, diagnostics, main_module, ast_overload, ""), asr{unit} + : CommonVisitor(al, nullptr, diagnostics, main_module, ast_overload, ""), asr{unit}, + gotoids{0} {} // Transforms statements to a list of ASR statements @@ -3300,6 +3305,8 @@ class BodyVisitor : public CommonVisitor { } void visit_FunctionDef(const AST::FunctionDef_t &x) { + goto_name2id.clear(); + gotoids = 0; SymbolTable *old_scope = current_scope; ASR::symbol_t *t = current_scope->get_symbol(x.m_name); if (ASR::is_a(*t)) { @@ -3320,6 +3327,13 @@ class BodyVisitor : public CommonVisitor { } current_scope = old_scope; tmp = nullptr; + + for( auto itr: goto_name2id ) { + if( !std::get<1>(itr.second) ) { + throw SemanticError("Label '" + itr.first + "' is not defined in '" + + std::string(x.m_name) + "'", std::get<2>(itr.second)); + } + } } void visit_Import(const AST::Import_t &/*x*/) { @@ -3969,7 +3983,36 @@ class BodyVisitor : public CommonVisitor { void visit_Attribute(const AST::Attribute_t &x) { if (AST::is_a(*x.m_value)) { std::string value = AST::down_cast(x.m_value)->m_id; + if( value == "label" ) { + std::string labelname = x.m_attr; + if( goto_name2id.find(labelname) == goto_name2id.end() ) { + goto_name2id[labelname] = std::make_tuple(gotoids, true, x.base.base.loc); + gotoids += 1; + } else if( !std::get<1>(goto_name2id[labelname]) ) { + goto_name2id[labelname] = std::make_tuple( + std::get<0>(goto_name2id[labelname]), + true, + std::get<2>(goto_name2id[labelname]) + ); + } + int id = std::get<0>(goto_name2id[labelname]); + tmp = ASR::make_GoToTarget_t(al, x.base.base.loc, id, x.m_attr); + return ; + } + + if (value == "goto"){ + std::string labelname = std::string(x.m_attr); + if( goto_name2id.find(labelname) == goto_name2id.end() ) { + goto_name2id[labelname] = std::make_tuple(gotoids, false, x.base.base.loc); + gotoids += 1; + } + int id = std::get<0>(goto_name2id[labelname]); + tmp = ASR::make_GoTo_t(al, x.base.base.loc, id, x.m_attr); + return ; + } + ASR::symbol_t *t = current_scope->resolve_symbol(value); + if (!t) { throw SemanticError("'" + value + "' is not defined in the scope", x.base.base.loc); @@ -4543,8 +4586,14 @@ class BodyVisitor : public CommonVisitor { return; } this->visit_expr(*x.m_value); - ASRUtils::EXPR(tmp); - tmp = nullptr; + + // If tmp is a statement and not an expression + // never cast into expression using ASRUtils::EXPR + // Just ignore and exit the function naturally. + if( !ASR::is_a(*tmp) ) { + LFORTRAN_ASSERT(ASR::is_a(*tmp)); + tmp = nullptr; + } } From 9e938f7350d35a24006cec8077c871028259bb90 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Fri, 30 Sep 2022 08:12:57 +0530 Subject: [PATCH 4/8] Support GoTo and GoToTarget in C backend Co-authored-by: Ron Nuriel --- src/libasr/codegen/asr_to_c.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libasr/codegen/asr_to_c.cpp b/src/libasr/codegen/asr_to_c.cpp index b63c6ee534..521303bff0 100644 --- a/src/libasr/codegen/asr_to_c.cpp +++ b/src/libasr/codegen/asr_to_c.cpp @@ -1126,6 +1126,14 @@ R"( src = "strlen(" + src + ")"; } + void visit_GoTo(const ASR::GoTo_t &x) { + std::string indent(indentation_level*indentation_spaces, ' '); + src = indent + "goto " + std::string(x.m_name) + ";\n"; + } + + void visit_GoToTarget(const ASR::GoToTarget_t &x) { + src = std::string(x.m_name) + ":\n"; + } }; Result asr_to_c(Allocator &al, ASR::TranslationUnit_t &asr, From fa57b76b5e239453a22922ff1ec89bd95adc422b Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Fri, 30 Sep 2022 08:13:29 +0530 Subject: [PATCH 5/8] Add name of the label in GoTo and GoToTarget --- src/libasr/ASR.asdl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libasr/ASR.asdl b/src/libasr/ASR.asdl index d0996a8de9..a7721a7a79 100644 --- a/src/libasr/ASR.asdl +++ b/src/libasr/ASR.asdl @@ -173,10 +173,10 @@ stmt -- GoTo points to a GoToTarget with the corresponding target_id within -- the same procedure. We currently use `int` IDs to link GoTo with -- GoToTarget to avoid issues with serialization. - | GoTo(int target_id) + | GoTo(int target_id, identifier name) -- An empty statement, a target of zero or more GoTo statements -- the `id` is only unique within a procedure - | GoToTarget(int id) + | GoToTarget(int id, identifier name) | If(expr test, stmt* body, stmt* orelse) | IfArithmetic(expr test, int lt_label, int eq_label, int gt_label) | Print(expr? fmt, expr* values, expr? separator, expr? end) From 3f8111a7e704ea164506096707691c0d7a36fe5c Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Fri, 30 Sep 2022 08:16:40 +0530 Subject: [PATCH 6/8] Update call to make_GoTo_t constructor --- src/libasr/asr_utils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libasr/asr_utils.h b/src/libasr/asr_utils.h index 7f724b51db..f1bee5206d 100644 --- a/src/libasr/asr_utils.h +++ b/src/libasr/asr_utils.h @@ -1746,7 +1746,8 @@ class ReplaceReturnWithGotoVisitor: public ASR::BaseStmtReplacerbase.base.loc, goto_label)); + *current_stmt = ASRUtils::STMT(ASR::make_GoTo_t(al, x->base.base.loc, goto_label, + s2c(al, "__" + std::to_string(goto_label)))); has_replacement_happened = true; } From 3a5f0f51ce83fcffdb2508500fd75f394f618379 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Fri, 30 Sep 2022 08:17:06 +0530 Subject: [PATCH 7/8] Register tests for goto statement --- integration_tests/CMakeLists.txt | 1 + tests/tests.toml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 22460d6263..34a2163c2a 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -275,6 +275,7 @@ RUN(NAME generics_list_01 LABELS cpython llvm) RUN(NAME test_statistics LABELS cpython llvm) RUN(NAME test_str_attributes LABELS cpython llvm) RUN(NAME kwargs_01 LABELS cpython llvm) +RUN(NAME test_01_goto LABELS cpython llvm c) RUN(NAME func_inline_01 LABELS llvm wasm) RUN(NAME func_static_01 LABELS cpython llvm c wasm) diff --git a/tests/tests.toml b/tests/tests.toml index 842664554c..d1ae0ab8d3 100644 --- a/tests/tests.toml +++ b/tests/tests.toml @@ -906,3 +906,7 @@ asr = true [[test]] filename = "errors/enum_02.py" asr = true + +[[test]] +filename = "errors/test_goto.py" +asr = true From d5b21d6d23ee53abaec0bff8d2053a4dc5465870 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Fri, 30 Sep 2022 08:17:50 +0530 Subject: [PATCH 8/8] Updated reference tests --- tests/reference/asr-test_goto-ba9fd22.json | 13 +++++++++++++ tests/reference/asr-test_goto-ba9fd22.stderr | 5 +++++ ...nline_function_calls-func_inline_01-8b6a5da.json | 2 +- ...ine_function_calls-func_inline_01-8b6a5da.stdout | 2 +- 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 tests/reference/asr-test_goto-ba9fd22.json create mode 100644 tests/reference/asr-test_goto-ba9fd22.stderr diff --git a/tests/reference/asr-test_goto-ba9fd22.json b/tests/reference/asr-test_goto-ba9fd22.json new file mode 100644 index 0000000000..98b0907e07 --- /dev/null +++ b/tests/reference/asr-test_goto-ba9fd22.json @@ -0,0 +1,13 @@ +{ + "basename": "asr-test_goto-ba9fd22", + "cmd": "lpython --show-asr --no-color {infile} -o {outfile}", + "infile": "tests/errors/test_goto.py", + "infile_hash": "8b81c2245b3ca31576ac41f49247b3781d1759ac6be61e8512bfd0f1", + "outfile": null, + "outfile_hash": null, + "stdout": null, + "stdout_hash": null, + "stderr": "asr-test_goto-ba9fd22.stderr", + "stderr_hash": "a1e26c1edcd8784938199af965004496663f071968ff7d58a33be725", + "returncode": 2 +} \ No newline at end of file diff --git a/tests/reference/asr-test_goto-ba9fd22.stderr b/tests/reference/asr-test_goto-ba9fd22.stderr new file mode 100644 index 0000000000..019db15d63 --- /dev/null +++ b/tests/reference/asr-test_goto-ba9fd22.stderr @@ -0,0 +1,5 @@ +semantic error: Label 'end' is not defined in 'f' + --> tests/errors/test_goto.py:8:13 + | +8 | goto .end + | ^^^^^^^^^ diff --git a/tests/reference/pass_inline_function_calls-func_inline_01-8b6a5da.json b/tests/reference/pass_inline_function_calls-func_inline_01-8b6a5da.json index 2bd4432391..730941c2bb 100644 --- a/tests/reference/pass_inline_function_calls-func_inline_01-8b6a5da.json +++ b/tests/reference/pass_inline_function_calls-func_inline_01-8b6a5da.json @@ -6,7 +6,7 @@ "outfile": null, "outfile_hash": null, "stdout": "pass_inline_function_calls-func_inline_01-8b6a5da.stdout", - "stdout_hash": "5043cad0aef3a2dec485b2a7198bcb250b6067d9f3d2de4b069c25e9", + "stdout_hash": "16e8a10b4b75d9783a31f403f0784fe3c66ae38ed584f6485d82b6ff", "stderr": null, "stderr_hash": null, "returncode": 0 diff --git a/tests/reference/pass_inline_function_calls-func_inline_01-8b6a5da.stdout b/tests/reference/pass_inline_function_calls-func_inline_01-8b6a5da.stdout index 3bdc8edb9c..ee3cc89838 100644 --- a/tests/reference/pass_inline_function_calls-func_inline_01-8b6a5da.stdout +++ b/tests/reference/pass_inline_function_calls-func_inline_01-8b6a5da.stdout @@ -1 +1 @@ -(TranslationUnit (SymbolTable 1 {_lpython_main_program: (Function (SymbolTable 5 {}) _lpython_main_program [] [(SubroutineCall 1 main () [] ())] () Source Public Implementation () .false. .false. .false. .false. .false. [] [] .false.), fib: (Function (SymbolTable 2 {_lpython_return_variable: (Variable 2 _lpython_return_variable ReturnVar () () Default (Integer 8 []) Source Public Required .false.), n: (Variable 2 n In () () Default (Integer 8 []) Source Public Required .false.)}) fib [(Var 2 n)] [(If (IntegerCompare (Var 2 n) Lt (Cast (IntegerConstant 2 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 2 (Integer 8 []))) (Logical 4 []) ()) [(= (Var 2 _lpython_return_variable) (Var 2 n) ()) (Return)] []) (= (Var 2 _lpython_return_variable) (IntegerBinOp (FunctionCall 1 fib () [((IntegerBinOp (Var 2 n) Sub (Cast (IntegerConstant 1 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 1 (Integer 8 []))) (Integer 8 []) ()))] (Integer 8 []) () ()) Add (FunctionCall 1 fib () [((IntegerBinOp (Var 2 n) Sub (Cast (IntegerConstant 2 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 2 (Integer 8 []))) (Integer 8 []) ()))] (Integer 8 []) () ()) (Integer 8 []) ()) ()) (Return)] (Var 2 _lpython_return_variable) Source Public Implementation () .false. .false. .false. .false. .false. [] [] .false.), main: (Function (SymbolTable 3 {_lpython_return_variable_fib: (Variable 3 _lpython_return_variable_fib Local () () Default (Integer 8 []) Source Public Required .false.), ans: (Variable 3 ans Local () () Default (Integer 8 []) Source Public Required .false.), n_fib: (Variable 3 n_fib Local () () Default (Integer 8 []) Source Public Required .false.), x: (Variable 3 x Local () () Default (Integer 8 []) Source Public Required .false.), ~empty_block: (Block (SymbolTable 6 {}) ~empty_block [])}) main [] [(= (Var 3 x) (Cast (IntegerConstant 40 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 40 (Integer 8 []))) ()) (= (Var 3 n_fib) (Var 3 x) ()) (If (IntegerCompare (Var 3 n_fib) Lt (Cast (IntegerConstant 2 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 2 (Integer 8 []))) (Logical 4 []) ()) [(= (Var 3 _lpython_return_variable_fib) (Var 3 n_fib) ()) (GoTo 1)] []) (= (Var 3 _lpython_return_variable_fib) (IntegerBinOp (FunctionCall 1 fib () [((IntegerBinOp (Var 3 n_fib) Sub (Cast (IntegerConstant 1 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 1 (Integer 8 []))) (Integer 8 []) ()))] (Integer 8 []) () ()) Add (FunctionCall 1 fib () [((IntegerBinOp (Var 3 n_fib) Sub (Cast (IntegerConstant 2 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 2 (Integer 8 []))) (Integer 8 []) ()))] (Integer 8 []) () ()) (Integer 8 []) ()) ()) (GoTo 1) (BlockCall 1 3 ~empty_block) (= (Var 3 ans) (Var 3 _lpython_return_variable_fib) ()) (Print () [(Var 3 ans)] () ()) (Assert (IntegerCompare (Var 3 ans) Eq (Cast (IntegerConstant 102334155 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 102334155 (Integer 8 []))) (Logical 4 []) ()) ())] () Source Public Implementation () .false. .false. .false. .false. .false. [] [] .false.), main_program: (Program (SymbolTable 4 {}) main_program [] [(SubroutineCall 1 _lpython_main_program () [] ())])}) []) +(TranslationUnit (SymbolTable 1 {_lpython_main_program: (Function (SymbolTable 5 {}) _lpython_main_program [] [(SubroutineCall 1 main () [] ())] () Source Public Implementation () .false. .false. .false. .false. .false. [] [] .false.), fib: (Function (SymbolTable 2 {_lpython_return_variable: (Variable 2 _lpython_return_variable ReturnVar () () Default (Integer 8 []) Source Public Required .false.), n: (Variable 2 n In () () Default (Integer 8 []) Source Public Required .false.)}) fib [(Var 2 n)] [(If (IntegerCompare (Var 2 n) Lt (Cast (IntegerConstant 2 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 2 (Integer 8 []))) (Logical 4 []) ()) [(= (Var 2 _lpython_return_variable) (Var 2 n) ()) (Return)] []) (= (Var 2 _lpython_return_variable) (IntegerBinOp (FunctionCall 1 fib () [((IntegerBinOp (Var 2 n) Sub (Cast (IntegerConstant 1 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 1 (Integer 8 []))) (Integer 8 []) ()))] (Integer 8 []) () ()) Add (FunctionCall 1 fib () [((IntegerBinOp (Var 2 n) Sub (Cast (IntegerConstant 2 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 2 (Integer 8 []))) (Integer 8 []) ()))] (Integer 8 []) () ()) (Integer 8 []) ()) ()) (Return)] (Var 2 _lpython_return_variable) Source Public Implementation () .false. .false. .false. .false. .false. [] [] .false.), main: (Function (SymbolTable 3 {_lpython_return_variable_fib: (Variable 3 _lpython_return_variable_fib Local () () Default (Integer 8 []) Source Public Required .false.), ans: (Variable 3 ans Local () () Default (Integer 8 []) Source Public Required .false.), n_fib: (Variable 3 n_fib Local () () Default (Integer 8 []) Source Public Required .false.), x: (Variable 3 x Local () () Default (Integer 8 []) Source Public Required .false.), ~empty_block: (Block (SymbolTable 6 {}) ~empty_block [])}) main [] [(= (Var 3 x) (Cast (IntegerConstant 40 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 40 (Integer 8 []))) ()) (= (Var 3 n_fib) (Var 3 x) ()) (If (IntegerCompare (Var 3 n_fib) Lt (Cast (IntegerConstant 2 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 2 (Integer 8 []))) (Logical 4 []) ()) [(= (Var 3 _lpython_return_variable_fib) (Var 3 n_fib) ()) (GoTo 1 __1)] []) (= (Var 3 _lpython_return_variable_fib) (IntegerBinOp (FunctionCall 1 fib () [((IntegerBinOp (Var 3 n_fib) Sub (Cast (IntegerConstant 1 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 1 (Integer 8 []))) (Integer 8 []) ()))] (Integer 8 []) () ()) Add (FunctionCall 1 fib () [((IntegerBinOp (Var 3 n_fib) Sub (Cast (IntegerConstant 2 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 2 (Integer 8 []))) (Integer 8 []) ()))] (Integer 8 []) () ()) (Integer 8 []) ()) ()) (GoTo 1 __1) (BlockCall 1 3 ~empty_block) (= (Var 3 ans) (Var 3 _lpython_return_variable_fib) ()) (Print () [(Var 3 ans)] () ()) (Assert (IntegerCompare (Var 3 ans) Eq (Cast (IntegerConstant 102334155 (Integer 4 [])) IntegerToInteger (Integer 8 []) (IntegerConstant 102334155 (Integer 8 []))) (Logical 4 []) ()) ())] () Source Public Implementation () .false. .false. .false. .false. .false. [] [] .false.), main_program: (Program (SymbolTable 4 {}) main_program [] [(SubroutineCall 1 _lpython_main_program () [] ())])}) [])