This is an archive of the discontinued LLVM Phabricator instance.

Implement some constexpr vector unary operators, fix boolean-ops
ClosedPublic

Authored by erichkeane on Dec 13 2021, 1:21 PM.

Details

Summary

As requested in the review, this implements unary +,-,~, and ! for
vector types.

All of our boolean operations on vector types should be using something
like vcmpeqd, which results in a mask of '-1' for the 'truth' type. We are
currently instead using '1', which results in some incorrect
calculations when used later (note that it does NOT result in a boolean
vector, as that is not really a thing).

This patch corrects that 1 to be a -1, and updates the affected tests.

Diff Detail

Event Timeline

erichkeane requested review of this revision.Dec 13 2021, 1:21 PM
erichkeane created this revision.

Note I am adding the folks who were added as reviewers the last time I did vector constexpr work: https://reviews.llvm.org/D79755

I again looked into operator[] to simplify things, but the 'LValueBase' stuff (seemingly required to do something like: VecTy[3] = 1;) is a little convoluted/tough to figure out and might be a fairly sizable 'touch'.

efriedma added inline comments.Dec 13 2021, 1:28 PM
clang/lib/AST/ExprConstant.cpp
2915

Using "operator=" to assign an int to an APInt is going to lead to weird/confusing results. For example, I think this produces the wrong result if you have an int128_t vector.

erichkeane added inline comments.Dec 13 2021, 1:39 PM
clang/lib/AST/ExprConstant.cpp
2915

Can you clarify your comment? The result type of one of these compare-ops is either an i8 or an i32 I think, so I would expect it to 'just work', right?

What problem of operator= on an APInt gives the wrong result? I guess I've never seen/don't know how to repro what you mean.

I DID try to get a test wtih a 'compare op' on an __int128 vector, but that ends up asserting in GetSignedVectorType

To add:
I DID just try to fix that thing:

