This is an archive of the discontinued LLVM Phabricator instance.

Fix size for _ExtInt types with builtins
ClosedPublic

Authored by jtmott-intel on Jun 8 2020, 12:21 PM.

Details

Summary

This patch addresses two issues.

The first issue is use of the checked arithmetic builtins in combination with _ExtInt types. _ExtInt types with non-power of two sizes would be (inconsistently) rounded up to the next largest power of two. The inconsistency caused the following compile error:

$ cat test.c
int main(void) {
    _ExtInt(31) x = 1;
    _ExtInt(31) y = 1;
    _ExtInt(31) result;
    _Bool status = __builtin_add_overflow(x, y, &result);
    return 0;
}

$ clang test.c
clang-11: /home/user/llvm_workspace/llvm-project/llvm/lib/IR/Instructions.cpp:1408: void llvm::StoreInst::AssertOK(): Assertion `getOperand(0)->getType() == cast<PointerType>(getOperand(1)->getType())->getElementType() && "Ptr must be a pointer to Val type!"' failed.

To fix this, I added the above example as a failing test to clang/test/CodeGen/builtins-overflow.c, and I changed clang/lib/CodeGen/CGBuiltin.cpp to get the correct size for _ExtInt types and allow the new test to pass.

The second issue is with __builtin_mul_overflow specifically in combination with _ExtInt types larger than 64 bits. For that combination, the emitted IR appears to be correct, but a full compile caused the following link error (for at least X86 target):

$ cat test.c
int main(void) {
    _ExtInt(65) x = 1;
    _ExtInt(65) y = 1;
    _ExtInt(65) result;
    _Bool status = __builtin_mul_overflow(x, y, &result);
    return 0;
}
$ clang test.c
/tmp/test-2b8174.o: In function `main':
test.c:(.text+0x63): undefined reference to `__muloti4'
clang-11: error: linker command failed with exit code 1 (use -v to see invocation)

To address this -- for now -- I changed clang/lib/Sema/SemaChecking.cpp to emit a diagnostic that explains the limitation to users better than the link error does. I did my best to borrow verbiage from similar messages. The new compile result is:

$ clang test.c
test.c:5:43: error: _ExtInt argument larger than 64-bits to overflow builtin requires runtime support that is not available for this target
    _Bool status = __builtin_mul_overflow(x, y, &result);
                                          ^
1 error generated.

Diff Detail

Event Timeline

jtmott-intel created this revision.Jun 8 2020, 12:21 PM

So the idea of the SEMA check is right, but it ultimately isn't correct. Integers up to 128 can be called, as long as you use compiler-rt (--rtlib=compiler-rt), see https://godbolt.org/z/JDXZG_.

However, 129 bits results in a crash in LLVM: https://godbolt.org/z/q432t_

When you change this, please add tests for 128/127 bit as well. AND the new error message needs a SEMA test as well.

clang/include/clang/Basic/DiagnosticSemaKinds.td
7951

Mentioning target-specific support here seems incorrect. @rjmccall I cannot think of a better wording, can you?

clang/lib/Sema/SemaChecking.cpp
334

I'll explain it below, but while I think this is the right IDEA, I think >64 is wrong. It should be >128.

Second, this requires a SEMA test.

Also, please file a bug (if you haven't already) about the >128 bit problem. We have a similar bug for normal division (https://bugs.llvm.org/show_bug.cgi?id=45649), but this needs one as well.

lebedev.ri added inline comments.
clang/lib/Sema/SemaChecking.cpp
1740–1741

I don't think this applies to add/sub: https://godbolt.org/z/Dj3GLP (or at least not all of them?)
OTOH this does apply to plain division/remainder: https://godbolt.org/z/SXNw8H

Linking compiler-rt is something that should be automatically happening. It's possible that compiler-rt will have different maximum widths on different targets, though.

clang/include/clang/Basic/DiagnosticSemaKinds.td
7951

"overflow builtins do not support _ExtInt operands of more than %0 bits on this target"? I don't think it's unreasonable to mention the target-specificness of it. Hard-coding the number 64 in the diagnostic seems excessively targeted, though.

Updated the diagnostic check and message for 128 rather than 64 bits.
Added sema tests for 128/129 sizes.
Added codegen tests for 127/128 sizes to mul.

jtmott-intel marked 2 inline comments as done.Jun 9 2020, 9:14 AM
jtmott-intel added inline comments.
clang/lib/Sema/SemaChecking.cpp
1740–1741

Correct. I passed the BuiltinID so that in SemaBuiltinOverflow I could check for mul specifically. Alternatively, I could give mul its own case "block" separate from add/sub. Thoughts/preferences?

Limited diagnostic to *signed* _ExtInt, and added test to verify unsigned works.
Reused existing diagnostic message rather than make a new one.

jtmott-intel marked an inline comment as done.Jun 9 2020, 9:16 PM
jtmott-intel added inline comments.
clang/include/clang/Basic/DiagnosticSemaKinds.td
7951

I discovered there's a good existing message I could use that already takes the bitwidth as a parameter. I decided not to add a new one. Thoughts/preferences? Here's how the message would come out.

test.c:5:43: error: signed _ExtInt of bit sizes greater than 128 not supported
    _Bool status = __builtin_mul_overflow(x, y, &result);
                                          ^
1 error generated.
rjmccall added inline comments.Jun 9 2020, 9:47 PM
clang/include/clang/Basic/DiagnosticSemaKinds.td
7951

It'd be much clearer to say something about the fact that it's just this builtin that's not supported.

erichkeane added inline comments.Jun 10 2020, 6:04 AM
clang/lib/Sema/SemaChecking.cpp
325

Disallow _signed_ ...

329

Since you're always doing "Arg.get" below, this should probably just store the Expr*.

330

Comments must end in a period.

332

Try:

Type *Ty = ArgExpr->getType()->getPointeeOrArrayElementType();

instead of the ternary.

335

remove the curley brackets, and make this:

return S.Diag(....

clang/test/Sema/builtins-overflow.c
39

As @rjmccall said, I'd suggest the new message anyway that mentions the builtin, otherwise this would be confusing for me. Something like:

signed argument to __builtin_mul_overflow must have a bitwidth of less than or equal to 128.

jtmott-intel marked an inline comment as done.Jun 10 2020, 3:11 PM
jtmott-intel added inline comments.
clang/lib/Sema/SemaChecking.cpp
332

A complication I'm seeing is that getPointeeOrArrayElementType returns a Type but getIntWidth wants a QualType. Some options that occurred to me:

  • Is it acceptable to just wrap a Type in a QualType?
  • I might be able to add a getIntWidth(const Type*) overload.
  • Looks like I can skip the getAs<PointerType>() and just call getPointeeType directly. The line would be shorter but I would still need the ternary.
rjmccall added inline comments.Jun 10 2020, 3:56 PM
clang/lib/Sema/SemaChecking.cpp
322–323

I know this is existing code, but this is a broken mess. Please change this to:

ExprResult Arg = DefaultFunctionArrayLvalueConversion(TheCall->getArg(2));
if (Arg.isInvalid()) return true;
TheCall->setArg(2, Arg.get());

QualType Ty = Arg.get()->getType();
const auto *PtrTy = Ty->getAs<PointerType>();
if (!PtrTy ||
    !PtrTy->getPointeeType()->isIntegerType() ||
    PtrTy->getPointeeType().isConstQualified()) {
  S.Diag(Arg.get()->getBeginLoc(),
         diag::err_overflow_builtin_must_be_ptr_int)
    << Ty << Arg.get()->getSourceRange();
  return true;
}

Test case would be something like (in ObjC):

@interface HasPointer
@property int *pointer;
@end

void test(HasPointer *hp) {
  __builtin_add_overflow(x, y, hp.pointer);
}

And the earlier block needs essentially the same change (but obviously checking for a different type). You can't check types before doing placeholder conversions, and once you do l-value conversions and a type-check, you really don't need the full parameter-initialization logic.

332
  • Is it acceptable to just wrap a Type in a QualType?

In general, no, this can lose qualifiers on the array element. But getIntWidth shouldn't ever care about that, so in the abstract, adding the overload should be fine. However, in this case I think the ternary is actually better code.

This updated patch prepared before John's latest comments.

  • Changed diagnostic message to something halfway between John and Erich's suggestions.
  • Removed superfluous calls to Arg.get.
  • Combined call to Diag and return of error status.
  • Simplified but kept ternary (for now).
  • Updated comments.
jtmott-intel marked 6 inline comments as done.Jun 10 2020, 4:39 PM
jtmott-intel added inline comments.
clang/include/clang/Basic/DiagnosticSemaKinds.td
7951

Updated message to something halfway between John and Erich's suggestions. Current result:

test.c:5:43: error: __builtin_mul_overflow does not support signed _ExtInt operands of more than 128 bits
    _Bool status = __builtin_mul_overflow(x, y, &result);
                                          ^
1 error generated.
clang/test/Sema/builtins-overflow.c
39

Updated message to something halfway between your and John's suggestions. Current result:

test.c:5:43: error: __builtin_mul_overflow does not support signed _ExtInt operands of more than 128 bits
    _Bool status = __builtin_mul_overflow(x, y, &result);
                                          ^
1 error generated.
rjmccall added inline comments.Jun 10 2020, 8:16 PM
clang/test/Sema/builtins-overflow.c
39

That looks great to me, thanks.

erichkeane accepted this revision.Jun 11 2020, 6:07 AM

Feel free to simplify the error message on commit.

clang/include/clang/Basic/DiagnosticSemaKinds.td
7952

I don't think it is worth doing a 'select' on signed/unsigned. When we need it, we can add it.

I more expect the builtin name to be modified first though (but don't bother making it a modifyable either).

This revision is now accepted and ready to land.Jun 11 2020, 6:07 AM
jtmott-intel added inline comments.Jun 11 2020, 10:01 PM
clang/lib/Sema/SemaChecking.cpp
322–323

I've implemented this locally but I have a quick question about the test. It passed even before I applied this code change. Is this test expected to fail before this change and pass after? Or is it just to prove that the feature (placeholder argument types?) works in general?

rjmccall added inline comments.Jun 11 2020, 10:53 PM
clang/lib/Sema/SemaChecking.cpp
322–323

Oh, we seem to handle placeholders specially, nevermind. Well, what I said about doing conversions before checking the type still holds, but the test case is just this:

int z[1];
__builtin_add_overflow(x, y, z);

where we should decay the argument to a pointer instead of rejecting it.

  • Removed sign/unsign select.
  • Added test and support for placeholder types in builtins.
jtmott-intel marked an inline comment as done.Jun 12 2020, 12:19 PM
jtmott-intel added inline comments.
clang/lib/Sema/SemaChecking.cpp
322–323

Done. Latest updated patch includes these code changes.

rjmccall accepted this revision.Jun 12 2020, 1:58 PM

LGTM, thanks!

I don't have commit access. For whoever performs the commit, here's a (draft) commit message:

[clang] Fix or emit diagnostic for checked arithmetic builtins with _ExtInt types

- Fix computed size for _ExtInt types passed to checked arithmetic builtins.
- Emit diagnostic when signed _ExtInt larger than 128-bits is passed to __builtin_mul_overflow.
- Change Sema checks for builtins to accept placeholder types.
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptJun 15 2020, 7:01 AM