Skip to content
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

Fix bad logical-and implementation #131

Merged
merged 2 commits into from
Jul 7, 2024
Merged

Conversation

nosba0957
Copy link
Collaborator

@nosba0957 nosba0957 commented Jun 13, 2024

The previous implementation of the logical-and operation produced incorrect result. This error also affected the funtionality of if statements using logical-and.

This implementation follows the C99 standard for logical-and, ensuring left-to-right evaluation. If the first operand is zero, the second operand is not evaluated.

Close #137

@jserv jserv requested a review from vacantron June 13, 2024 02:37
@jserv
Copy link
Collaborator

jserv commented Jun 13, 2024

Can you add some test cases in tests/driver.sh to reflect the proposed changes?

@DrXiao
Copy link
Collaborator

DrXiao commented Jun 13, 2024

Hi, @nosba0957 , I use the following code to test the logical-and operation:

/* test.c */
#include <stdio.h>

int func(int x)
{
    if (x >= 0) {
        printf("x >= 0\n");
    }   
    return x;
}   

int main()
{
    int ret = 0;
    
    ret = 1 && func(5);
    if (ret)                   
        printf("%d\n", ret);

    printf("============\n");
        
    ret = 0 && func(5);
    if (ret)
        printf("%d\n", ret);

    return 0;
}

If we use GCC or Clang to compile and execute the code, both yield the same output:

$ gcc -o test-gcc test.c && ./test-gcc
x >= 0
1
============
$ clang -o test-clang test.c && ./test-clang
x >= 0
1
============

In the main function, for the logical-and operation, because the first operand of the second if statement is zero, its second operand, which calls the function func(), will not be evaluated. (This follows the left-to-right evaluation rule.)

However, if we use shecc with the modified implementation to generate an executable with Arm32, the result is unexpected:

$ qemu-arm out/shecc-stage2.elf -o test-shecc-stage2 test.c && qemu-arm test-shecc-stage2
x >= 0
5
============
x >= 0

For the second if statement, although the first operand is zero, the second operand (func(5)) is still executed. Besides, the result of the logical-and should be 0 or 1, but the proposed changes may cause the second operand's value being returned.

Therefore, I think we can ensure the correctness of the logical-and behavior's result, but we still need to have more effort to implement the left-to-right evaluation rule for logical-and and logical-or operations.

@nosba0957
Copy link
Collaborator Author

Sure, I'll add test cases to tests/driver.sh.

@DrXiao, Thank you for your feedback. I've decided to first ensure the correctness of the logical-and operation. Currently, left-to-right evaluation cannot be achieved. I will revise the commit messages.

@jserv
Copy link
Collaborator

jserv commented Jun 15, 2024

Currently, left-to-right evaluation cannot be achieved.

Per the C99 standard, left-to-right evaluation should be guaranteed. You should investigate the root cause preventing the proposed changes from being compliant with the C standard.

src/arm-codegen.c Outdated Show resolved Hide resolved
src/riscv-codegen.c Outdated Show resolved Hide resolved
@nosba0957
Copy link
Collaborator Author

@vacantron, I'm currently modifying the parser to achieve left-to-right evaluation. But if I start modifying the implementation from the parser, the changes to the codegen might not be necessary.

@vacantron
Copy link
Collaborator

@vacantron, I'm currently modifying the parser to achieve left-to-right evaluation. But if I start modifying the implementation from the parser, the changes to the codegen might not be necessary.

That's okay. You can close this pull request since then.

Copy link
Collaborator

@jserv jserv left a comment

Choose a reason for hiding this comment

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

Use git rebase instead of git merge.

@nosba0957
Copy link
Collaborator Author

nosba0957 commented Jul 5, 2024

In src/ssa.c, within the insert_phi_insn function, why we didn't link the prev pointer between the original instruction and the new phi-func?
This can cause errors in append_unwound_phi_insn. For example, if a basic block has two instructions:

phi %a
branch %b

