Thanks to visit codestin.com
Credit goes to github.com

Skip to content

WASM: Initial support for complex numbers #1527

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 13 commits into from
Feb 21, 2023

Conversation

ubaidsk
Copy link
Collaborator

@ubaidsk ubaidsk commented Feb 15, 2023

This PR adds support for complex numbers and operations on them in the wasm backend.

Fixes #1519.

@ubaidsk
Copy link
Collaborator Author

ubaidsk commented Feb 15, 2023

We were hoping to use Array approach to store a complex number. In the Array approach, we had to reset the memory at the end of the function. To maintain where a function started (from which memory location), we are needing to store the memory offset in a (new/extra) local variable. Also, we currently have support only for memory allocation at compile-time. With these constraints, supporting the array approach seems tricky.

For the moment, to have something which just works I currently used two local variables to store a complex number.

@ubaidsk
Copy link
Collaborator Author

ubaidsk commented Feb 15, 2023

Some concerns:

  • the code for emit_complex_add_32(), emit_complex_add_64(), emit_complex_sub_32(), emit_complex_sub_64(), emit_complex_mul_32() and emit_complex_mul_64() altogether seems lengthy. Is it maintainable enough?
  • the function indices have been hard-coded when calling add_c32(), add_c64(), sub_32(), sub_64(), mul_32(), mul_64().

