/*===-- debuginfo.c - tool for testing libLLVM and llvm-c API -------------===*\
|*                                                                            *|
|* 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                    *|
|*                                                                            *|
|*===----------------------------------------------------------------------===*|
|*                                                                            *|
|* Tests for the LLVM C DebugInfo API                                         *|
|*                                                                            *|
\*===----------------------------------------------------------------------===*/

#include "llvm-c/DebugInfo.h"
#include "llvm-c-test.h"
#include "llvm-c/Core.h"
#include "llvm-c/Types.h"

#include <assert.h>
#include <stdio.h>
#include <string.h>

static LLVMMetadataRef
declare_objc_class(LLVMDIBuilderRef DIB, LLVMMetadataRef File) {
  LLVMMetadataRef Decl = LLVMDIBuilderCreateStructType(DIB, File, "TestClass", 9, File, 42, 64, 0, LLVMDIFlagObjcClassComplete, NULL, NULL, 0, 0, NULL, NULL, 0);
  LLVMMetadataRef SuperDecl = LLVMDIBuilderCreateStructType(DIB, File, "TestSuperClass", 14, File, 42, 64, 0, LLVMDIFlagObjcClassComplete, NULL, NULL, 0, 0, NULL, NULL, 0);
  LLVMDIBuilderCreateInheritance(DIB, Decl, SuperDecl, 0, 0, 0);
  LLVMMetadataRef TestProperty =
      LLVMDIBuilderCreateObjCProperty(DIB, "test", 4, File, 42, "getTest", 7, "setTest", 7, 0x20 /*copy*/ | 0x40 /*nonatomic*/, SuperDecl);
  LLVMDIBuilderCreateObjCIVar(DIB, "_test", 5, File, 42, 64, 0, 64, LLVMDIFlagPublic, SuperDecl, TestProperty);
  return Decl;
}