[ekeane1@scsel-clx-24 clang]$ git diff
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 2b69c3727852..330772d2b10a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -12265,6 +12265,8 @@ QualType Sema::GetSignedVectorType(QualType V) {
       return Context.getExtVectorType(Context.ShortTy, VTy->getNumElements());
     else if (TypeSize == Context.getTypeSize(Context.IntTy))
       return Context.getExtVectorType(Context.IntTy, VTy->getNumElements());
+    else if (TypeSize == Context.getTypeSize(Context.Int128Ty))
+      return Context.getExtVectorType(Context.Int128Ty, VTy->getNumElements());
     else if (TypeSize == Context.getTypeSize(Context.LongTy))
       return Context.getExtVectorType(Context.LongTy, VTy->getNumElements());
     assert(TypeSize == Context.getTypeSize(Context.LongLongTy) &&
@@ -12272,7 +12274,10 @@ QualType Sema::GetSignedVectorType(QualType V) {
     return Context.getExtVectorType(Context.LongLongTy, VTy->getNumElements());
   }

-  if (TypeSize == Context.getTypeSize(Context.LongLongTy))
+  if (TypeSize == Context.getTypeSize(Context.Int128Ty))
+    return Context.getVectorType(Context.Int128Ty, VTy->getNumElements(),
+                                 VectorType::GenericVector);
+  else if (TypeSize == Context.getTypeSize(Context.LongLongTy))
     return Context.getVectorType(Context.LongLongTy, VTy->getNumElements(),
                                  VectorType::GenericVector);
   else if (TypeSize == Context.getTypeSize(Context.LongTy))
[ekeane1@scsel-clx-24 clang]$

I then compiled:

using VecTy = __int128 __attribute__((vector_size(32)));
constexpr auto F() {
  VecTy V{1,2};

  return V <= 2;
}
auto bar() {
  constexpr auto Var = F();
  return Var;
}

And got:

; Function Attrs: mustprogress noinline nounwind optnone
define dso_local <2 x i128> @_Z3barv() #0 {
entry:
  %Var = alloca <2 x i128>, align 32
  store <2 x i128> <i128 18446744073709551615, i128 18446744073709551615>, <2 x i128>* %Var, align 32
  ret <2 x i128> <i128 18446744073709551615, i128 18446744073709551615>
}

I believe that is the uint64 'max value', right? So would that would be what you mean?

craig.topper added inline comments.
clang/lib/AST/ExprConstant.cpp
2915

Don't compare ops produce a result with the same number of bits as the input? If the compare is a vector of int128 the result should also be a vector of int128. APInt::operator= takes a uint64_t as input and will zero extend it to 128 bits. I think we want to sign extend here.

I assume the APInt for result already been given its bit width before this call?

craig.topper added inline comments.Dec 13 2021, 2:29 PM
clang/lib/AST/ExprConstant.cpp
2915

The easy fix is probably just to do the assignment like it was before and then do Result.negate().

erichkeane added inline comments.Dec 13 2021, 3:55 PM
clang/lib/AST/ExprConstant.cpp
2915

Yes, the APInt should already be the right size. And you do seem correct about the width being the size of the elements, so you were right about that.

THanks for the tip on the .negate, I can just do that!

Enable __int128 vectors, and fix the issue of the >64 bit vectors for negation. Also add a test for these.

Note, my pre-merge check failure seems completely unrelated. It seems to have some problem with the some 'go' bindings, but I don't believe that has anything to do with this change.

aaron.ballman added inline comments.Dec 15 2021, 7:54 AM
clang/lib/AST/ExprConstant.cpp
10389

Would it be worth handling ~ and ! as well given that they seem pretty trivial to support? I don't insist, but I'm trying to reason whether the for loop should be hoisted out of the switch because it seems like it'll be needed for all of the cases.

clang/lib/Sema/SemaExpr.cpp
12262–12287

NFC nit: a whole pile of else after return that can be removed someday.

erichkeane added inline comments.Dec 15 2021, 8:15 AM
clang/lib/AST/ExprConstant.cpp
10389

Yep, I can take a look at that. Note the rest of the unary operators are all either already handled (real/imag), or require switching this over to an LValueBase/etc as they are modifying, which I don't understand. But those two should be pretty easy to do.

craig.topper added inline comments.Dec 15 2021, 9:44 AM
clang/lib/AST/ExprConstant.cpp
10393

Can this be Elt.getInt().negate()?

10397

Can this be Elt.getFloat().changeSign()?

@craig.topper : Yep, probably so in both those cases. I'm working on a patch that includes the 2 'nots' as well, which complicates this whole section a bunch though.

erichkeane retitled this revision from Correct behavior of Vector boolean-operations, implement vector operator- to Implement some constexpr vector unary operators, fix boolean-ops.
erichkeane edited the summary of this revision. (Show Details)

Implement the other two easily doable operators, do Craigs suggestions.

aaron.ballman added inline comments.Dec 15 2021, 10:37 AM
clang/lib/AST/ExprConstant.cpp
10188

Comment can be updated as we're not missing ~ any longer.

10399

operator!, right?

10405–10406

-1 for true and 0 for false (just to be super clear).

craig.topper added inline comments.Dec 15 2021, 10:37 AM
clang/lib/AST/ExprConstant.cpp
10188

Drop "unary ~" since you handled it?

10373

Lower case per coding standards? Put "handle" first since it is the verb. We seem to be inconcistent about where we put "Vector", we have handleVectorVectorBinOp and handleCompareOpForVector.

10395

Would APInt.getInt().flipAllbits() be better than re-creating an APValue?

clang/lib/Sema/SemaExpr.cpp
12264

Thanks for fixing the else after returns here. I almost commented on that earlier.

erichkeane marked 7 inline comments as done.

Fix Aaron+ Craig's comments.

aaron.ballman accepted this revision.Dec 15 2021, 10:59 AM

LGTM, but I leave the final sign-off about the behavior to others.

This revision is now accepted and ready to land.Dec 15 2021, 10:59 AM

Note: I think I've done everything requested, so I'm hoping to commit this tomorrow 1st thing in order to have it in time for everyone's vacations (and so my downstream can get it before the new year), unless I hear objections from the other reviewers.

This revision was landed with ongoing or failed builds.Dec 17 2021, 6:08 AM
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptDec 17 2021, 6:08 AM

Hi everyone! We are encountering crashes in some of our altivec test cases due to this change. clang crashes when we have an instance of a struct or union type, and we assign the result of a unary operator ++ or -- with a vector operand to a field of the instance. If we are assigning the result to just a vector type, the compiler does not crash.

Here is a reproducer. To reproduce the crash, use command clang -target powerpc-unknown-unknown -maltivec small.c.

// small.c
#include <stdio.h>

// struct type
typedef struct result
{
    vector signed short result_vec;
} RESULT;

vector signed short op_nine = { 9, 9, 9, 9, 9, 9, 9, 9 };

int main() {
    // Instance of the struct type
    RESULT r;

    // This line below crashes - we are assigning the result of the unary operator to a struct field. 
    r.result_vec = ++op_nine;
    for (int i = 0; i < 8; ++i) {
        printf("%d\t", r.result_vec[i]);
    }
    printf("\n");

    // This case works fine. The compiler does not crash. 
    /*vector signed short op_10 = ++op_nine;
    for (int i = 0; i < 8; ++i) {
        printf("%d\t", op_10[i]);
    }
    printf("\n"); */
}

We discovered this on AIX and we can reproduce this on Linux on Power. The issue is reproducible on Intel based Macs as well. Here is a stack dump from MacOS.

Assertion failed: (isVector() && "Invalid accessor"), function getVectorLength, file /Users/qiongsiwu/workspace/community/llvm-project/clang/include/clang/AST/APValue.h, line 498.
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /Users/qiongsiwu/workspace/community/llvm-project/build/bin/clang-14 -cc1 -triple powerpc-unknown-unknown -emit-obj -mrelax-all --mrelax-relocations -disable-free -clear-ast-before-backend -main-file-name small.c -mrelocation-model static -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu ppc -target-feature +altivec -mfloat-abi hard -debugger-tuning=gdb -target-linker-version 650.9 -fcoverage-compilation-dir=/Users/qiongsiwu/workspace/13359 -resource-dir /Users/qiongsiwu/workspace/community/llvm-project/build/lib/clang/14.0.0 -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ -fdebug-compilation-dir=/Users/qiongsiwu/workspace/13359 -ferror-limit 19 -fno-signed-char -fgnuc-version=4.2.1 -fcolor-diagnostics -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/folders/gh/5hw84m4x2hv4csmhpp8y02680000gn/T/small-e9e02b.o -x c small.c
1.	small.c:23:5: current parser token 'return'
2.	small.c:12:12: parsing function body 'main'
3.	small.c:12:12: in compound statement ('{}')
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  clang-14                 0x0000000107d8d1f7 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 39
1  clang-14                 0x0000000107d8bfb8 llvm::sys::RunSignalHandlers() + 248
2  clang-14                 0x0000000107d8d840 SignalHandler(int) + 272
3  libsystem_platform.dylib 0x00007ff80662fe2d _sigtramp + 29
4  libsystem_platform.dylib 0x00007ff7ba7632d0 _sigtramp + 18446744072435741888
5  libsystem_c.dylib        0x00007ff806566d10 abort + 123
6  libsystem_c.dylib        0x00007ff8065660be err + 0
7  clang-14                 0x000000010c208443 (anonymous namespace)::VectorExprEvaluator::VisitUnaryOperator(clang::UnaryOperator const*) (.cold.13) + 35
8  clang-14                 0x000000010a40b1d7 (anonymous namespace)::VectorExprEvaluator::VisitUnaryOperator(clang::UnaryOperator const*) + 2375

On AIX, the compiler can produce the following AST during EvaluateVector before the crash.

[EvaluateVector]
BinaryOperator 0x11173e148 '__vector short' '='
|-MemberExpr 0x11173e0e0 '__vector short' lvalue .result_vec 0x111739f40
| `-DeclRefExpr 0x11173a5a0 'RESULT':'struct result' lvalue Var 0x11173a520 'r' 'RESULT':'struct result'
`-UnaryOperator 0x11173e130 '__vector short' prefix '++'
  `-DeclRefExpr 0x11173e110 '__vector short' lvalue Var 0x11173a070 'op_nine' '__vector short'
[EvaluateVector]
UnaryOperator 0x11173e130 '__vector short' prefix '++'
`-DeclRefExpr 0x11173e110 '__vector short' lvalue Var 0x11173a070 'op_nine' '__vector short'

The compiler crashes when trying to deal with 0x11173a070. It is expecting a vector kind (APValue::ValueKind::Vector), but the actual kind is APValue::ValueKind::LValue, and the compiler crashes inside SubExprValue.getVectorLength().

Could you help us take a look? Thanks a lot! FYI @bmahjour @anhtuyen

Hi!
Sorry for the delay, I just got back from my christmas break. I'll take a look later today/this week.

Should be fixed here: 2edc21e8566be8fa9b20e0bb71a83af90ec9aa97

Thanks!

Thanks so much for the fix Erich! I can confirm the altivec test cases are all passing now.