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

[CIR][CodeGen][Lowering] Support Integer overflow with fwrap #539

Merged
merged 9 commits into from
Apr 17, 2024

Conversation

gitoleg
Copy link
Collaborator

@gitoleg gitoleg commented Apr 9, 2024

This PR fixes some cases when a program compiled with -fwrapv fails with NYI .
Basically, the default behavior is no overlap:

void baz(int x, int y) {
  int z = x - y;
}

LLVM IR (no CIR enabled):

%sub = sub nsw i32 %0, %1

and with -fwrapv :

%sub = sub i32 %0, %1

We need something similar in CIR. The only way I see how to implement it is to add a couple of attributes to the BinOp to make things even with the llvm dialect.

Well, are there any other ideas?

@Lancern
Copy link
Member

Lancern commented Apr 9, 2024

Since nsw and nuw only applies to integral operands, what about inventing a new operation specifically for wrapped integral arithmetic? Something like cir.binop.wrapped.

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Great, more comments inline!

@gitoleg
Copy link
Collaborator Author

gitoleg commented Apr 10, 2024

@Lancern

Since nsw and nuw only applies to integral operands, what about inventing a new operation specifically for wrapped integral arithmetic? Something like cir.binop.wrapped.

well, maybe .. but I would not introduce new operations for math arithmetic, at least now, if there is a chance to do the same with the existing ones.

@bcardosolopes I didn't have a chance to check carefully, but as far I understand unsigned wrap is a standard behavior in C, and only in the rare case we need to emphasize there is no wrap with nuw.
So from my point of view (at least now) it's may be wrong way to infer "nuw" or "nsw" from integer type. Or even more! I took a look at original codegen tests, and it could be both nuw nsw set simultaneously!

@bcardosolopes
Copy link
Member

bcardosolopes commented Apr 10, 2024

Mistake from my review: my intention was to suggest nowrap, not wrap.
Thanks for the feedback @gitoleg

well, maybe .. but I would not introduce new operations for math arithmetic, at least now, if there is a chance to do the same with the existing ones.

+1

I didn't have a chance to check carefully, but as far I understand unsigned wrap is a standard behavior in C, and only in the rare case we need to emphasize there is no wrap with nuw. So from my point of view (at least now) it's may be wrong way to infer "nuw" or "nsw" from integer type. Or even more! I took a look at original codegen tests, and it could be both nuw nsw set simultaneously!

We need testcases that cover that too.

By looking at the PR, seems like this is a TU level configuration, and emitting these flags is dependent on:

  • CGF.getLangOpts().isSignedOverflowDefined() being ON
  • The type of overflow behavior: SOB_Defined, SOB_Undefined and SOB_Trapping.

So here's one idea, perhaps we should encode that at the module level (like we for fp behavior) and LLVM lowering could then look at these module properties to pick how it should lower BinOps. How does that sound? For this to work, I'm assuming that all of these binops on both signed and unsigned in a TU will have to follow the rules, do you know if that applies?

@bcardosolopes
Copy link
Member

In fact we already have those global definitions, see SignedOverflowBehaviorAttr.

@gitoleg
Copy link
Collaborator Author

gitoleg commented Apr 12, 2024

