//=== unittests/CodeGen/TBAAMetadataTest.cpp - Checks metadata generation -===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "IRMatchers.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Parse/ParseAST.h"
#include "llvm/ADT/Triple.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/MemoryBuffer.h"
#include "gtest/gtest.h"
#include <memory>

using namespace llvm;

namespace {

struct TestCompiler {
  LLVMContext Context;
  clang::CompilerInstance compiler;
  clang::CodeGenerator *CG = nullptr;
  llvm::Module *M = nullptr;
  unsigned PtrSize = 0;

  void init(const char *TestProgram) {
    compiler.createDiagnostics();
    compiler.getCodeGenOpts().StructPathTBAA = 1;
    compiler.getCodeGenOpts().OptimizationLevel = 1;

    std::string TrStr = llvm::Triple::normalize(llvm::sys::getProcessTriple());
    llvm::Triple Tr(TrStr);
    Tr.setOS(Triple::Linux);
    Tr.setVendor(Triple::VendorType::UnknownVendor);
    Tr.setEnvironment(Triple::EnvironmentType::UnknownEnvironment);
    compiler.getTargetOpts().Triple = Tr.getTriple();
    compiler.setTarget(clang::TargetInfo::CreateTargetInfo(
        compiler.getDiagnostics(),
        std::make_shared<clang::TargetOptions>(compiler.getTargetOpts())));

    const clang::TargetInfo &TInfo = compiler.getTarget();
    PtrSize = TInfo.getPointerWidth(0) / 8;

    compiler.createFileManager();
    compiler.createSourceManager(compiler.getFileManager());
    compiler.createPreprocessor(clang::TU_Prefix);

    compiler.createASTContext();

    CG = CreateLLVMCodeGen(
        compiler.getDiagnostics(),
        "main-module",
        compiler.getHeaderSearchOpts(),
        compiler.getPreprocessorOpts(),
        compiler.getCodeGenOpts(),
        Context);
    compiler.setASTConsumer(std::unique_ptr<clang::ASTConsumer>(CG));

    compiler.createSema(clang::TU_Prefix, nullptr);

    clang::SourceManager &sm = compiler.getSourceManager();
    sm.setMainFileID(sm.createFileID(
        llvm::MemoryBuffer::getMemBuffer(TestProgram), clang::SrcMgr::C_User));
  }

