diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index a441164b28..9e742a7702 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -210,6 +210,9 @@ RUN(NAME expr_13 LABELS llvm c EXTRAFILES expr_13b.c) RUN(NAME expr_14 LABELS cpython llvm c) RUN(NAME loop_01 LABELS cpython llvm c) +RUN(NAME loop_02 LABELS cpython llvm wasm wasm_x86) +RUN(NAME if_01 LABELS cpython llvm wasm wasm_x86) +RUN(NAME if_02 LABELS cpython llvm wasm wasm_x86) RUN(NAME print_02 LABELS cpython llvm) RUN(NAME test_types_01 LABELS cpython llvm c) RUN(NAME test_str_01 LABELS cpython llvm c) diff --git a/integration_tests/if_01.py b/integration_tests/if_01.py index 090928c739..842cf15443 100644 --- a/integration_tests/if_01.py +++ b/integration_tests/if_01.py @@ -1,26 +1,27 @@ def Test_if_01(): + z: i32 = 0 if True: - print(1) + assert True if False: - print(0) + assert False if 1 < 0: - print(0) + assert False else: - print(1) + assert True if 1 > 0: - print(1) + assert True else: - print(0) + assert False if 1 < 0: - print(1) + assert False elif 1 > 0: - print(1) + assert True else: - print(0) + assert False def Test_if_02(): if True: @@ -28,18 +29,18 @@ def Test_if_02(): if True: print(2) if False: - print(3) + assert False elif True: print(4) else: - print(5) + assert False else: - print(6) + assert False if True: print(7) if False: - print(8) + assert False if False: print(9) else: @@ -48,7 +49,7 @@ def Test_if_02(): if True: print(11) else: - print(12) + assert False print(13) print(14) diff --git a/integration_tests/if_02.py b/integration_tests/if_02.py index e2dd52f6ff..e3a5b203b6 100644 --- a/integration_tests/if_02.py +++ b/integration_tests/if_02.py @@ -1,4 +1,4 @@ -from ltypes import bool, i32 +from ltypes import i32 def test_if_01(): x: bool = True @@ -33,8 +33,7 @@ def test_if_01(): if y >= 2: z += 1 - # TODO: replace this an assert statement - print(z) + assert z == 9 def verify(): test_if_01() diff --git a/integration_tests/loop_02.py b/integration_tests/loop_02.py new file mode 100644 index 0000000000..f54754e9da --- /dev/null +++ b/integration_tests/loop_02.py @@ -0,0 +1,84 @@ +from ltypes import i32 +def test_loop_01(): + i: i32 = 0 + j: i32 = 0 + + while False: + assert False + + while i < 0: + assert False + + while i < 10: + i += 1 + assert i == 10 + + while i < 20: + while i < 15: + i += 1 + i += 1 + assert i == 20 + + for i in range(5): + assert i == j + j += 1 + +def test_loop_02(): + i: i32 = 0 + j: i32 = 0 + + j = 0 + for i in range(10, 0, -1): + j = j + i + assert j == 55 + + for i in range(5): + if i == 3: + break + assert i == 3 + + j = 0 + for i in range(5): + if i == 3: + continue + j += 1 + assert j == 4 + +def test_loop_03(): + i: i32 = 0 + j: i32 = 0 + k: i32 = 0 + while i < 10: + j = 0 + while j < 10: + k += 1 + if i == 0: + if j == 3: + continue + else: + j += 1 + j += 1 + i += 1 + assert k == 95 + + i = 0; j = 0; k = 0 + while i < 10: + j = 0 + while j < 10: + k += i + j + if i == 5: + if j == 4: + break + else: + j += 1 + j += 1 + i += 1 + assert k == 826 + + +def verify(): + test_loop_01() + test_loop_02() + test_loop_03() + +verify() diff --git a/src/libasr/codegen/asr_to_wasm.cpp b/src/libasr/codegen/asr_to_wasm.cpp index bb38b985ed..45c10a2ccf 100644 --- a/src/libasr/codegen/asr_to_wasm.cpp +++ b/src/libasr/codegen/asr_to_wasm.cpp @@ -2032,6 +2032,7 @@ class ASRToWASMVisitor : public ASR::BaseVisitor { m_code_section, m_al, nesting_level - cur_loop_nesting_level); // emit_branch and label the loop + wasm::emit_b8(m_code_section, m_al, 0x05); // starting of else wasm::emit_expr_end(m_code_section, m_al); // end if nesting_level--; diff --git a/src/libasr/codegen/wasm_to_x86.cpp b/src/libasr/codegen/wasm_to_x86.cpp index 16a31b28be..9db9d2b4b6 100644 --- a/src/libasr/codegen/wasm_to_x86.cpp +++ b/src/libasr/codegen/wasm_to_x86.cpp @@ -32,7 +32,8 @@ class X86Visitor : public WASMDecoder, public: X86Assembler &m_a; uint32_t cur_func_idx; - std::vector unique_id; + std::vector if_unique_id; + std::vector loop_unique_id; int32_t last_vis_i32_const, last_last_vis_i32_const; std::unordered_map loc_to_str; @@ -129,23 +130,59 @@ class X86Visitor : public WASMDecoder, void visit_EmtpyBlockType() {} + void visit_Br(uint32_t label_index) { + // Branch is used to jump to the `loop.head` or `loop.end`. + if (if_unique_id.size() - loop_unique_id.size() == label_index - 1) { + // cycle/continue or loop.end + m_a.asm_jmp_label(".loop.head_" + loop_unique_id.back()); + } else { + // exit/break + m_a.asm_jmp_label(".loop.end_" + loop_unique_id.back()); + } + } + + void visit_Loop() { + loop_unique_id.push_back(std::to_string(offset)); + /* + The loop statement starts with `loop.head`. The `loop.body` and + `loop.branch` are enclosed within the `if.block`. If the condition + fails, the loop is exited through `else.block`. + .head + .If + # Statements + .Br + .Else + .endIf + .end + */ + m_a.add_label(".loop.head_" + loop_unique_id.back()); + { + decode_instructions(); + } + // end + m_a.add_label(".loop.end_" + loop_unique_id.back()); + loop_unique_id.pop_back(); + } + void visit_If() { - unique_id.push_back(std::to_string(offset)); + if_unique_id.push_back(std::to_string(offset)); + // `eax` contains the logical value (true = 1, false = 0) + // of the if condition m_a.asm_pop_r32(X86Reg::eax); m_a.asm_cmp_r32_imm8(LFortran::X86Reg::eax, 1); - m_a.asm_je_label(".then_" + unique_id.back()); - m_a.asm_jmp_label(".else_" + unique_id.back()); - m_a.add_label(".then_" + unique_id.back()); + m_a.asm_je_label(".then_" + if_unique_id.back()); + m_a.asm_jmp_label(".else_" + if_unique_id.back()); + m_a.add_label(".then_" + if_unique_id.back()); { decode_instructions(); } - m_a.add_label(".endif_" + unique_id.back()); - unique_id.pop_back(); + m_a.add_label(".endif_" + if_unique_id.back()); + if_unique_id.pop_back(); } void visit_Else() { - m_a.asm_jmp_label(".endif_" + unique_id.back()); - m_a.add_label(".else_" + unique_id.back()); + m_a.asm_jmp_label(".endif_" + if_unique_id.back()); + m_a.add_label(".else_" + if_unique_id.back()); } void visit_LocalGet(uint32_t localidx) { @@ -225,6 +262,7 @@ class X86Visitor : public WASMDecoder, std::string label = std::to_string(offset); m_a.asm_pop_r32(X86Reg::ebx); m_a.asm_pop_r32(X86Reg::eax); + // `eax` and `ebx` contain the left and right operands, respectively m_a.asm_cmp_r32_r32(X86Reg::eax, X86Reg::ebx); if (compare_op == "Eq") { m_a.asm_je_label(".compare_1" + label); @@ -241,6 +279,8 @@ class X86Visitor : public WASMDecoder, } else { throw CodeGenError("Comparison operator not implemented"); } + // if the `compare` condition in `true`, jump to compare_1 + // and assign `1` else assign `0` m_a.asm_push_imm8(0); m_a.asm_jmp_label(".compare.end_" + label); m_a.add_label(".compare_1" + label); diff --git a/tests/reference/wat-loop1-e0046d4.json b/tests/reference/wat-loop1-e0046d4.json index 2817a7f6ea..b4654f1007 100644 --- a/tests/reference/wat-loop1-e0046d4.json +++ b/tests/reference/wat-loop1-e0046d4.json @@ -6,7 +6,7 @@ "outfile": null, "outfile_hash": null, "stdout": "wat-loop1-e0046d4.stdout", - "stdout_hash": "72c18793b437f96f1f96c9f8fc8cc253601fb360929216a4de1cb2f1", + "stdout_hash": "f2518c3df21a94fba3afb27274ecfae56de7beaaaa8b54b4c1fd84fa", "stderr": null, "stderr_hash": null, "returncode": 0 diff --git a/tests/reference/wat-loop1-e0046d4.stdout b/tests/reference/wat-loop1-e0046d4.stdout index 427c30176d..2df01b900f 100644 --- a/tests/reference/wat-loop1-e0046d4.stdout +++ b/tests/reference/wat-loop1-e0046d4.stdout @@ -48,6 +48,7 @@ i32.sub local.set 0 br 1 + else end end local.get 2 @@ -83,6 +84,7 @@ i32.mul local.set 3 br 1 + else end end local.get 3 @@ -121,6 +123,7 @@ i32.sub local.set 0 br 1 + else end end local.get 2