-
Notifications
You must be signed in to change notification settings - Fork 171
Implement if-else in wasm_to_x86 backend #1264
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
Conversation
src/libasr/codegen/wasm_to_x86.cpp
Outdated
while (cur_byte != 0x0B) { | ||
if (cur_byte == 0x05) { | ||
cur_byte = wasm::read_b8(code, offset_); | ||
if (cur_byte == 0x0B || cur_byte == 0x41) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a doubt:
Does wasm
always have an else block (even though else is empty) in the if statement?
@Shaikh-Ubaid
If so, how Assert
is handled in asr_to_wasm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I asked this because, the else block is not empty in the visit_assert()
, it has 0x41
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does wasm always have an else block (even though else is empty) in the if statement?
Currently, if there is no else
block, then in the wat
generated, we would just have if
<some instructions>
end
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I asked this because, the else block is not empty in the visit_assert(), it has 0x41
Do you mean visit_Assert()
in ASR->WASM
backend?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah, After the wasm code is generated and captured in the wasm->x86
src/libasr/codegen/wasm_to_x86.cpp
Outdated
while (cur_byte != 0x0B) { | ||
if (cur_byte == 0x05) { | ||
cur_byte = wasm::read_b8(code, offset_); | ||
if (cur_byte == 0x0B || cur_byte == 0x41) { | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
} | ||
cur_byte = wasm::read_b8(code, offset_); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this logic, detects the else block, even for the following syntax!!
if True:
pass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is the reason, I check the next byte of the visit_else
to be 0x0B (which checks the end of the if stmt).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At present, if there is no else
block, then visit_Else()
should not be visited. I guess, we might be missing something.
d494fb3
to
7b8e83f
Compare
I looked into the If an |
I will rebase this on top of #1262 (so that we can print some value inside the |
This looks great! Thanks for updating. |
Let's implement visit_Assert() in this PR itself. def Main0():
assert 1 == 1
Main0() Output: $ lpython examples/expr2.py --backend wasm_x86 && ./a.out
AssertionError def Main0():
assert 0 == 1
Main0() $ lpython examples/expr2.py --backend wasm_x86 && ./a.out |
src/libasr/codegen/asr_to_wasm.cpp
Outdated
void visit_Assert(const ASR::Assert_t &x) { | ||
this->visit_expr(*x.m_test); | ||
wasm::emit_i32_eqz(m_code_section, m_al); | ||
wasm::emit_b8(m_code_section, m_al, 0x04); // emit if start | ||
wasm::emit_b8(m_code_section, m_al, 0x40); // empty block type | ||
wasm::emit_b8(m_code_section, m_al, 0x05); // starting of else | ||
if (x.m_msg) { | ||
std::string msg = | ||
ASR::down_cast<ASR::StringConstant_t>(x.m_msg)->m_s; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous version was more correct, right? The previous version would say -> If Assert
condition failed, raise an error (which is what assert
does, I think).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main motive for these changes was that visit_I32Eqz()
in the wasm_to_x86
is not working correctly.
Here, the changes imply:
If True:
// Do nothing
else:
print("AssertionError")
quit(1)
which is similar to assert False
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same approach as we do in the LLVM backend:
lpython/src/libasr/codegen/asr_to_llvm.cpp
Lines 4845 to 4862 in d4a6d53
void visit_Assert(const ASR::Assert_t &x) { | |
this->visit_expr_wrapper(x.m_test, true); | |
create_if_else(tmp, []() {}, [=]() { | |
if (x.m_msg) { | |
char* s = ASR::down_cast<ASR::StringConstant_t>(x.m_msg)->m_s; | |
llvm::Value *fmt_ptr = builder->CreateGlobalStringPtr("AssertionError: %s\n"); | |
llvm::Value *fmt_ptr2 = builder->CreateGlobalStringPtr(s); | |
print_error(context, *module, *builder, {fmt_ptr, fmt_ptr2}); | |
} else { | |
llvm::Value *fmt_ptr = builder->CreateGlobalStringPtr("AssertionError\n"); | |
print_error(context, *module, *builder, {fmt_ptr}); | |
} | |
int exit_code_int = 1; | |
llvm::Value *exit_code = llvm::ConstantInt::get(context, | |
llvm::APInt(32, exit_code_int)); | |
exit(context, *module, *builder, exit_code); | |
}); | |
} |
src/libasr/codegen/asr_to_wasm.cpp
Outdated
} | ||
wasm::emit_i32_const(m_code_section, m_al, 1); // non-zero exit code | ||
exit(); | ||
wasm::emit_b8(m_code_section, m_al, 0x05); // starting of else | ||
wasm::emit_expr_end(m_code_section, m_al); // emit if end | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The else
portion was empty in the previous version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The right way to support Assert
would instead be to implement the visit_I32Eqz
in the WASM->X86
backend.
void visit_I32Eqz() {
m_a.asm_pop_r32(X86Reg::eax);
m_a.asm_mov_r32_imm32(X86Reg::ebx, 0U);
m_a.asm_cmp_r32_r32(X86Reg::eax, X86Reg::ebx);
m_a.asm_push_r32(X86Reg::eax);
}
Thanks @Thirumalai-Shaktivel for working on this, very helpful. Thanks @Shaikh-Ubaid for the review. |
This seems not working in the wasm_x86 backend. |
What does I32Eqz do? isn't it a NOT operator? |
It checks if the stack top is equal to zero. Yes, it works similar to a not operator.
If it is similar to the
It should (hopefully) work. I tested it locally on my system before sharing here. If we are following the |
Were you able to get "AssertionError" for |
For the code: def Test_assert_01():
if True:
assert True
assert 1 != 1, "One is equal to one."
def Verify():
Test_assert_01()
Verify() I get: $ lpython examples/expr2.py --backend wasm_x86 && ./a.out
Call to imported function with index 4 not yet supported
Call to imported function with index 4 not yet supported |
Sorry my bad, |
Let's wait for the |
Yup! Now, I feel the same. |
6e47f8e
to
4093db1
Compare
Thank you for your approval and guidance! |
@@ -16,6 +16,7 @@ class X86Visitor : public WASMDecoder<X86Visitor>, | |||
public: | |||
X86Assembler &m_a; | |||
uint32_t cur_func_idx; | |||
std::vector<uint32_t> unique_id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Later:
std::vector<uint32_t> unique_id; | |
uint32_t unique_id; |
void visit_EmtpyBlockType() {} | ||
|
||
void visit_If() { | ||
unique_id.push_back(offset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unique_id.push_back(offset); | |
unique_id++; | |
uint32_t label = unique_id; |
m_a.asm_cmp_r32_imm8(LFortran::X86Reg::eax, 1); | ||
m_a.asm_je_label(".then_" + std::to_string(unique_id.back())); | ||
m_a.asm_jmp_label(".else_" + std::to_string(unique_id.back())); | ||
m_a.add_label(".then_" + std::to_string(unique_id.back())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For later (but probably quite soon): we should not be using strings for labels, but just unique integers. The unique integer can just be a counter in this visitor, just an integer, see above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(we might need to have a stack of unique ids...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, thanks!
Related #1222