When we execute append_unwound_phi_insn, some phi-functions need to insert new unwind-phi instruction in this basic block. If a branch instruction is detected at the end of basic block, it will insert unwind-phi instruction before the branch. However, we do not link prev from branch %b to phi %a, so phi %a will be directly replaced when inserting the unwind-phi instruction.
When attempting to link the prev pointer in insert_phi_insn, it ends with segmentation fault. Therefore, I attempted to modify append_unwound_phi_insn to search for a place for unwind_phi instruction from head of insn_list. It finally runs correctly.

This problem can be reproduced by removing the modification in append_unwound_phi_insn and executing a simple example of do-while loop with logical-and condition expression.

@jserv jserv requested a review from vacantron July 5, 2024 02:49
@vacantron
Copy link
Collaborator

vacantron commented Jul 5, 2024

In src/ssa.c, within the insert_phi_insn function, why we didn't link the prev pointer between the original instruction and the new phi-func?

The property prev was added later and I forgot to update the related code. Thank you for pointing it out.

Copy link
Collaborator

@vacantron vacantron left a comment

Choose a reason for hiding this comment

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

There is a snapshot test under the tests/ directory to watch the consistence of the work of lever/parser. Since they have been changed, the corresponding snapshots need to be updated together.

You can run tests/update-snapshots.sh under the ARM config.

@nosba0957
Copy link
Collaborator Author

The property prev was added later and I forgot to update the related code. Thank you to point it out

So should I keep the modifications in append_unwound_phi_insn for now?

You can run tests/update-snapshots.sh under the ARM config.

Thanks for your friendly reminder.

@vacantron
Copy link
Collaborator

So should I keep the modifications in append_unwound_phi_insn for now?

You can add a comment like FIXME: ... to indicate the workaround.

@nosba0957 nosba0957 marked this pull request as ready for review July 6, 2024 08:01
src/parser.c Outdated Show resolved Hide resolved
src/ssa.c Outdated
* the newly added phi instruction. Once this error is fixed, we
* can remove the first if statement.
*/
while (head->next->opcode != OP_branch)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you avoid traversing the whole elements since we just want to figure out the branch operation at first glance?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@nosba0957 , I have found the reason of this error. I will open another pull request for it later.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Should I still modify this part of the code, or should I keep the current commit as is and wait for @vacantron 's fix?

Copy link
Collaborator

Choose a reason for hiding this comment

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

You can cherry-pick the commit at vacantron@5fd2fbe and update your change.

@vacantron
Copy link
Collaborator

@nosba0957,I have opened the #137. You can redirect this pull request to that issue.

Copy link
Collaborator

@jserv jserv left a comment

Choose a reason for hiding this comment

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

Rebase the latest master branch.

nosba0957 and others added 2 commits July 7, 2024 09:47
The previous implementation of the logical-and operation produced
incorrect result. This implementation ensures the correctness of the
logical-and behavior and guarantees left-to-right evaluation as
mentioned in the C99 standard.

Add new function, `read_log_and_operation` and `finalize_log_and`.

`read_log_and_operation` is used when `read_expr` encounters
`OP_log_and`. This function creates a new branch instruction to test the
operand before `OP_log_and`, and then creates a new basic block for next
expression.

`finalize_log_and` is used when all operands are consumed or when an
operator with lower priority than logical-and is encountered. This
function create new basic blocks and new branchs to derive the final
value (0 or 1) for the result of the logical-and operation, and then
creates a new basic block for other new instructions.

In `read_expr`, we provide data for the false branch of logical-and
operation, including `else_bb` and `land_else_label`. All operands of
logical-and operator are designed to branch to the same "else" basic
block for the false branch.

This commit adjusts the order of connecting the basic block during
parsing `T_for` and `T_while`. This change ensures that basic blocks are
connected correctly when the condition expression includes a logical-and
operation.

Resolve sysprog21#137
The current "prev" pointer of instruction doesn't be updated after
inserting/removing the phi node. This causes the unexpected result of
phi node unwinding.
@jserv jserv merged commit 7e49741 into sysprog21:master Jul 7, 2024
4 checks passed
@jserv
Copy link
Collaborator

jserv commented Jul 7, 2024

Thank @nosba0957 for contributing!

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.

Implement short-circuit evaluation of && operator
4 participants