TestStackOffset.cpp 3.58 KB
//===- 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());

  StackOffset D(2, MVT::nxv4i64);
  EXPECT_EQ(64, D.getScalableBytes());

  StackOffset E(2, MVT::v4i64);
  EXPECT_EQ(0, E.getScalableBytes());

  StackOffset F(2, MVT::nxv4i64);
  EXPECT_EQ(0, F.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());

  StackOffset E(1, MVT::nxv1i32);
  StackOffset F = C + E;
  EXPECT_EQ(12, F.getBytes());
  EXPECT_EQ(4, F.getScalableBytes());
}

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());

  C += StackOffset(2, MVT::nxv1i32);
  StackOffset E = StackOffset(1, MVT::nxv1i32);
  StackOffset F = C - E;
  EXPECT_EQ(4, F.getBytes());
  EXPECT_EQ(4, F.getScalableBytes());
}

TEST(StackOffset, isZero) {
  StackOffset A(0, MVT::i64);
  StackOffset B(0, MVT::i32);
  EXPECT_TRUE(!A);
  EXPECT_TRUE(!(A + B));

  StackOffset C(0, MVT::nxv1i32);
  EXPECT_TRUE(!(A + C));

  StackOffset D(1, MVT::nxv1i32);
  EXPECT_FALSE(!(A + D));
}

TEST(StackOffset, isValid) {
  EXPECT_FALSE(StackOffset(1, MVT::nxv8i1).isValid());
  EXPECT_TRUE(StackOffset(2, MVT::nxv8i1).isValid());

#ifndef NDEBUG
#ifdef GTEST_HAS_DEATH_TEST
  EXPECT_DEATH(StackOffset(1, MVT::i1),
               "Offset type is not a multiple of bytes");
  EXPECT_DEATH(StackOffset(1, MVT::nxv1i1),
               "Offset type is not a multiple of bytes");
#endif // defined GTEST_HAS_DEATH_TEST
#endif // not defined NDEBUG
}

TEST(StackOffset, getForFrameOffset) {
  StackOffset A(1, MVT::i64);
  StackOffset B(1, MVT::i32);
  StackOffset C(1, MVT::nxv4i32);

  // If all offsets can be materialized with only ADDVL,
  // make sure PLSized is 0.
  int64_t ByteSized, VLSized, PLSized;
  (A + B + C).getForFrameOffset(ByteSized, PLSized, VLSized);
  EXPECT_EQ(12, ByteSized);
  EXPECT_EQ(1, VLSized);
  EXPECT_EQ(0, PLSized);

  // If we need an ADDPL to materialize the offset, and the number of scalable
  // bytes fits the ADDPL immediate, fold the scalable bytes to fit in PLSized.
  StackOffset D(1, MVT::nxv16i1);
  (C + D).getForFrameOffset(ByteSized, PLSized, VLSized);
  EXPECT_EQ(0, ByteSized);
  EXPECT_EQ(0, VLSized);
  EXPECT_EQ(9, PLSized);

  StackOffset E(4, MVT::nxv4i32);
  StackOffset F(1, MVT::nxv16i1);
  (E + F).getForFrameOffset(ByteSized, PLSized, VLSized);
  EXPECT_EQ(0, ByteSized);
  EXPECT_EQ(0, VLSized);
  EXPECT_EQ(33, PLSized);

  // If the offset requires an ADDPL instruction to materialize, and would
  // require more than two instructions, decompose it into both
  // ADDVL (n x 16 bytes) and ADDPL (n x 2 bytes) instructions.
  StackOffset G(8, MVT::nxv4i32);
  StackOffset H(1, MVT::nxv16i1);
  (G + H).getForFrameOffset(ByteSized, PLSized, VLSized);
  EXPECT_EQ(0, ByteSized);
  EXPECT_EQ(8, VLSized);
  EXPECT_EQ(1, PLSized);
}