Comment on lines 2811 to 2826
} else if (t->type == ASR::ttypeType::Complex) {
emit_call_fd_write(1, "(", 1, 0);
this->visit_expr(*x.m_values[i]);
wasm::emit_drop(m_code_section, m_al); // drop imag part
if (a_kind == 4) {
wasm::emit_f64_promote_f32(m_code_section, m_al);
}
wasm::emit_call(m_code_section, m_al, 3 /* print_f64 */);
emit_call_fd_write(1, ",", 1, 0);
this->visit_expr(*x.m_values[i]);
if (a_kind == 4) {
wasm::emit_f64_promote_f32(m_code_section, m_al);
}
wasm::emit_call(m_code_section, m_al, 3 /* print_f64 */);
emit_call_fd_write(1, ")", 1, 0);
wasm::emit_drop(m_code_section, m_al); // drop real part
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Concern:
Line 2814 & Line 2826: When printing a complex number we print the real part and then the imaginary part. It currently happens that, when we fetch a complex number on stack top, the imaginary part of the complex is at the stack top followed by the real part. Thus, we are needing to drop the top stack so that we can print the appropriate real/imag part of the complex number.

Copy link
Collaborator Author

@ubaidsk ubaidsk Feb 16, 2023

Choose a reason for hiding this comment

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

This concern is now solved/fixed in WASM: handle_print(): Use globals and fix revisiting expr.

@ubaidsk
Copy link
Collaborator Author

ubaidsk commented Feb 16, 2023

From #1527 (comment),

Concern: the function indices have been hard-coded when calling add_c32(), add_c64(), sub_32(), sub_64(), mul_32(), mul_64().

This is now solved/fixed in WASM: Refactor: Define and use m_self_func_name_idx_map.

Comment on lines 737 to 744
emit_complex_add_32();
emit_complex_add_64();
emit_complex_sub_32();
emit_complex_sub_64();
emit_complex_mul_32();
emit_complex_mul_64();
emit_complex_abs_32();
emit_complex_abs_64();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ideally, these should be emitted into wasm code only when the particular operations are being used in the source code.

Concern/Challenge: If we do not define theses functions initally and add them at the very end (that is after defining all the other functions), then we do not have any function index to refer to these functions when they are called during a call (while defining the other functions).

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 I would keep a running index of all functions and just appending these at the end as needed; then once we are done, we convert the index to the function index at the start of the WASM binary file.

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 attempted to support it in WASM: Support emitting functions based on requirement, and I'd love to get your feedback on it. Specifically, I'm wondering if this is what you had in mind, and if there are any changes you would like me to make. I'm open to any suggestions you have and would love to discuss this further with you.

@ubaidsk ubaidsk marked this pull request as ready for review February 17, 2023 20:09
@ubaidsk
Copy link
Collaborator Author

ubaidsk commented Feb 17, 2023

This is ready. Please review and share feedback.

@ubaidsk ubaidsk requested a review from certik February 17, 2023 20:11
@czgdp1807
Copy link
Collaborator

czgdp1807 commented Feb 18, 2023

  • the code for emit_complex_add_32(), emit_complex_add_64(), emit_complex_sub_32(), emit_complex_sub_64(), emit_complex_mul_32() and emit_complex_mul_64() altogether seems lengthy. Is it maintainable enough?

Can we somehow convert,

void emit_complex_add_32(int fn_idx = -1) {
        using namespace wasm;
        define_emit_func({f32, f32, f32, f32}, {f32, f32}, {}, "add_c32", [&](){
            wasm::emit_get_local(m_code_section, m_al, 0);
            wasm::emit_get_local(m_code_section, m_al, 2);
            wasm::emit_f32_add(m_code_section, m_al);

            wasm::emit_get_local(m_code_section, m_al, 1);
            wasm::emit_get_local(m_code_section, m_al, 3);
            wasm::emit_f32_add(m_code_section, m_al);
        }, fn_idx);
    }

to

(assuming type of f32, f64 is wasm::type and callback_type is say the type of wasm::emit_f32_add, wasm::emit_64_add and so on)

void emit_complex_op_util(wasm::type& ftype, std::string op_name, callback_type callback, int fn_idx = -1) {
        using namespace wasm;
        define_emit_func({ftype, ftype, ftype, ftype}, {ftype, ftype}, {}, op_name, [&](){
            wasm::emit_get_local(m_code_section, m_al, 0);
            wasm::emit_get_local(m_code_section, m_al, 2);
            callback(m_code_section, m_al);

            wasm::emit_get_local(m_code_section, m_al, 1);
            wasm::emit_get_local(m_code_section, m_al, 3);
            callback(m_code_section, m_al);
        }, fn_idx);
    }

and call it whereever needed.

@ubaidsk
Copy link
Collaborator Author

ubaidsk commented Feb 19, 2023

The callback seems to work for emit_complex_add_32 and emit_complex_add_64.

@czgdp1807 how do we use the similar callback approach in emit_complex_mul_32 or emit_complex_abs_32?

void emit_complex_mul_32(int fn_idx = -1) {
        using namespace wasm;
        define_emit_func({f32, f32, f32, f32}, {f32, f32}, {}, "mul_c32", [&](){
            wasm::emit_get_local(m_code_section, m_al, 0);
            wasm::emit_get_local(m_code_section, m_al, 2);
            wasm::emit_f32_mul(m_code_section, m_al);

            wasm::emit_get_local(m_code_section, m_al, 1);
            wasm::emit_get_local(m_code_section, m_al, 3);
            wasm::emit_f32_mul(m_code_section, m_al);

            wasm::emit_f32_sub(m_code_section, m_al);

            wasm::emit_get_local(m_code_section, m_al, 0);
            wasm::emit_get_local(m_code_section, m_al, 3);
            wasm::emit_f32_mul(m_code_section, m_al);

            wasm::emit_get_local(m_code_section, m_al, 1);
            wasm::emit_get_local(m_code_section, m_al, 2);
            wasm::emit_f32_mul(m_code_section, m_al);

            wasm::emit_f32_add(m_code_section, m_al);
        }, fn_idx);
    }

@czgdp1807
Copy link
Collaborator

how do we use the similar callback approach in emit_complex_mul_32/64 or emit_complex_abs_32/64?

I guess we also need to support division of complex numbers. So some similar refactoring can be done for emit_complex_mul_32/64, emit_complex_div_32/64. emi_complex_abs_32/64 doesn't seem to have any common logic with add, mul, div, sub, so I guess it can be written independently without calling any utility function accepting callbacks.

@ubaidsk
Copy link
Collaborator Author

ubaidsk commented Feb 19, 2023

@czgdp1807 Refactoring emit_complex_mul_32 and emit_complex_mul_64 and combining them into one common function is not very clear to me. Please, could you share an example implementation where we refactor these two functions and combine them into one, similar to the transformation of emit_complex_add_32 as you shared in #1527 (comment)?

As a later change, I wonder if we can/should keep the functions emit_complex_mul_32/64, emit_complex_div_32/64. emi_complex_abs_32/64 in a separate file (maybe with some name like wasm_rt_lib.cpp). For the moment, shall we keep these as per the current implementation?

@czgdp1807
Copy link
Collaborator

If you need it urgently then no need to refactor anything because that can be done later. If you have time to merge this, then may be figure out the patterns and create utility functions. The boilerplate code is high but that's okay. Just add TODOs before merging. I am approving it in its current state.

Copy link
Collaborator

@czgdp1807 czgdp1807 left a comment

Choose a reason for hiding this comment

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

Resolve conflicts then it should be good to go. Add TODOs for refactoring whevever you think refactoring can be done to reduce the boilerplate code. Thanks. cc: @certik

@ubaidsk
Copy link
Collaborator Author

ubaidsk commented Feb 21, 2023

If you have time to merge this, then may be figure out the patterns and create utility functions. The boilerplate code is high but that's okay.

Yes, these functions would later be implemented as ASR->ASR pass. Thus, I am unsure if we should refactor them now.

m_self_funcs_map["mul_c32"] = &ASRToWASMVisitor::emit_complex_mul_32;
m_self_funcs_map["mul_c64"] = &ASRToWASMVisitor::emit_complex_mul_64;
m_self_funcs_map["abs_c32"] = &ASRToWASMVisitor::emit_complex_abs_32;
m_self_funcs_map["abs_c64"] = &ASRToWASMVisitor::emit_complex_abs_64;
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's use enums here for performance.

Copy link
Contributor

@certik certik Feb 21, 2023

Choose a reason for hiding this comment

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

You can also then just use std::vector<bool> for all the enumerators, so it will be a very quick (constant) check.

Copy link
Contributor

Choose a reason for hiding this comment

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

This can be done in a subsequent PR.

if (a_kind == 4) {
if (m_self_func_name_idx_map.find("add_c32") == m_self_func_name_idx_map.end()) {
m_self_func_name_idx_map["add_c32"] = no_of_types++;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This just becomes a quick check, like if (m_self_complex_fn_present[wasm_add_c32]).

Copy link
Contributor

@certik certik left a comment

Choose a reason for hiding this comment

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

I think this is good to merge as is, after resolving conflicts / polishing history as needed.

In subsequent PRs we can refine.

@ubaidsk ubaidsk enabled auto-merge February 21, 2023 18:56
@ubaidsk ubaidsk merged commit 975eb1f into lcompilers:main Feb 21, 2023
@ubaidsk ubaidsk deleted the wasm_complex_nums3 branch February 21, 2023 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

WASM: complex numbers
3 participants