int llvm_test_dibuilder(void) {
  const char *Filename = "debuginfo.c";
  LLVMModuleRef M = LLVMModuleCreateWithName(Filename);

  LLVMSetIsNewDbgInfoFormat(M, true);
  assert(LLVMIsNewDbgInfoFormat(M));

  LLVMDIBuilderRef DIB = LLVMCreateDIBuilder(M);

  LLVMMetadataRef File = LLVMDIBuilderCreateFile(DIB, Filename,
    strlen(Filename), ".", 1);

  LLVMMetadataRef CompileUnit = LLVMDIBuilderCreateCompileUnit(
      DIB, LLVMDWARFSourceLanguageC, File, "llvm-c-test", 11, 0, NULL, 0, 0,
      NULL, 0, LLVMDWARFEmissionFull, 0, 0, 0, "/", 1, "", 0);

  LLVMMetadataRef Module =
    LLVMDIBuilderCreateModule(DIB, CompileUnit,
                              "llvm-c-test", 11,
                              "", 0,
                              "/test/include/llvm-c-test.h", 27,
                              "", 0);

  LLVMMetadataRef OtherModule =
    LLVMDIBuilderCreateModule(DIB, CompileUnit,
                              "llvm-c-test-import", 18,
                              "", 0,
                              "/test/include/llvm-c-test-import.h", 34,
                              "", 0);
  LLVMMetadataRef ImportedModule = LLVMDIBuilderCreateImportedModuleFromModule(
      DIB, Module, OtherModule, File, 42, NULL, 0);
  LLVMDIBuilderCreateImportedModuleFromAlias(DIB, Module, ImportedModule, File,
                                             42, NULL, 0);

  LLVMMetadataRef ClassTy = declare_objc_class(DIB, File);
  LLVMMetadataRef GlobalClassValueExpr =
      LLVMDIBuilderCreateConstantValueExpression(DIB, 0);
  LLVMDIBuilderCreateGlobalVariableExpression(
      DIB, Module, "globalClass", 11, "", 0, File, 1, ClassTy, true,
      GlobalClassValueExpr, NULL, 0);

  LLVMMetadataRef Int64Ty =
      LLVMDIBuilderCreateBasicType(DIB, "Int64", 5, 64, 0, LLVMDIFlagZero);
  LLVMMetadataRef Int64TypeDef =
      LLVMDIBuilderCreateTypedef(DIB, Int64Ty, "int64_t", 7, File, 42, File, 0);

  LLVMMetadataRef GlobalVarValueExpr =
      LLVMDIBuilderCreateConstantValueExpression(DIB, 0);
  LLVMDIBuilderCreateGlobalVariableExpression(
      DIB, Module, "global", 6, "", 0, File, 1, Int64TypeDef, true,
      GlobalVarValueExpr, NULL, 0);

  LLVMMetadataRef NameSpace =
      LLVMDIBuilderCreateNameSpace(DIB, Module, "NameSpace", 9, false);

  LLVMMetadataRef StructDbgElts[] = {Int64Ty, Int64Ty, Int64Ty};
  LLVMMetadataRef StructDbgTy =
    LLVMDIBuilderCreateStructType(DIB, NameSpace, "MyStruct",
    8, File, 0, 192, 0, 0, NULL, StructDbgElts, 3,
    LLVMDWARFSourceLanguageC, NULL, "MyStruct", 8);

  LLVMMetadataRef StructDbgPtrTy =
    LLVMDIBuilderCreatePointerType(DIB, StructDbgTy, 192, 0, 0, "", 0);

  LLVMAddNamedMetadataOperand(M, "FooType",
    LLVMMetadataAsValue(LLVMGetModuleContext(M), StructDbgPtrTy));


  LLVMTypeRef FooParamTys[] = {
    LLVMInt64Type(),
    LLVMInt64Type(),
    LLVMVectorType(LLVMInt64Type(), 10),
  };
  LLVMTypeRef FooFuncTy = LLVMFunctionType(LLVMInt64Type(), FooParamTys, 3, 0);
  LLVMValueRef FooFunction = LLVMAddFunction(M, "foo", FooFuncTy);
  LLVMBasicBlockRef FooEntryBlock = LLVMAppendBasicBlock(FooFunction, "entry");

  LLVMMetadataRef Subscripts[] = {
    LLVMDIBuilderGetOrCreateSubrange(DIB, 0, 10),
  };
  LLVMMetadataRef VectorTy =
    LLVMDIBuilderCreateVectorType(DIB, 64 * 10, 0,
                                  Int64Ty, Subscripts, 1);


  LLVMMetadataRef ParamTypes[] = {Int64Ty, Int64Ty, VectorTy};
  LLVMMetadataRef FunctionTy =
    LLVMDIBuilderCreateSubroutineType(DIB, File, ParamTypes, 3, 0);

  LLVMMetadataRef ReplaceableFunctionMetadata =
    LLVMDIBuilderCreateReplaceableCompositeType(DIB, 0x15, "foo", 3,
                                                File, File, 42,
                                                0, 0, 0,
                                                LLVMDIFlagFwdDecl,
                                                "", 0);

  LLVMMetadataRef FooParamLocation =
    LLVMDIBuilderCreateDebugLocation(LLVMGetGlobalContext(), 42, 0,
                                     ReplaceableFunctionMetadata, NULL);
  LLVMMetadataRef FunctionMetadata =
    LLVMDIBuilderCreateFunction(DIB, File, "foo", 3, "foo", 3,
                                File, 42, FunctionTy, true, true,
                                42, 0, false);
  LLVMMetadataReplaceAllUsesWith(ReplaceableFunctionMetadata, FunctionMetadata);

  LLVMMetadataRef FooParamExpression =
    LLVMDIBuilderCreateExpression(DIB, NULL, 0);
  LLVMMetadataRef FooParamVar1 =
    LLVMDIBuilderCreateParameterVariable(DIB, FunctionMetadata, "a", 1, 1, File,
                                         42, Int64Ty, true, 0);

  LLVMDIBuilderInsertDeclareRecordAtEnd(
      DIB, LLVMConstInt(LLVMInt64Type(), 0, false), FooParamVar1,
      FooParamExpression, FooParamLocation, FooEntryBlock);

  LLVMMetadataRef FooParamVar2 =
    LLVMDIBuilderCreateParameterVariable(DIB, FunctionMetadata, "b", 1, 2, File,
                                         42, Int64Ty, true, 0);

  LLVMDIBuilderInsertDeclareRecordAtEnd(
      DIB, LLVMConstInt(LLVMInt64Type(), 0, false), FooParamVar2,
      FooParamExpression, FooParamLocation, FooEntryBlock);

  LLVMMetadataRef FooParamVar3 = LLVMDIBuilderCreateParameterVariable(
      DIB, FunctionMetadata, "c", 1, 3, File, 42, VectorTy, true, 0);

  LLVMDIBuilderInsertDeclareRecordAtEnd(
      DIB, LLVMConstInt(LLVMInt64Type(), 0, false), FooParamVar3,
      FooParamExpression, FooParamLocation, FooEntryBlock);

  LLVMSetSubprogram(FooFunction, FunctionMetadata);

  LLVMMetadataRef FooLabel1 = LLVMDIBuilderCreateLabel(DIB, FunctionMetadata,
    "label1", 6, File, 42, false);
  LLVMDIBuilderInsertLabelAtEnd(DIB, FooLabel1, FooParamLocation,
    FooEntryBlock);

  LLVMMetadataRef FooLexicalBlock =
    LLVMDIBuilderCreateLexicalBlock(DIB, FunctionMetadata, File, 42, 0);

  LLVMBasicBlockRef FooVarBlock = LLVMAppendBasicBlock(FooFunction, "vars");
  LLVMMetadataRef FooVarsLocation =
    LLVMDIBuilderCreateDebugLocation(LLVMGetGlobalContext(), 43, 0,
                                     FunctionMetadata, NULL);
  LLVMMetadataRef FooVar1 =
    LLVMDIBuilderCreateAutoVariable(DIB, FooLexicalBlock, "d", 1, File,
                                    43, Int64Ty, true, 0, 0);
  LLVMValueRef FooVal1 = LLVMConstInt(LLVMInt64Type(), 0, false);
  LLVMMetadataRef FooVarValueExpr1 =
      LLVMDIBuilderCreateConstantValueExpression(DIB, 0);

  LLVMDIBuilderInsertDbgValueRecordAtEnd(
      DIB, FooVal1, FooVar1, FooVarValueExpr1, FooVarsLocation, FooVarBlock);

  LLVMMetadataRef FooVar2 = LLVMDIBuilderCreateAutoVariable(
      DIB, FooLexicalBlock, "e", 1, File, 44, Int64Ty, true, 0, 0);
  LLVMValueRef FooVal2 = LLVMConstInt(LLVMInt64Type(), 1, false);
  LLVMMetadataRef FooVarValueExpr2 =
      LLVMDIBuilderCreateConstantValueExpression(DIB, 1);

  LLVMDIBuilderInsertDbgValueRecordAtEnd(
      DIB, FooVal2, FooVar2, FooVarValueExpr2, FooVarsLocation, FooVarBlock);

  LLVMMetadataRef MacroFile =
      LLVMDIBuilderCreateTempMacroFile(DIB, NULL, 0, File);
  LLVMDIBuilderCreateMacro(DIB, MacroFile, 0, LLVMDWARFMacinfoRecordTypeDefine,
                           "SIMPLE_DEFINE", 13, NULL, 0);
  LLVMDIBuilderCreateMacro(DIB, MacroFile, 0, LLVMDWARFMacinfoRecordTypeDefine,
                           "VALUE_DEFINE", 12, "1", 1);

  LLVMMetadataRef EnumeratorTestA =
      LLVMDIBuilderCreateEnumerator(DIB, "Test_A", strlen("Test_A"), 0, true);
  LLVMMetadataRef EnumeratorTestB =
      LLVMDIBuilderCreateEnumerator(DIB, "Test_B", strlen("Test_B"), 1, true);
  LLVMMetadataRef EnumeratorTestC =
      LLVMDIBuilderCreateEnumerator(DIB, "Test_B", strlen("Test_C"), 2, true);
  LLVMMetadataRef EnumeratorsTest[] = {EnumeratorTestA, EnumeratorTestB,
                                       EnumeratorTestC};
  LLVMMetadataRef EnumTest = LLVMDIBuilderCreateEnumerationType(
      DIB, NameSpace, "EnumTest", strlen("EnumTest"), File, 0, 64, 0,
      EnumeratorsTest, 3, Int64Ty);
  LLVMAddNamedMetadataOperand(
      M, "EnumTest", LLVMMetadataAsValue(LLVMGetModuleContext(M), EnumTest));

  // Using the new debug format, debug records get attached to instructions.
  // Insert a `br` and `ret` now to absorb the debug records which are
  // currently "trailing", meaning that they're associated with a block
  // but no particular instruction, which is only valid as a transient state.
  LLVMContextRef Ctx = LLVMGetModuleContext(M);
  LLVMBuilderRef Builder = LLVMCreateBuilderInContext(Ctx);
  LLVMPositionBuilderAtEnd(Builder, FooEntryBlock);
  // Build `br label %vars` in entry.
  LLVMBuildBr(Builder, FooVarBlock);

  // Build another br for the sake of testing labels.
  LLVMMetadataRef FooLabel2 = LLVMDIBuilderCreateLabel(DIB, FunctionMetadata,
    "label2", 6, File, 42, false);
  LLVMDIBuilderInsertLabelBefore(DIB, FooLabel2, FooParamLocation,
    LLVMBuildBr(Builder, FooVarBlock));
  // label3 will be emitted, but label4 won't be emitted
  // because label3 is AlwaysPreserve and label4 is not.
  LLVMDIBuilderCreateLabel(DIB, FunctionMetadata,
    "label3", 6, File, 42, true);
  LLVMDIBuilderCreateLabel(DIB, FunctionMetadata,
    "label4", 6, File, 42, false);
  LLVMDIBuilderFinalize(DIB);

  // Build `ret i64 0` in vars.
  LLVMPositionBuilderAtEnd(Builder, FooVarBlock);
  LLVMTypeRef I64 = LLVMInt64TypeInContext(Ctx);
  LLVMValueRef Zero = LLVMConstInt(I64, 0, false);
  LLVMValueRef Ret = LLVMBuildRet(Builder, Zero);

  // Insert a `phi` before the `ret`. In the new debug info mode we need to
  // be careful to insert before debug records too, else the debug records
  // will come before the `phi` (and be absorbed onto it) which is an invalid
  // state.
  LLVMValueRef InsertPos = LLVMGetFirstInstruction(FooVarBlock);
  LLVMPositionBuilderBeforeInstrAndDbgRecords(Builder, InsertPos);
  LLVMValueRef Phi1 = LLVMBuildPhi(Builder, I64, "p1");
  LLVMAddIncoming(Phi1, &Zero, &FooEntryBlock, 1);

  // Do the same again using the other position-setting function.
  LLVMPositionBuilderBeforeDbgRecords(Builder, FooVarBlock, InsertPos);
  LLVMValueRef Phi2 = LLVMBuildPhi(Builder, I64, "p2");
  LLVMAddIncoming(Phi2, &Zero, &FooEntryBlock, 1);

  // Insert a non-phi before the `ret` but not before the debug records to
  // test that works as expected.
  LLVMPositionBuilder(Builder, FooVarBlock, Ret);
  LLVMValueRef Add = LLVMBuildAdd(Builder, Phi1, Phi2, "a");

  // Iterate over debug records in the add instruction. There should be two.
  LLVMDbgRecordRef AddDbgRecordFirst = LLVMGetFirstDbgRecord(Add);
  assert(AddDbgRecordFirst != NULL);
  LLVMDbgRecordRef AddDbgRecordSecond = LLVMGetNextDbgRecord(AddDbgRecordFirst);
  assert(AddDbgRecordSecond != NULL);
  LLVMDbgRecordRef AddDbgRecordLast = LLVMGetLastDbgRecord(Add);
  assert(AddDbgRecordLast != NULL);
  (void)AddDbgRecordLast;
  assert(AddDbgRecordSecond == AddDbgRecordLast);
  LLVMDbgRecordRef AddDbgRecordOverTheRange =
      LLVMGetNextDbgRecord(AddDbgRecordSecond);
  assert(AddDbgRecordOverTheRange == NULL);
  (void)AddDbgRecordOverTheRange;
  LLVMDbgRecordRef AddDbgRecordFirstPrev =
      LLVMGetPreviousDbgRecord(AddDbgRecordSecond);
  assert(AddDbgRecordFirstPrev != NULL);
  assert(AddDbgRecordFirst == AddDbgRecordFirstPrev);
  LLVMDbgRecordRef AddDbgRecordUnderTheRange =
      LLVMGetPreviousDbgRecord(AddDbgRecordFirstPrev);
  assert(AddDbgRecordUnderTheRange == NULL);
  (void)AddDbgRecordUnderTheRange;

  char *MStr = LLVMPrintModuleToString(M);
  puts(MStr);
  LLVMDisposeMessage(MStr);

  LLVMDisposeBuilder(Builder);
  LLVMDisposeDIBuilder(DIB);
  LLVMDisposeModule(M);

  return 0;
}

