//===- MachineInstrTest.cpp -----------------------------------------------===//
//
// 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 "VEInstrInfo.h"
#include "VESubtarget.h"
#include "VETargetMachine.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"

#include "gtest/gtest.h"

using namespace llvm;

TEST(VETest, VLIndex) {
  using namespace VE;

  // Return expected VL register index in each MI's operands.  Aurora VE has
  // multiple instruction formats for each instruction.  So, we define
  // instructions hierarchically and tests parts of the whole instructions.
  // This function returns -1 to N as expected index, or -2 as default.
  // We skip a test on an instruction that this function returns -2.
  auto VLIndex = [](unsigned Opcode) {
    switch (Opcode) {
    default:
      break;
    case VLDNCrz:
      return -1;
    case VLDUNCrzl:
    case VLDLSXrzl_v:
    case VLDLZXNCirL:
    case VLD2DNCrrL_v:
    case VLDU2DNCrzL_v:
    case VLDL2DSXizL_v:
    case VLDL2DZXNCirl:
      return 3;
    case VSTOTrrv:
      return -1;
    case VSTUNCrzvl:
    case VSTLNCOTizvL:
    case VST2Dirvl:
      return 3;
    case VSTU2DNCrzvml:
    case VSTL2DNCOTrzvml:
      return 4;
    case VGTNCsrzm_v:
      return -1;
    case VGTUNCvrzl:
    case VGTLSXvrzl_v:
    case VGTLZXNCsirL:
      return 4;
    case VGTNCsrrmL_v:
    case VGTUNCvrzmL:
    case VGTLSXsizml_v:
      return 5;
    case VSCNCsrzvm:
      return -1;
    case VSCUNCvrzvl:
    case VSCLNCsirvL:
      return 4;
    case VSCOTsrrvmL:
    case VSCUNCOTvrzvmL:
    case VSCLsizvml:
      return 5;
    case PFCHVrr:
      return -1;
    case PFCHVrrl:
    case PFCHVNCrzL:
      return 2;
    case VBRDrm:
      return -1;
    case VBRDrl:
      return 2;
    case VBRDimL_v:
      return 3;
    case VMVrvm_v:
      return -1;
    case VMVivl:
      return 3;
    case VMVrvmL_v:
      return 4;
    case VADDULvv_v:
    case PVADDULOrvm:
      return -1;
    case VADDUWvvl_v:
    case PVADDUUPrvL:
      return 3;
    case PVADDUvvmL_v:
    case VADDSWSXivml:
    case VADDSLivml:
      return 4;
    case VDIVULvv_v:
    case VDIVSWSXrvm:
      return -1;
    case VDIVUWvrl_v:
    case VDIVSWZXviL:
      return 3;
    case VDIVSLivmL_v:
    case VDIVSWSXivml:
      return 4;
    // We test casually if instructions are defined using a multiclass already
    // tested.
    case VSUBSLivml:
    case VMULSLivml:
    case VCMPSLivml:
    case VMAXSLivml:
      return 4;
    case VANDvv_v:
    case PVANDLOrvm:
      return -1;
    case PVANDvvl_v:
    case PVANDUPrvL:
      return 3;
    case VORvvmL_v:
    case PVORLOmvml:
    case VXORmvml:
    case VEQVmvml:
      return 4;
    case VLDZv:
      return -1;
    case VPCNTvL:
      return 2;
    case VBRVvml:
      return 3;
    case VSEQ:
      return -1;
    case VSEQL:
      return 1;
    case VSEQml:
      return 2;
    case VSLLvv_v:
    case PVSLLLOvrm:
      return -1;
    case PVSLLvvl_v:
    case PVSRLUPvrL:
      return 3;
    case VSLLvimL_v:
    case PVSRLLOvrml:
    case VSLALvimL_v:
    case VSRALvimL_v:
      return 4;
    case VSLDvvr_v:
    case VSLDvvim:
      return -1;
    case VSLDvvrl_v:
    case VSRDvviL:
      return 4;
    case VSLDvvimL_v:
    case VSRDvvrml:
      return 5;
    case VSFAvrr_v:
    case VSFAvrmm_v:
      return -1;
    case VSFAvirl_v:
    case VSFAvirL:
      return 4;
    case VSFAvimml:
    case VSFAvimmL_v:
      return 5;
    case VFADDDivml:
    case VFSUBDivml:
    case VFMULDivml:
    case VFDIVDivml:
    case VFCMPDivml:
    case VFMAXDivml:
      return 4;
    case VFSQRTDv_v:
    case VFSQRTSvm:
      return -1;
    case VFSQRTDvl_v:
    case VFSQRTDvL:
      return 2;
    case VFSQRTDvmL_v:
    case VFSQRTDvml:
    case VFSQRTDvmL:
      return 3;
    case VFMADDvvv_v:
    case PVFMADLOvrvm:
      return -1;
    case PVFMADvivl_v:
    case PVFMADUPvrvL:
      return 4;
    case VFMADSivvmL_v:
    case PVFMADLOvrvml:
    case VFMSBDivvmL_v:
    case VFNMADDivvmL_v:
    case VFNMSBDivvmL_v:
      return 5;
    case VRCPDvmL:
    case VRSQRTDvmL:
    case VRSQRTDNEXvmL:
      return 3;
    case VCVTWDSXv:
    case VCVTWDZXvm_v:
      return -1;
    case VCVTWSSXvl_v:
    case VCVTWSZXvL:
      return 3;
    case PVCVTWSLOvmL_v:
    case PVCVTWSUPvml:
    case PVCVTWSvmL:
    case VCVTLDvml:
      return 4;
    case VCVTDWvml:
    case VCVTDLvml:
    case VCVTSDvml:
    case VCVTDSvml:
    case VSUMWSXvml:
    case VSUMLvml:
    case VFSUMDvml:
    case VRMAXSWFSTSXvml:
    case VRMAXSLFSTvml:
    case VFRMAXDFSTvml:
    case VRANDvml:
    case VRORvml:
    case VRXORvml:
      return 3;
    case VFIADvr_v:
    case VFIASvi_v:
      return -1;
    case VFIADvrl_v:
    case VFIASviL_v:
    case VFISDviL_v:
    case VFIMDviL_v:
      return 3;
    case VFIAMDvvr_v:
    case VFIAMSvvi_v:
      return -1;
    case VFISMDvvrl_v:
    case VFISMSvviL_v:
    case VFIMADvviL_v:
    case VFIMSDvviL_v:
      return 4;
    case VMRGivml:
      return 4;
    case VCPvml:
    case VEXvml:
      return 3;
    case VSHFvvr:
    case VSHFvvr_v:
      return -1;
    case VSHFvvrl:
    case VSHFvvrL_v:
      return 4;
    case VFMKLv:
    case VFMKLvm:
      return -1;
    case VFMKLvl:
    case VFMKLvL:
      return 3;
    case VFMKLvml:
    case VFMKLvmL:
      return 4;
    case VFMKLal:
    case VFMKLnaL:
      return 1;
    case VFMKLaml:
    case VFMKLnamL:
    case VFMKWnamL:
    case VFMKDnamL:
      return 2;
    case TOVMm:
    case PCVMm:
    case LZVMm:
      return -1;
    case TOVMml:
    case PCVMmL:
    case LZVMml:
      return 2;
    }
    return -2;
  };

  LLVMInitializeVETargetInfo();
  LLVMInitializeVETarget();
  LLVMInitializeVETargetMC();

  auto TT(Triple::normalize("ve-unknown-linux-gnu"));
  std::string Error;
  const Target *T = TargetRegistry::lookupTarget(TT, Error);
  if (!T) {
    dbgs() << Error;
    return;
  }

  TargetOptions Options;
  auto TM = std::unique_ptr<LLVMTargetMachine>(
    static_cast<LLVMTargetMachine*>(
      T->createTargetMachine(TT, "", "", Options, std::nullopt, std::nullopt,
                             CodeGenOptLevel::Default)));
  VESubtarget ST(TM->getTargetTriple(), std::string(TM->getTargetCPU()),
                 std::string(TM->getTargetFeatureString()),
                 *static_cast<const VETargetMachine *>(TM.get()));
  const VEInstrInfo *TII = ST.getInstrInfo();
  auto MII = TM->getMCInstrInfo();

  for (unsigned i = 0; i < VE::INSTRUCTION_LIST_END; ++i) {
    // Skip -2 (default value)
    if (VLIndex(i) == -2)
      continue;

    const MCInstrDesc &Desc = TII->get(i);

    uint64_t Flags = Desc.TSFlags;
    ASSERT_EQ(VLIndex(i), GET_VLINDEX(Flags))
              << MII->getName(i)
              << ": mismatched expected VL register index in its argument\n";
  }
}
