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 undefined behaviour warnings in ir_fold.h #99

Merged
merged 1 commit into from
Jan 14, 2025

Conversation

nielsdos
Copy link
Contributor

@nielsdos nielsdos commented Jan 10, 2025

See php/php-src#17430

I changed the adds, subs, muls to use unsigned arithmetic because on 2-complement systems that's the same as signed arithmetic but without potential UB warnings. This essentially makes the wrapping behaviour defined. I only did this for 32 and 64 bit types because for 8 and 16 bit the operations will do integer promotion, avoiding the issue.

There is also val.i64 < 0 && val.i64 - 1 < 0 that I changed. This is because the second condition would be removed by the compiler because val.i64 < 0 and signed wrapping is undefined.

See php/php-src#17430

I changed the adds, subs, muls to use unsigned arithmetic because on
2-complement systems that's the same as signed arithmetic but without
potential UB warnings. This essentially makes the wrapping behaviour
defined. I only did this for 32 and 64 bit types because for 8 and 16
bit the operations will do integer promotion, avoiding the issue.

There is also `val.i64 < 0 && val.i64 - 1 < 0` that I changed.
This is because the second condition would be thrown away by the
compiler because `val.i64 < 0` and signed wrapping is undefined.
Copy link
Owner

@dstogov dstogov left a comment

Choose a reason for hiding this comment

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

The fix looks right. LLVM and HotSpot also use unsigned math at least for add/sub/mul.

IR_FOLD_CONST_I(op1_insn->val.i32 + op2_insn->val.i32);
IR_FOLD_CONST_I(op1_insn->val.u32 + op2_insn->val.u32);
Copy link
Owner

Choose a reason for hiding this comment

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

Could you please add a comment: e.g. "Here and below we use unsigned math to prevent undefined signed overflow behavior".

@dstogov dstogov merged commit e5b2d5a into dstogov:master Jan 14, 2025
7 checks passed
@dstogov
Copy link
Owner

dstogov commented Jan 14, 2025

Unfortunately, the fix is incomplete. I see 6 new tests failures on C test suite from MIR project (https://github.com/vnmakarov/mir/tree/master/c-tests). I'll take care.

@dstogov
Copy link
Owner

dstogov commented Jan 14, 2025

The problem is in this rule

IR_FOLD(SUB(C_I32, C_I32))
{
	IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
	IR_FOLD_CONST_I(op1_insn->val.u32 - op2_insn->val.u32);
}

With op1 == 0 and op2 == 1 now it starts produce 0xffffffff instead of expected -1.

dstogov added a commit that referenced this pull request Jan 14, 2025
@dstogov
Copy link
Owner

dstogov commented Jan 14, 2025

@nielsdos I reverted this patch.
Could you please take another look. I guess, i32 should be changed to u64 instead of u32.

@dstogov
Copy link
Owner

dstogov commented Jan 14, 2025

No. Actually we need to treat the result of unsigned operation as signed to make the proper sign extension.

IR_FOLD(SUB(C_I32, C_I32))
{
	IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
	IR_FOLD_CONST_I((int32_t)(op1_insn->val.u32 - op2_insn->val.u32));
}

dstogov pushed a commit that referenced this pull request Jan 14, 2025
See php/php-src#17430

I changed the adds, subs, muls to use unsigned arithmetic because on
2-complement systems that's the same as signed arithmetic but without
potential UB warnings. This essentially makes the wrapping behaviour
defined. I only did this for 32 and 64 bit types because for 8 and 16
bit the operations will do integer promotion, avoiding the issue.

There is also `val.i64 < 0 && val.i64 - 1 < 0` that I changed.
This is because the second condition would be thrown away by the
compiler because `val.i64 < 0` and signed wrapping is undefined.
@dstogov
Copy link
Owner

dstogov commented Jan 14, 2025

I committed the fixed version 4c3389b

@nielsdos
Copy link
Contributor Author

Thanks for handling this.
I looked at the commit, it looks good. There is one place where there is both the int32_t change and change from i32 to u64, that could be u32 instead but probably won't make real harm: 4c3389b#diff-974027a6039dc7d081f523d2b3c139e47b209fca874e31ad964b797921f15284R397

@dstogov
Copy link
Owner

dstogov commented Jan 14, 2025

I looked at the commit, it looks good. There is one place where there is both the int32_t change and change from i32 to u64, that could be u32 instead but probably won't make real harm: 4c3389b#diff-974027a6039dc7d081f523d2b3c139e47b209fca874e31ad964b797921f15284R397

Thanks for catching this. This is definitely my mistake. I'll commit s/u64/u32/.

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.

2 participants