int llvm_get_di_tag(void) {
  LLVMModuleRef M = LLVMModuleCreateWithName("Mod");
  LLVMContextRef Context = LLVMGetModuleContext(M);

  const char String[] = "foo";
  LLVMMetadataRef StringMD =
      LLVMMDStringInContext2(Context, String, strlen(String));
  LLVMMetadataRef NodeMD = LLVMMDNodeInContext2(Context, &StringMD, 1);
  assert(LLVMGetDINodeTag(NodeMD) == 0);
  (void)NodeMD;

  LLVMDIBuilderRef Builder = LLVMCreateDIBuilder(M);
  const char Filename[] = "metadata.c";
  const char Directory[] = ".";
  LLVMMetadataRef File = LLVMDIBuilderCreateFile(
      Builder, Filename, strlen(Filename), Directory, strlen(Directory));
  const char Name[] = "TestClass";
  LLVMMetadataRef Struct = LLVMDIBuilderCreateStructType(
      Builder, File, Name, strlen(Name), File, 42, 64, 0,
      LLVMDIFlagObjcClassComplete, NULL, NULL, 0, 0, NULL, NULL, 0);
  assert(LLVMGetDINodeTag(Struct) == 0x13);
  (void)Struct;

  LLVMDisposeDIBuilder(Builder);
  LLVMDisposeModule(M);

  return 0;
}

int llvm_di_type_get_name(void) {
  LLVMModuleRef M = LLVMModuleCreateWithName("Mod");

  LLVMDIBuilderRef Builder = LLVMCreateDIBuilder(M);
  const char Filename[] = "metadata.c";
  const char Directory[] = ".";
  LLVMMetadataRef File = LLVMDIBuilderCreateFile(
      Builder, Filename, strlen(Filename), Directory, strlen(Directory));
  const char Name[] = "TestClass";
  LLVMMetadataRef Struct = LLVMDIBuilderCreateStructType(
      Builder, File, Name, strlen(Name), File, 42, 64, 0,
      LLVMDIFlagObjcClassComplete, NULL, NULL, 0, 0, NULL, NULL, 0);

  size_t Len;
  const char *TypeName = LLVMDITypeGetName(Struct, &Len);
  assert(Len == strlen(Name));
  assert(strncmp(TypeName, Name, Len) == 0);
  (void)TypeName;

  LLVMDisposeDIBuilder(Builder);
  LLVMDisposeModule(M);

  return 0;
}
