text: //===--------- VectorBuilderTest.cpp - VectorBuilder 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 "llvm/IR/VectorBuilder.h"
  #include "llvm/IR/Constants.h"
  #include "llvm/IR/IRBuilder.h"
  #include "llvm/IR/IntrinsicInst.h"
  #include "llvm/IR/LLVMContext.h"
  #include "llvm/IR/Module.h"
  #include "gtest/gtest.h"
  
  using namespace llvm;
  
  namespace {
  
  static unsigned VectorNumElements = 8;
  
  class VectorBuilderTest : public testing::Test {
  protected:
    LLVMContext Context;
  
    VectorBuilderTest() : Context() {}
  
    std::unique_ptr<Module> createBuilderModule(Function *&Func, BasicBlock *&BB,
                                                Value *&Mask, Value *&EVL) {
      auto Mod = std::make_unique<Module>("TestModule", Context);
      auto *Int32Ty = Type::getInt32Ty(Context);
      auto *Mask8Ty =
          FixedVectorType::get(Type::getInt1Ty(Context), VectorNumElements);
      auto *VoidFuncTy =
          FunctionType::get(Type::getVoidTy(Context), {Mask8Ty, Int32Ty}, false);
      Func =
          Function::Create(VoidFuncTy, GlobalValue::ExternalLinkage, "bla", *Mod);
      Mask = Func->getArg(0);
      EVL = Func->getArg(1);
      BB = BasicBlock::Create(Context, "entry", Func);
  
      return Mod;
    }
  };
  
  /// Check that creating binary arithmetic VP intrinsics works.
  TEST_F(VectorBuilderTest, TestCreateBinaryInstructions) {
    Function *F;
    BasicBlock *BB;
    Value *Mask, *EVL;
    auto Mod = createBuilderModule(F, BB, Mask, EVL);
  
    IRBuilder<> Builder(BB);
    VectorBuilder VBuild(Builder);
    VBuild.setMask(Mask).setEVL(EVL);
  
    auto *FloatVecTy =
        FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
    auto *IntVecTy =
        FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
  
  #define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS)                             \
    {                                                                            \
      auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE);                \
      bool IsFP = (#INSTCLASS)[0] == 'F';                                        \
      auto *ValueTy = IsFP ? FloatVecTy : IntVecTy;                              \
      Value *Op = UndefValue::get(ValueTy);                                      \
      auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy,     \
                                               {Op, Op});                        \
      ASSERT_TRUE(isa<VPIntrinsic>(I));                                          \
      auto *VPIntrin = cast<VPIntrinsic>(I);                                     \
      ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID);                               \
      ASSERT_EQ(VPIntrin->getMaskParam(), Mask);                                 \
      ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL);                          \
    }
  #include "llvm/IR/Instruction.def"
  }
  
  static bool isAllTrueMask(Value *Val, unsigned NumElements) {
    auto *ConstMask = dyn_cast<Constant>(Val);
    if (!ConstMask)
      return false;
  
    // Structure check.
    if (!ConstMask->isAllOnesValue())
      return false;
  
    // Type check.
    auto *MaskVecTy = cast<FixedVectorType>(ConstMask->getType());
    if (MaskVecTy->getNumElements() != NumElements)
      return false;
  
    return MaskVecTy->getElementType()->isIntegerTy(1);
  }
  
  /// Check that creating binary arithmetic VP intrinsics works.
  TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoMask) {
    Function *F;
    BasicBlock *BB;
    Value *Mask, *EVL;
    auto Mod = createBuilderModule(F, BB, Mask, EVL);
  
    IRBuilder<> Builder(BB);
    VectorBuilder VBuild(Builder);
    VBuild.setEVL(EVL).setStaticVL(VectorNumElements);
  
    auto *FloatVecTy =
        FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
    auto *IntVecTy =
        FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
  
  #define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS)                             \
    {                                                                            \
      auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE);                \
      bool IsFP = (#INSTCLASS)[0] == 'F';                                        \
      Type *ValueTy = IsFP ? FloatVecTy : IntVecTy;                              \
      Value *Op = UndefValue::get(ValueTy);                                      \
      auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy,     \
                                               {Op, Op});                        \
      ASSERT_TRUE(isa<VPIntrinsic>(I));                                          \
      auto *VPIntrin = cast<VPIntrinsic>(I);                                     \
      ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID);                               \
      ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements));   \
      ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL);                          \
    }
  #include "llvm/IR/Instruction.def"
  }
  
  static bool isLegalConstEVL(Value *Val, unsigned ExpectedEVL) {
    auto *ConstEVL = dyn_cast<ConstantInt>(Val);
    if (!ConstEVL)
      return false;
  
    // Value check.
    if (ConstEVL->getZExtValue() != ExpectedEVL)
      return false;
  
    // Type check.
    return ConstEVL->getType()->isIntegerTy(32);
  }
  
  /// Check that creating binary arithmetic VP intrinsics works.
  TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoEVL) {
    Function *F;
    BasicBlock *BB;
    Value *Mask, *EVL;
    auto Mod = createBuilderModule(F, BB, Mask, EVL);
  
    IRBuilder<> Builder(BB);
    VectorBuilder VBuild(Builder);
    VBuild.setMask(Mask).setStaticVL(VectorNumElements);
  
    auto *FloatVecTy =
        FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
    auto *IntVecTy =
        FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
  
  #define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS)                             \
    {                                                                            \
      auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE);                \
      bool IsFP = (#INSTCLASS)[0] == 'F';                                        \
      Type *ValueTy = IsFP ? FloatVecTy : IntVecTy;                              \
      Value *Op = UndefValue::get(ValueTy);                                      \
      auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy,     \
                                               {Op, Op});                        \
      ASSERT_TRUE(isa<VPIntrinsic>(I));                                          \
      auto *VPIntrin = cast<VPIntrinsic>(I);                                     \
      ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID);                               \
      ASSERT_EQ(VPIntrin->getMaskParam(), Mask);                                 \
      ASSERT_TRUE(                                                               \
          isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \
    }
  #include "llvm/IR/Instruction.def"
  }
  
  /// Check that creating binary arithmetic VP intrinsics works.
  TEST_F(VectorBuilderTest,
         TestCreateBinaryInstructions_FixedVector_NoMask_NoEVL) {
    Function *F;
    BasicBlock *BB;
    Value *Mask, *EVL;
    auto Mod = createBuilderModule(F, BB, Mask, EVL);
  
    IRBuilder<> Builder(BB);
    VectorBuilder VBuild(Builder);
    VBuild.setStaticVL(VectorNumElements);
  
    auto *FloatVecTy =
        FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
    auto *IntVecTy =
        FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
  
  #define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS)                             \
    {                                                                            \
      auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE);                \
      bool IsFP = (#INSTCLASS)[0] == 'F';                                        \
      Type *ValueTy = IsFP ? FloatVecTy : IntVecTy;                              \
      Value *Op = UndefValue::get(ValueTy);                                      \
      auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy,     \
                                               {Op, Op});                        \
      ASSERT_TRUE(isa<VPIntrinsic>(I));                                          \
      auto *VPIntrin = cast<VPIntrinsic>(I);                                     \
      ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID);                               \
      ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements));   \
      ASSERT_TRUE(                                                               \
          isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \
    }
  #include "llvm/IR/Instruction.def"
  }
  /// Check that creating vp.load/vp.store works.
  TEST_F(VectorBuilderTest, TestCreateLoadStore) {
    Function *F;
    BasicBlock *BB;
    Value *Mask, *EVL;
    auto Mod = createBuilderModule(F, BB, Mask, EVL);
  
    IRBuilder<> Builder(BB);
    VectorBuilder VBuild(Builder);
    VBuild.setMask(Mask).setEVL(EVL);
  
    auto *FloatVecTy =
        FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
    auto *FloatVecPtrTy = FloatVecTy->getPointerTo();
  
    Value *FloatVecPtr = UndefValue::get(FloatVecPtrTy);
    Value *FloatVec = UndefValue::get(FloatVecTy);
  
    // vp.load
    auto LoadVPID = VPIntrinsic::getForOpcode(Instruction::Load);
    auto *LoadIntrin = VBuild.createVectorInstruction(Instruction::Load,
                                                      FloatVecTy, {FloatVecPtr});
    ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));
    auto *VPLoad = cast<VPIntrinsic>(LoadIntrin);
    ASSERT_EQ(VPLoad->getIntrinsicID(), LoadVPID);
    ASSERT_EQ(VPLoad->getMemoryPointerParam(), FloatVecPtr);
  
    // vp.store
    auto *VoidTy = Builder.getVoidTy();
    auto StoreVPID = VPIntrinsic::getForOpcode(Instruction::Store);
    auto *StoreIntrin = VBuild.createVectorInstruction(Instruction::Store, VoidTy,
                                                       {FloatVec, FloatVecPtr});
    ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));
    auto *VPStore = cast<VPIntrinsic>(StoreIntrin);
    ASSERT_EQ(VPStore->getIntrinsicID(), StoreVPID);
    ASSERT_EQ(VPStore->getMemoryPointerParam(), FloatVecPtr);
    ASSERT_EQ(VPStore->getMemoryDataParam(), FloatVec);
  }
  
  /// Check that the SilentlyReturnNone error handling mode works.
  TEST_F(VectorBuilderTest, TestFail_SilentlyReturnNone) {
    Function *F;
    BasicBlock *BB;
    Value *Mask, *EVL;
    auto Mod = createBuilderModule(F, BB, Mask, EVL);
  
    IRBuilder<> Builder(BB);
    auto *VoidTy = Builder.getVoidTy();
    VectorBuilder VBuild(Builder, VectorBuilder::Behavior::SilentlyReturnNone);
    VBuild.setMask(Mask).setEVL(EVL);
    auto *Val = VBuild.createVectorInstruction(Instruction::Br, VoidTy, {});
    ASSERT_EQ(Val, nullptr);
  }
  
  /// Check that the ReportAndFail error handling mode aborts as advertised.
  TEST_F(VectorBuilderTest, TestFail_ReportAndAbort) {
    Function *F;
    BasicBlock *BB;
    Value *Mask, *EVL;
    auto Mod = createBuilderModule(F, BB, Mask, EVL);
  
    IRBuilder<> Builder(BB);
    auto *VoidTy = Builder.getVoidTy();
    VectorBuilder VBuild(Builder, VectorBuilder::Behavior::ReportAndAbort);
    VBuild.setMask(Mask).setEVL(EVL);
    ASSERT_DEATH({ VBuild.createVectorInstruction(Instruction::Br, VoidTy, {}); },
                 "No VPIntrinsic for this opcode");
  }
  
  } // end anonymous namespace
  