  const BasicBlock *compile() {
    clang::ParseAST(compiler.getSema(), false, false);
    M = CG->GetModule();

    // Do not expect more than one function definition.
    auto FuncPtr = M->begin();
    for (; FuncPtr != M->end(); ++FuncPtr)
      if (!FuncPtr->isDeclaration())
        break;
    assert(FuncPtr != M->end());
    const llvm::Function &Func = *FuncPtr;
    ++FuncPtr;
    for (; FuncPtr != M->end(); ++FuncPtr)
      if (!FuncPtr->isDeclaration())
        break;
    assert(FuncPtr == M->end());

    // The function must consist of single basic block.
    auto BBPtr = Func.begin();
    assert(Func.begin() != Func.end());
    const BasicBlock &BB = *BBPtr;
    ++BBPtr;
    assert(BBPtr == Func.end());

    return &BB;
  }
};


auto OmnipotentCharC = MMTuple(
  MMString("omnipotent char"),
  MMTuple(
    MMString("Simple C/C++ TBAA")),
  MConstInt(0, 64)
);


auto OmnipotentCharCXX = MMTuple(
  MMString("omnipotent char"),
  MMTuple(
    MMString("Simple C++ TBAA")),
  MConstInt(0, 64)
);


TEST(TBAAMetadataTest, BasicTypes) {
  const char TestProgram[] = R"**(
    void func(char *CP, short *SP, int *IP, long long *LP, void **VPP,
              int **IPP) {
      *CP = 4;
      *SP = 11;
      *IP = 601;
      *LP = 604;
      *VPP = CP;
      *IPP = IP;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().C11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(4, 8),
        MMTuple(
          OmnipotentCharC,
          MSameAs(0),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(11, 16),
        MMTuple(
          MMTuple(
            MMString("short"),
            OmnipotentCharC,
            MConstInt(0)),
          MSameAs(0),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(601, 32),
        MMTuple(
          MMTuple(
            MMString("int"),
            OmnipotentCharC,
            MConstInt(0)),
          MSameAs(0),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(604, 64),
        MMTuple(
          MMTuple(
            MMString("long long"),
            OmnipotentCharC,
            MConstInt(0)),
          MSameAs(0),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MValType(Type::getInt8PtrTy(Compiler.Context)),
        MMTuple(
          MMTuple(
            MMString("any pointer"),
            OmnipotentCharC,
            MConstInt(0)),
          MSameAs(0),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MValType(Type::getInt32PtrTy(Compiler.Context)),
        MMTuple(
          MMTuple(
            MMString("any pointer"),
            OmnipotentCharC,
            MConstInt(0)),
          MSameAs(0),
          MConstInt(0))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, CFields) {
  const char TestProgram[] = R"**(
    struct ABC {
       short f16;
       int f32;
       long long f64;
       unsigned short f16_2;
       unsigned f32_2;
       unsigned long long f64_2;
    };

    void func(struct ABC *A) {
      A->f32 = 4;
      A->f16 = 11;
      A->f64 = 601;
      A->f16_2 = 22;
      A->f32_2 = 77;
      A->f64_2 = 604;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().C11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto StructABC = MMTuple(
    MMString("ABC"),
    MMTuple(
      MMString("short"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(0),
    MMTuple(
      MMString("int"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(4),
    MMTuple(
      MMString("long long"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(8),
    MSameAs(1),
    MConstInt(16),
    MSameAs(3),
    MConstInt(20),
    MSameAs(5),
    MConstInt(24));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(4, 32),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("int"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(11, 16),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("short"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(601, 64),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("long long"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(8))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(22, 16),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("short"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(16))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(77, 32),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("int"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(20))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(604, 64),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("long long"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(24))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, CTypedefFields) {
  const char TestProgram[] = R"**(
    typedef struct {
       short f16;
       int f32;
    } ABC;
    typedef struct {
       short value_f16;
       int value_f32;
    } CDE;

    void func(ABC *A, CDE *B) {
      A->f32 = 4;
      A->f16 = 11;
      B->value_f32 = 44;
      B->value_f16 = 111;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().C11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto NamelessStruct = MMTuple(
    MMString(""),
    MMTuple(
      MMString("short"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(0),
    MMTuple(
      MMString("int"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(4));

  const Metadata *MetaABC = nullptr;
  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(4, 32),
        MMTuple(
          MMSave(MetaABC, NamelessStruct),
          MMTuple(
            MMString("int"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(11, 16),
        MMTuple(
          NamelessStruct,
          MMTuple(
            MMString("short"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  const Metadata *MetaCDE = nullptr;
  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(44, 32),
        MMTuple(
          MMSave(MetaCDE, NamelessStruct),
          MMTuple(
            MMString("int"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(111, 16),
        MMTuple(
          NamelessStruct,
          MMTuple(
            MMString("short"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  // FIXME: Nameless structures used in definitions of 'ABC' and 'CDE' are
  // different structures and must be described by different descriptors.
  //ASSERT_TRUE(MetaABC != MetaCDE);
}

TEST(TBAAMetadataTest, CTypedefFields2) {
  const char TestProgram[] = R"**(
    typedef struct {
       short f16;
       int f32;
    } ABC;
    typedef struct {
       short f16;
       int f32;
    } CDE;

    void func(ABC *A, CDE *B) {
      A->f32 = 4;
      A->f16 = 11;
      B->f32 = 44;
      B->f16 = 111;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().C11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto NamelessStruct = MMTuple(
    MMString(""),
    MMTuple(
      MMString("short"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(0),
    MMTuple(
      MMString("int"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(4));

  const Metadata *MetaABC = nullptr;
  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(4, 32),
        MMTuple(
          MMSave(MetaABC, NamelessStruct),
          MMTuple(
            MMString("int"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(11, 16),
        MMTuple(
          NamelessStruct,
          MMTuple(
            MMString("short"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  const Metadata *MetaCDE = nullptr;
  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(44, 32),
        MMTuple(
          MMSave(MetaCDE, NamelessStruct),
          MMTuple(
            MMString("int"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(111, 16),
        MMTuple(
          NamelessStruct,
          MMTuple(
            MMString("short"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  // FIXME: Nameless structures used in definitions of 'ABC' and 'CDE' are
  // different structures, although they have the same field sequence. They must
  // be described by different descriptors.
  //ASSERT_TRUE(MetaABC != MetaCDE);
}

TEST(TBAAMetadataTest, CTypedefFields3) {
  const char TestProgram[] = R"**(
    typedef struct {
       short f16;
       int f32;
    } ABC;
    typedef struct {
       int f32;
       short f16;
    } CDE;

    void func(ABC *A, CDE *B) {
      A->f32 = 4;
      A->f16 = 11;
      B->f32 = 44;
      B->f16 = 111;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().C11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto NamelessStruct1 = MMTuple(
    MMString(""),
    MMTuple(
      MMString("short"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(0),
    MMTuple(
      MMString("int"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(4));

  auto NamelessStruct2 = MMTuple(
    MMString(""),
    MMTuple(
      MMString("int"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(0),
    MMTuple(
      MMString("short"),
      OmnipotentCharC,
      MConstInt(0)),
    MConstInt(4));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(4, 32),
        MMTuple(
          NamelessStruct1,
          MMTuple(
            MMString("int"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(11, 16),
        MMTuple(
          NamelessStruct1,
          MMTuple(
            MMString("short"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(44, 32),
        MMTuple(
          NamelessStruct2,
          MMTuple(
            MMString("int"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(111, 16),
        MMTuple(
          NamelessStruct2,
          MMTuple(
            MMString("short"),
            OmnipotentCharC,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, CXXFields) {
  const char TestProgram[] = R"**(
    struct ABC {
       short f16;
       int f32;
       long long f64;
       unsigned short f16_2;
       unsigned f32_2;
       unsigned long long f64_2;
    };

    void func(struct ABC *A) {
      A->f32 = 4;
      A->f16 = 11;
      A->f64 = 601;
      A->f16_2 = 22;
      A->f32_2 = 77;
      A->f64_2 = 604;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().CPlusPlus = 1;
  Compiler.compiler.getLangOpts().CPlusPlus11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto StructABC = MMTuple(
    MMString("_ZTS3ABC"),
    MMTuple(
      MMString("short"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0),
    MMTuple(
      MMString("int"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(4),
    MMTuple(
      MMString("long long"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(8),
    MSameAs(1),
    MConstInt(16),
    MSameAs(3),
    MConstInt(20),
    MSameAs(5),
    MConstInt(24));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(4, 32),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(11, 16),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("short"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(601, 64),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("long long"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(8))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(22, 16),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("short"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(16))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(77, 32),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(20))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(604, 64),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("long long"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(24))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, CXXTypedefFields) {
  const char TestProgram[] = R"**(
    typedef struct {
       short f16;
       int f32;
    } ABC;
    typedef struct {
       short value_f16;
       int value_f32;
    } CDE;

    void func(ABC *A, CDE *B) {
      A->f32 = 4;
      A->f16 = 11;
      B->value_f32 = 44;
      B->value_f16 = 111;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().CPlusPlus = 1;
  Compiler.compiler.getLangOpts().CPlusPlus11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto StructABC = MMTuple(
    MMString("_ZTS3ABC"),
    MMTuple(
      MMString("short"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0),
    MMTuple(
      MMString("int"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(4));

  auto StructCDE = MMTuple(
    MMString("_ZTS3CDE"),
    MMTuple(
      MMString("short"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0),
    MMTuple(
      MMString("int"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(4));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(4, 32),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(11, 16),
        MMTuple(
          StructABC,
          MMTuple(
            MMString("short"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(44, 32),
        MMTuple(
          StructCDE,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(111, 16),
        MMTuple(
          StructCDE,
          MMTuple(
            MMString("short"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, StructureFields) {
  const char TestProgram[] = R"**(
    struct Inner {
      int f32;
    };

    struct Outer {
      short f16;
      Inner b1;
      Inner b2;
    };

    void func(Outer *S) {
      S->f16 = 14;
      S->b1.f32 = 35;
      S->b2.f32 = 77;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().CPlusPlus = 1;
  Compiler.compiler.getLangOpts().CPlusPlus11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto StructInner = MMTuple(
    MMString("_ZTS5Inner"),
    MMTuple(
      MMString("int"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0));

  auto StructOuter = MMTuple(
    MMString("_ZTS5Outer"),
    MMTuple(
      MMString("short"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0),
    StructInner,
    MConstInt(4),
    MSameAs(3),
    MConstInt(8));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(14, 16),
        MMTuple(
          StructOuter,
          MMTuple(
            MMString("short"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(35, 32),
        MMTuple(
          StructOuter,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(77, 32),
        MMTuple(
          StructOuter,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(8))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, ArrayFields) {
  const char TestProgram[] = R"**(
    struct Inner {
      int f32;
    };

    struct Outer {
      short f16;
      Inner b1[2];
    };

    void func(Outer *S) {
      S->f16 = 14;
      S->b1[0].f32 = 35;
      S->b1[1].f32 = 77;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().CPlusPlus = 1;
  Compiler.compiler.getLangOpts().CPlusPlus11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto StructInner = MMTuple(
    MMString("_ZTS5Inner"),
    MMTuple(
      MMString("int"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0));

  auto StructOuter = MMTuple(
    MMString("_ZTS5Outer"),
    MMTuple(
      MMString("short"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0),
    OmnipotentCharCXX,    // FIXME: Info about array field is lost.
    MConstInt(4));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(14, 16),
        MMTuple(
          StructOuter,
          MMTuple(
            MMString("short"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(35, 32),
        MMTuple(
          StructInner,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(77, 32),
        MMTuple(
          StructInner,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, BaseClass) {
  const char TestProgram[] = R"**(
    struct Base {
      int f32;
    };

    struct Derived : public Base {
      short f16;
    };

    void func(Base *B, Derived *D) {
      B->f32 = 14;
      D->f16 = 35;
      D->f32 = 77;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().CPlusPlus = 1;
  Compiler.compiler.getLangOpts().CPlusPlus11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto ClassBase = MMTuple(
    MMString("_ZTS4Base"),
    MMTuple(
      MMString("int"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0));

  auto ClassDerived = MMTuple(
    MMString("_ZTS7Derived"),
    MMTuple(
      MMString("short"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(4));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(14, 32),
        MMTuple(
          ClassBase,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(35, 16),
        MMTuple(
          ClassDerived,
          MMTuple(
            MMString("short"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(77, 32),
        MMTuple(
          ClassBase,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, PolymorphicClass) {
  const char TestProgram[] = R"**(
    struct Base {
      virtual void m1(int *) = 0;
      int f32;
    };

    struct Derived : public Base {
      virtual void m1(int *) override;
      short f16;
    };

    void func(Base *B, Derived *D) {
      B->f32 = 14;
      D->f16 = 35;
      D->f32 = 77;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().CPlusPlus = 1;
  Compiler.compiler.getLangOpts().CPlusPlus11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto ClassBase = MMTuple(
    MMString("_ZTS4Base"),
    MMTuple(
      MMString("int"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(Compiler.PtrSize));

  auto ClassDerived = MMTuple(
    MMString("_ZTS7Derived"),
    MMTuple(
      MMString("short"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(Compiler.PtrSize + 4));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(14, 32),
        MMTuple(
          ClassBase,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(Compiler.PtrSize))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(35, 16),
        MMTuple(
          ClassDerived,
          MMTuple(
            MMString("short"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(Compiler.PtrSize + 4))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(77, 32),
        MMTuple(
          ClassBase,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(Compiler.PtrSize))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, VirtualBase) {
  const char TestProgram[] = R"**(
    struct Base {
      int f32;
    };

    struct Derived : public virtual Base {
      short f16;
    };

    void func(Base *B, Derived *D) {
      B->f32 = 14;
      D->f16 = 35;
      D->f32 = 77;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().CPlusPlus = 1;
  Compiler.compiler.getLangOpts().CPlusPlus11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto ClassBase = MMTuple(
    MMString("_ZTS4Base"),
    MMTuple(
      MMString("int"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0));

  auto ClassDerived = MMTuple(
    MMString("_ZTS7Derived"),
    MMTuple(
      MMString("short"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(Compiler.PtrSize));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MConstInt(14, 32),
        MMTuple(
          ClassBase,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(35, 16),
        MMTuple(
          ClassDerived,
          MMTuple(
            MMString("short"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(Compiler.PtrSize))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Load,
        MMTuple(
          MMTuple(
            MMString("vtable pointer"),
            MMTuple(
              MMString("Simple C++ TBAA")),
            MConstInt(0)),
          MSameAs(0),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(77, 32),
        MMTuple(
          ClassBase,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);
}

TEST(TBAAMetadataTest, TemplSpec) {
  const char TestProgram[] = R"**(
    template<typename T1, typename T2>
    struct ABC {
      T1 f1;
      T2 f2;
    };

    void func(ABC<double, int> *p) {
      p->f1 = 12.1;
      p->f2 = 44;
    }
  )**";

  TestCompiler Compiler;
  Compiler.compiler.getLangOpts().CPlusPlus = 1;
  Compiler.compiler.getLangOpts().CPlusPlus11 = 1;
  Compiler.init(TestProgram);
  const BasicBlock *BB = Compiler.compile();

  auto SpecABC = MMTuple(
    MMString("_ZTS3ABCIdiE"),
    MMTuple(
      MMString("double"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(0),
    MMTuple(
      MMString("int"),
      OmnipotentCharCXX,
      MConstInt(0)),
    MConstInt(8));

  const Instruction *I = match(BB,
      MInstruction(Instruction::Store,
        MValType(MType([](const Type &T)->bool { return T.isDoubleTy(); })),
        MMTuple(
          SpecABC,
          MMTuple(
            MMString("double"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(0))));
  ASSERT_TRUE(I);

  I = matchNext(I,
      MInstruction(Instruction::Store,
        MConstInt(44, 32),
        MMTuple(
          SpecABC,
          MMTuple(
            MMString("int"),
            OmnipotentCharCXX,
            MConstInt(0)),
          MConstInt(8))));
  ASSERT_TRUE(I);
}
}
