summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorReid Kleckner <reid@kleckner.net>2014-08-12 22:01:39 +0000
committerReid Kleckner <reid@kleckner.net>2014-08-12 22:01:39 +0000
commit3d4eae74e7555c9bf61f07f9207ec26eaf4e6977 (patch)
treedbaff9778a7a6c1d69b5261e3f91cbc8cdff5e47
parent5e1fa85ec665a3339296ef8ba66db9db7a0c6c17 (diff)
downloadbcm5719-llvm-3d4eae74e7555c9bf61f07f9207ec26eaf4e6977.tar.gz
bcm5719-llvm-3d4eae74e7555c9bf61f07f9207ec26eaf4e6977.zip
APInt: Make self-move-assignment a no-op to fix stage3 clang-cl
It's not clear what the semantics of a self-move should be. The consensus appears to be that a self-move should leave the object in a moved-from state, which is what our existing move assignment operator does. However, the MSVC 2013 STL will perform self-moves in some cases. In particular, when doing a std::stable_sort of an already sorted APSInt vector of an appropriate size, one of the merge steps will self-move half of the elements. We don't notice this when building with MSVC, because MSVC will not synthesize the move assignment operator for APSInt. Presumably MSVC does this because APInt, the base class, has user-declared special members that implicitly delete move special members. Instead, MSVC selects the copy-assign operator, which defends against self-assignment. Clang, on the other hand, selects the move-assign operator, and we get garbage APInts. llvm-svn: 215478
-rw-r--r--llvm/include/llvm/ADT/APInt.h13
-rw-r--r--llvm/unittests/ADT/APIntTest.cpp17
2 files changed, 28 insertions, 2 deletions
diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index aa3c3f67ec1..f815628f30c 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -656,13 +656,22 @@ public:
/// @brief Move assignment operator.
APInt &operator=(APInt &&that) {
- if (!isSingleWord())
+ if (!isSingleWord()) {
+ // The MSVC STL shipped in 2013 requires that self move assignment be a
+ // no-op. Otherwise algorithms like stable_sort will produce answers
+ // where half of the output is left in a moved-from state.
+ if (this == &that)
+ return *this;
delete[] pVal;
+ }
- BitWidth = that.BitWidth;
VAL = that.VAL;
+ // If 'this == &that', avoid zeroing our own bitwidth by storing to 'that'
+ // first.
+ unsigned ThatBitWidth = that.BitWidth;
that.BitWidth = 0;
+ BitWidth = ThatBitWidth;
return *this;
}
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index 19c47ab13bf..7da3fb25548 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -678,4 +678,21 @@ TEST(APIntTest, nearestLogBase2) {
EXPECT_EQ(A9.nearestLogBase2(), UINT32_MAX);
}
+TEST(APIntTest, SelfMoveAssignment) {
+ APInt X(32, 0xdeadbeef);
+ X = std::move(X);
+ EXPECT_EQ(32, X.getBitWidth());
+ EXPECT_EQ(0xdeadbeefULL, X.getLimitedValue());
+
+ uint64_t Bits[] = {0xdeadbeefdeadbeefULL, 0xdeadbeefdeadbeefULL};
+ APInt Y(128, Bits);
+ Y = std::move(Y);
+ EXPECT_EQ(128, Y.getBitWidth());
+ EXPECT_EQ(~0ULL, Y.getLimitedValue());
+ const uint64_t *Raw = Y.getRawData();
+ EXPECT_EQ(2, Y.getNumWords());
+ EXPECT_EQ(0xdeadbeefdeadbeefULL, Raw[0]);
+ EXPECT_EQ(0xdeadbeefdeadbeefULL, Raw[1]);
+}
+
}
OpenPOWER on IntegriCloud