This patch implements builtin_elementwise_max and
builtin_elementwise_min, as specified in D111529.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Move type checking to helper function checkMathBuiltinElementType to be reused in D111986.
clang/include/clang/Sema/Sema.h | ||
---|---|---|
12727–12728 | <nothing to fix>Why oh why did we start slapping Sema as a prefix on a bunch of functions implemented in a class named Sema?</nothing to fix> | |
clang/lib/Sema/SemaChecking.cpp | ||
1981 | Spurious whitespace change? | |
16529–16530 | Sema typically comes first, so this is just to match local style. | |
16531 | I'm a bit surprised we'd need !Ty->getAs<VectorType>() as I would have expected !ConstantMatrixType::isValidElementType(Ty) to cover all the type checking of Ty. Can you explain why the extra check is needed here? | |
16539 | Do we actually need this parameter? | |
16543–16548 | Should these arguments undergo usual conversions (array to pointer decay, integer promotions, etc)? | |
clang/test/Sema/builtins-elementwise-math.c | ||
21–22 | Also: i = __builtin_elementwise_max(i, i, i); // too many arguments | |
43 | Additional tests I would like to see: int i; short s; __builtin_elementwise_min(i, s); // ok (integer promotions)? enum e { one, two }; __builtin_elementwise_min(one, two); // ok (using enums)? enum f { three }; __builtin_elementwise_min(one, three); // ok (different enums)? _ExtInt(32) ext; __builtin_elementwise_min(ext, ext); // ok (using bit-precise integers)? const int ci; __builtin_elementwise_min(i, ci); // ok (qualifiers don't match)? |
Thanks for taking a look!
clang/lib/Sema/SemaChecking.cpp | ||
---|---|---|
16529–16530 | Thanks, adjusted! | |
16531 | The builtins as implemented accept either vector types or a scalar type, which then must be a valid element type for matrix types. The second check may be a bit confusing, but the restrictions laid out in the spec for scalar/element-types match the matrix element type restrictions, so this seems a convenient helper to use. Does this in general make sense? | |
16539 | No, it can just return ExprResult(TheCall) instead I think! | |
16543–16548 | I intentionally went with not performing conversions, because it might lead to surprising results. If the arguments have different types, it is not clear to me which type should be chosen to try convert the other one; e.g. if we have __builtin_elementwise_max(float, int) should the first argument be converted to int` or the second one to float? Forcing the types to match without conversion seem to make them less error-prone to use, but I might be missing some general rule to handle type conflicts here? | |
clang/test/Sema/builtins-elementwise-math.c | ||
21–22 | I'll add it | |
43 | Thanks I'll add those! at the moment __builtin_elementwise_min(i, s); // ok (integer promotions)? would be rejected, as per my response in Sema. The other currently are not handled properly, which I'll fix! |
clang/lib/Sema/SemaChecking.cpp | ||
---|---|---|
16531 | Thanks for the explanation, that makes sense. | |
16539 | Could also return a bool and have the caller fiddle with ExprResult, too. I don't have strong opinions on which way to go though. | |
16543–16548 | I'm not certain how these builtins are expected to be used, but if they're likely to be used with literals, I think we may want integer promotions because of that alone. Btw, given that these only have two arguments, it seems like we could use the usual arithmetic conversions to pick a common type the same way as happens for binary operators. If you end up not adding any conversions here, we should have a test case to cover passing in two array objects (which won't decay to pointers). | |
clang/test/Sema/builtins-elementwise-math.c | ||
43 | I'd still like to see the test case added so it's clear we intend to reject it. It may also be wise to add a test case involving an integer literal and a short variable cast to int to make it clear that you have to add casts sometimes to make this work. Another interesting test is where sugars don't match. e.g., int i; __attribute__((address_space(0))) int addr_space_i; typedef int foo; __builtin_elementwise_min(i, addr_space_i); // ok (attributes don't match)? __builtin_elementwise_min(i, foo); // ok (sugar doesn't match)? |
clang/test/Sema/builtins-elementwise-math.c | ||
---|---|---|
43 | You might also need to handle something like: int (i); int j; __builtin_elementwise_min(i, j); // May get a type mismatch here So you may need to do some type canonicalization to avoid these sorts of issues. |
clang/lib/Sema/SemaChecking.cpp | ||
---|---|---|
16543–16548 |
Yes, they should ideally work with literals! I think it should be relatively straight-forward to add promotion of literals.
IIUC this happens in SemaOverload.cpp, but I am not sure how builtins would hook into the infrastructure there. Are there other builtins that are similarly overloaded by any chance? |
clang/lib/Sema/SemaChecking.cpp | ||
---|---|---|
16543–16548 |
I was thinking of the literal being an int and the variable being a short, so the literal isn't what needs promotion in that case. That said, I suppose character literals in C++ would promote from char to int, so there are some literals that could promote. That'd be a somewhat amusing test case for C and C++ both (__builtin_elementwise_max('a', 1);).
Sema::SemaBuiltinUnorderedCompare() uses this, for example: ExprResult OrigArg0 = TheCall->getArg(0); ExprResult OrigArg1 = TheCall->getArg(1); // Do standard promotions between the two arguments, returning their common // type. QualType Res = UsualArithmeticConversions( OrigArg0, OrigArg1, TheCall->getExprLoc(), ACK_Comparison); if (OrigArg0.isInvalid() || OrigArg1.isInvalid()) return true; |
Address comments @aaron.ballman, thanks!
The most notable changes are using UsualArithmeticConversions for argument conversion and checking the canonical types. Also added a bunch of additional Sema tests as suggested, and some codegen tests for a couple of conversions.
clang/lib/Sema/SemaChecking.cpp | ||
---|---|---|
16539 | updated to return a bool | |
16543–16548 |
I added i1 = __builtin_elementwise_max(1, 'a'); to the codegen tests.
Thanks a lot! I updated to code to use UsualArithmeticConversions and added the tests you suggested (hope I didn't miss any important ones). Looks like all should work as expected now. | |
clang/test/Sema/builtins-elementwise-math.c | ||
43 | Thanks, I updated the code to check the canonical type. |
clang/lib/Sema/SemaChecking.cpp | ||
---|---|---|
16564 | I think you want to set this to Res, because that's the common type between TyB and TyA, right? That will also ensure that qualifiers are stripped, I believe. e.g., const int a = 2; int b = 1; static_assert(!std::is_const_v<decltype(__builtin_elementwise_max(a, b))>); static_assert(!std::is_const_v<decltype(__builtin_elementwise_max(b, a))>); |
clang/include/clang/Basic/DiagnosticSemaKinds.td | ||
---|---|---|
8747–8748 | Does err_typecheck_call_different_arg_types suffice? | |
8750–8751 | It'd be nice to avoid this entirely as the diagnostic situation is one we would have elsewhere (so we should be able to use common diagnostic checking code from here and places like SemaBuiltinMatrixColumnMajorLoad() ideally). | |
clang/lib/Sema/SemaChecking.cpp | ||
16531–16536 | Related to the comment above on the diagnostic, I'm wondering if we want to abstract this into a helper function that gets used by all the elementwise builtins you're adding? |
I thought of another test case -- should these new functions work on complex types? (Those are weird enough we may want to consider adding both Sema and CodeGen tests if we do support them, and Sema tests alone if we don't.)
Address latest comments, thanks
- Added a generic err_builtin_invalid_arg_type diagnostic kind, which can also be used for some of the matrix type mismatches (see D112532).
- Dropped err_elementwise_math_arg_types_mismatch in favor of the existing err_typecheck_call_different_arg_types.
- Use Res to set the call type
- Add tests with _Complex, which gets rejected.
- add C++ test that check constness of builtins.
At the moment they should not work with complex types (like C99's _Complex). The problem there is that there is no dedicated IR type and the LLVM intrinsics do not support complex types. So at the moment we cannot lower such operations effectively.
I think it would make sense to extend them to complex types once we can better model those operations in LLVM IR.
clang/include/clang/Basic/DiagnosticSemaKinds.td | ||
---|---|---|
8747–8748 | err_typecheck_call_different_arg_types is sufficient, thanks! | |
8750–8751 | I turned the diagnostic kind into a more generic one and also put up a patch to use them instead some of the custom matrix diagnostic kinds (D112532). WDYT? I'm not sure if there's much potential for sharing the checking code, as it looks like most places check for slightly different things (or the check is quite compact already). | |
clang/lib/Sema/SemaChecking.cpp | ||
16531–16536 | Unfortunately I am not sure which code this is referring to. The element type check is already a generic function to be used to check the new builtins with 1 arg and the reduction builtins. | |
16564 | Agreed, using TyB is confusing, so I updated it. I *think* it was still doing the right thing, because TyB is to B's type after conversion, so I *think* that would be to common type already. I also added a .cpp Sema test file to check the constness. |
Does err_typecheck_call_different_arg_types suffice?