[AArch64] NFC: Add generic StackOffset to describe scalable offsets.

To support spilling/filling of scalable vectors we need a more generic
representation of a stack offset than simply 'int'.

For this we introduce the StackOffset struct, which comprises multiple
offsets sized by their respective MVTs. Byte-offsets will thus be a simple
tuple such as { offset, MVT::i8 }. Adding two byte-offsets will result in a
byte offset { offsetA + offsetB, MVT::i8 }. When two offsets have different
types, we can canonicalise them to use the same MVT, as long as their
runtime sizes are guaranteed to have the same size-ratio as they would have
at compile-time.

When we have both scalable- and fixed-size objects on the stack, we can 
create an offset that is: 

  ({ offset_fixed, MVT::i8 } + { offset_scalable, MVT::nxv1i8 })

The struct also contains a getForFrameOffset() method that is specific to
AArch64 and decomposes the frame-offset to be used directly in instructions
that operate on the stack or index into the stack.

Note: This patch adds StackOffset as an AArch64-only concept, but we would
like to make this a generic concept/struct that is supported by all 
interfaces that take or return stack offsets (currently as 'int'). Since
that would be a bigger change that is currently pending on D32530 landing,
we thought it makes sense to first show/prove the concept in the AArch64
target before proposing to roll this out further.

Reviewers: thegameg, rovka, t.p.northover, efriedma, greened

Reviewed By: rovka, greened

Differential Revision: https://reviews.llvm.org/D61435

llvm-svn: 368024
diff --git a/llvm/unittests/Target/AArch64/CMakeLists.txt b/llvm/unittests/Target/AArch64/CMakeLists.txt
index 76f6c82..650d309 100644
--- a/llvm/unittests/Target/AArch64/CMakeLists.txt
+++ b/llvm/unittests/Target/AArch64/CMakeLists.txt
@@ -19,4 +19,5 @@
 
 add_llvm_unittest(AArch64Tests
   InstSizes.cpp
+  TestStackOffset.cpp
   )
diff --git a/llvm/unittests/Target/AArch64/TestStackOffset.cpp b/llvm/unittests/Target/AArch64/TestStackOffset.cpp
new file mode 100644
index 0000000..240cec9
--- /dev/null
+++ b/llvm/unittests/Target/AArch64/TestStackOffset.cpp
@@ -0,0 +1,60 @@
+//===- TestStackOffset.cpp - StackOffset unit tests------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AArch64StackOffset.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+TEST(StackOffset, MixedSize) {
+  StackOffset A(1, MVT::i8);
+  EXPECT_EQ(1, A.getBytes());
+
+  StackOffset B(2, MVT::i32);
+  EXPECT_EQ(8, B.getBytes());
+
+  StackOffset C(2, MVT::v4i64);
+  EXPECT_EQ(64, C.getBytes());
+}
+
+TEST(StackOffset, Add) {
+  StackOffset A(1, MVT::i64);
+  StackOffset B(1, MVT::i32);
+  StackOffset C = A + B;
+  EXPECT_EQ(12, C.getBytes());
+
+  StackOffset D(1, MVT::i32);
+  D += A;
+  EXPECT_EQ(12, D.getBytes());
+}
+
+TEST(StackOffset, Sub) {
+  StackOffset A(1, MVT::i64);
+  StackOffset B(1, MVT::i32);
+  StackOffset C = A - B;
+  EXPECT_EQ(4, C.getBytes());
+
+  StackOffset D(1, MVT::i64);
+  D -= A;
+  EXPECT_EQ(0, D.getBytes());
+}
+
+TEST(StackOffset, isZero) {
+  StackOffset A(0, MVT::i64);
+  StackOffset B(0, MVT::i32);
+  EXPECT_TRUE(!A);
+  EXPECT_TRUE(!(A + B));
+}
+
+TEST(StackOffset, getForFrameOffset) {
+  StackOffset A(1, MVT::i64);
+  StackOffset B(1, MVT::i32);
+  int64_t ByteSized;
+  (A + B).getForFrameOffset(ByteSized);
+  EXPECT_EQ(12, ByteSized);
+}