@bcardosolopes
So far I can't prove to myself that we can solve it on the module level, since there are both NUW and NSW arithmetic instructions emitted in the original codegen even without language options checked. So I'm afraid we have to do it for operations :(

We need testcases that cover that too.

Just if you are curious about, how it's possible:

unsigned test(unsigned x, unsigned y) {
  return x << y;
}

compiled with -fsanitize=unsigned-shift-base
So I think we need to support this sanitizer first.


NUW - no unsigned wrap
NSW - no signed wrap

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Ok, thanks for the extra info, we should probably just follow up with your initial suggestion, just added some things that need to be fixed first, but mostly nits.


let assemblyFormat = [{
`(` $kind `,` $lhs `,` $rhs `)` `:` type($lhs) attr-dict
`(` $kind `,` $lhs `,` $rhs `)`
(`nsw` $hasNoSignedWrap^)?
Copy link
Member

Choose a reason for hiding this comment

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

Add a verifier to make sure both nsw and nuw are only valid for the binops that make sense. It might also be needed to check for valid types: are those allowed with vectors or (recently introduced) extInts?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This allowed with any integer type - and wtth extInts as well (checked). But is no allowed with vectors

@gitoleg
Copy link
Collaborator Author

gitoleg commented Apr 16, 2024

@bcardosolopes

Now the question is - do you want me to do the same for ShiftOp (for the left one) in this PR? OR we just postpone it until we will need it? I just noticed shifts are not BinOp in cir.

@bcardosolopes
Copy link
Member

bcardosolopes commented Apr 16, 2024

Now the question is - do you want me to do the same for ShiftOp (for the left one) in this PR? OR we just postpone it until we will need it? I just noticed shifts are not BinOp in cir.

It'd be great if you can already fix the shift, so we don't need to revisit this - feel free to do it in another PR though, this one does good incremental work.

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Ok, some minor work left


if (noWrap && !noWrapOps)
return emitError() << "The nsw/nuw flags are applicable to Add, Sub and "
"Mul operations only";
Copy link
Member

Choose a reason for hiding this comment

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

Thanks! Please add 'invalid.cir' tests and nit one the error message: The nsw/nuw flags are applicable to opcodes: 'add', 'sub' and 'mul'.

@gitoleg
Copy link
Collaborator Author

gitoleg commented Apr 17, 2024

@bcardosolopes done!

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

LGTM

@bcardosolopes bcardosolopes merged commit 46555c3 into llvm:main Apr 17, 2024
6 checks passed
lanza pushed a commit that referenced this pull request Apr 29, 2024
This PR fixes some cases when a program compiled with `-fwrapv` fails
with `NYI` .
Basically, the default behavior  is no overlap:
```
void baz(int x, int y) {
  int z = x - y;
}
```
LLVM IR (no CIR enabled):
```
%sub = sub nsw i32 %0, %1
```
and with `-fwrapv` :
```
%sub = sub i32 %0, %1
```
We need something similar in CIR. The only way I see how to implement it
is to add a couple of attributes to the `BinOp` to make things even with
the llvm dialect.

Well, are there any other ideas?

---------

Co-authored-by: Bruno Cardoso Lopes <[email protected]>
lanza pushed a commit that referenced this pull request Apr 29, 2024
This PR fixes some cases when a program compiled with `-fwrapv` fails
with `NYI` .
Basically, the default behavior  is no overlap:
```
void baz(int x, int y) {
  int z = x - y;
}
```
LLVM IR (no CIR enabled):
```
%sub = sub nsw i32 %0, %1
```
and with `-fwrapv` :
```
%sub = sub i32 %0, %1
```
We need something similar in CIR. The only way I see how to implement it
is to add a couple of attributes to the `BinOp` to make things even with
the llvm dialect.

Well, are there any other ideas?

---------

Co-authored-by: Bruno Cardoso Lopes <[email protected]>
lanza pushed a commit that referenced this pull request Apr 29, 2024
This PR fixes some cases when a program compiled with `-fwrapv` fails
with `NYI` .
Basically, the default behavior  is no overlap:
```
void baz(int x, int y) {
  int z = x - y;
}
```
LLVM IR (no CIR enabled):
```
%sub = sub nsw i32 %0, %1
```
and with `-fwrapv` :
```
%sub = sub i32 %0, %1
```
We need something similar in CIR. The only way I see how to implement it
is to add a couple of attributes to the `BinOp` to make things even with
the llvm dialect.

Well, are there any other ideas?

---------

Co-authored-by: Bruno Cardoso Lopes <[email protected]>
bruteforceboy pushed a commit to bruteforceboy/clangir that referenced this pull request Oct 2, 2024
This PR fixes some cases when a program compiled with `-fwrapv` fails
with `NYI` .
Basically, the default behavior  is no overlap:
```
void baz(int x, int y) {
  int z = x - y;
}
```
LLVM IR (no CIR enabled):
```
%sub = sub nsw i32 %0, %1
```
and with `-fwrapv` :
```
%sub = sub i32 %0, %1
```
We need something similar in CIR. The only way I see how to implement it
is to add a couple of attributes to the `BinOp` to make things even with
the llvm dialect.

Well, are there any other ideas?

---------

Co-authored-by: Bruno Cardoso Lopes <[email protected]>
Hugobros3 pushed a commit to shady-gang/clangir that referenced this pull request Oct 2, 2024
This PR fixes some cases when a program compiled with `-fwrapv` fails
with `NYI` .
Basically, the default behavior  is no overlap:
```
void baz(int x, int y) {
  int z = x - y;
}
```
LLVM IR (no CIR enabled):
```
%sub = sub nsw i32 %0, %1
```
and with `-fwrapv` :
```
%sub = sub i32 %0, %1
```
We need something similar in CIR. The only way I see how to implement it
is to add a couple of attributes to the `BinOp` to make things even with
the llvm dialect.

Well, are there any other ideas?

---------

Co-authored-by: Bruno Cardoso Lopes <[email protected]>
keryell pushed a commit to keryell/clangir that referenced this pull request Oct 19, 2024
This PR fixes some cases when a program compiled with `-fwrapv` fails
with `NYI` .
Basically, the default behavior  is no overlap:
```
void baz(int x, int y) {
  int z = x - y;
}
```
LLVM IR (no CIR enabled):
```
%sub = sub nsw i32 %0, %1
```
and with `-fwrapv` :
```
%sub = sub i32 %0, %1
```
We need something similar in CIR. The only way I see how to implement it
is to add a couple of attributes to the `BinOp` to make things even with
the llvm dialect.

Well, are there any other ideas?

---------

Co-authored-by: Bruno Cardoso Lopes <[email protected]>
lanza pushed a commit that referenced this pull request Nov 5, 2024
This PR fixes some cases when a program compiled with `-fwrapv` fails
with `NYI` .
Basically, the default behavior  is no overlap:
```
void baz(int x, int y) {
  int z = x - y;
}
```
LLVM IR (no CIR enabled):
```
%sub = sub nsw i32 %0, %1
```
and with `-fwrapv` :
```
%sub = sub i32 %0, %1
```
We need something similar in CIR. The only way I see how to implement it
is to add a couple of attributes to the `BinOp` to make things even with
the llvm dialect.

Well, are there any other ideas?

---------

Co-authored-by: Bruno Cardoso Lopes <[email protected]>
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.

3 participants