//===--- IRGenDebugInfo.h - Debug Info Support ------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines IR codegen support for debug information.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_IRGEN_DEBUGINFO_H
#define SWIFT_IRGEN_DEBUGINFO_H

#include <swift/SIL/SILInstruction.h>
#include "DebugTypeInfo.h"
#include "IRGenFunction.h"

namespace llvm {
class DIBuilder;
}

namespace swift {

class ClangImporter;
class IRGenOptions;

enum class SILFunctionTypeRepresentation : uint8_t;

namespace irgen {

class IRBuilder;
class IRGenFunction;
class IRGenModule;

enum ArtificialKind : bool { RealValue = false, ArtificialValue = true };

/// Used to signal to emitDbgIntrinsic which kind of debug intrinsic to emit.
/// - DbgDeclare: llvm.dbg.declare (variable lives at this address for its
///   entire lifetime). The leading DW_OP_deref is stripped because declare
///   implicitly provides one level of indirection.
/// - DbgValue: llvm.dbg.value (expression evaluated as-is; all derefs are
///   explicit).
/// - DbgDeclareValue: llvm.dbg.declare_value (coro context variant). The
///   leading DW_OP_deref is stripped like DbgDeclare.
enum class AddrDbgInstrKind : uint8_t {
  DbgDeclare,
  DbgValue,
  DbgDeclareValue,
};

/// Helper object that keeps track of the current CompileUnit, File,
/// LexicalScope, and knows how to translate a \c SILLocation into an
/// \c llvm::DebugLoc.
class IRGenDebugInfo {
public:
  static std::unique_ptr<IRGenDebugInfo>
  createIRGenDebugInfo(const IRGenOptions &Opts, ClangImporter &CI,
                       IRGenModule &IGM, llvm::Module &M,
                       StringRef MainOutputFilenameForDebugInfo,
                       StringRef PrivateDiscriminator,
                       StringRef CacheKeyForJob);
  virtual ~IRGenDebugInfo();

  /// Finalize the llvm::DIBuilder owned by this object.
  void finalize();

  /// Update the IRBuilder's current debug location to the location
  /// Loc and the lexical scope DS.
  void setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
                     SILLocation Loc);

  /// Replace the current debug location in \p Builder with the same location,
  /// but contained in an inlined function which is named like \p failureMsg.
  ///
  /// This lets the debugger display the \p failureMsg as an inlined function
  /// frame.
  void addFailureMessageToCurrentLoc(IRBuilder &Builder, StringRef failureMsg);

  void clearLoc(IRBuilder &Builder);

  /// Push the current debug location onto a stack and initialize the
  /// IRBuilder to an empty location.
  void pushLoc();

  /// Restore the current debug location from the stack.
  void popLoc();

  /// If we are not emitting CodeView, this does nothing since the ``llvm.trap``
  /// instructions should already have an artificial location of zero.
  /// In CodeView, since zero is not an artificial location, we emit the
  /// location of the unified trap block at the end of the function as an
  /// artificial inline location pointing to the user's instruction.
  void setInlinedTrapLocation(IRBuilder &Builder, const SILDebugScope *Scope);

  /// Set the location for entry point function (main by default).
  void setEntryPointLoc(IRBuilder &Builder);

  /// Return the scope for the entry point function (main by default).
  llvm::DIScope *getEntryPointFn();

  /// Translate a SILDebugScope into an llvm::DIDescriptor.
  llvm::DIScope *getOrCreateScope(const SILDebugScope *DS);

  
  /// Emit debug info for an import declaration.
  ///
  /// The DWARF output for import decls is similar to that of a using
  /// directive in C++:
  ///   import Foundation
  ///   -->
  ///   0: DW_TAG_imported_module
  ///        DW_AT_import(*1)
  ///   1: DW_TAG_module // instead of DW_TAG_namespace.
  ///        DW_AT_name("Foundation")
  ///
  void emitImport(ImportDecl *D);

  /// Emit debug info for the given function.
  /// \param DS The parent scope of the function.
  /// \param Fn The IR representation of the function.
  /// \param Rep The calling convention of the function.
  /// \param Ty The signature of the function.
  llvm::DISubprogram *emitFunction(const SILDebugScope *DS, llvm::Function *Fn,
                                   SILFunctionTypeRepresentation Rep,
                                   SILType Ty, DeclContext *DeclCtx = nullptr,
                                   GenericEnvironment *GE = nullptr);

  /// Emit debug info for a given SIL function.
  llvm::DISubprogram *emitFunction(SILFunction &SILFn, llvm::Function *Fn);

  /// Convenience function useful for functions without any source
  /// location. Internally calls emitFunction, emits a debug
  /// scope, and finally sets it using setCurrentLoc.
  inline void emitArtificialFunction(IRGenFunction &IGF, llvm::Function *Fn,
                                     SILType SILTy = SILType()) {
    emitArtificialFunction(IGF.Builder, Fn, SILTy);
  }

  void emitArtificialFunction(IRBuilder &Builder,
                              llvm::Function *Fn, SILType SILTy = SILType());

  inline void emitOutlinedFunction(IRGenFunction &IGF,
                                   llvm::Function *Fn,
                                   StringRef outlinedFromName) {
    emitOutlinedFunction(IGF.Builder, Fn, outlinedFromName);
  }

  void emitOutlinedFunction(IRBuilder &Builder,
                            llvm::Function *Fn,
                            StringRef outlinedFromName);

  /// Emit a dbg.declare intrinsic at the current insertion point and
  /// the Builder's current debug location.
  void emitVariableDeclaration(IRBuilder &Builder,
                               ArrayRef<llvm::Value *> Storage,
                               DebugTypeInfo Ty, const SILDebugScope *DS,
                               std::optional<SILLocation> VarLoc,
                               SILDebugVariable VarInfo,
                               bool InCoroContext = false,
                               ArtificialKind Artificial = RealValue,
                               AddrDbgInstrKind = AddrDbgInstrKind::DbgDeclare);

  /// Emit a dbg.value or dbg.declare intrinsic, depending on Storage. If \p
  /// AddrDbgInstrKind is set to DbgDeclare, then instead of emitting a
  /// dbg.value, we will insert a dbg.declare. Please only use that if you know
  /// that the given value can never be moved and have its lifetime ended early
  /// (e.x.: type metadata).
  void emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage,
                        llvm::DILocalVariable *Var, llvm::DIExpression *Expr,
                        unsigned Line, unsigned Col, llvm::DILocalScope *Scope,
                        const SILDebugScope *DS, bool InCoroContext = false,
                        AddrDbgInstrKind = AddrDbgInstrKind::DbgDeclare);

  /// Create debug metadata for a global variable.
  void emitGlobalVariableDeclaration(llvm::GlobalVariable *Storage,
                                     StringRef Name, StringRef LinkageName,
                                     DebugTypeInfo DebugType,
                                     bool IsLocalToUnit,
                                     std::optional<SILLocation> Loc);

  /// Emit debug metadata for type metadata (for generic types). So meta.
  void emitTypeMetadata(IRGenFunction &IGF, llvm::Value *Metadata,
                        unsigned Depth, unsigned Index, StringRef Name);

  /// Emit debug info for the IR function parameter holding the size of one or
  /// more parameter / type packs.
  void emitPackCountParameter(IRGenFunction &IGF, llvm::Value *Metadata,
                              SILDebugVariable VarInfo);

  /// Emit and retain complete debug info for a type that is only ever used as
  /// the payload of an existential.
  ///
  /// Debug info is otherwise only emitted for types that some variable or
  /// parameter has, so a type that appears solely inside an existential gets
  /// none.
  void emitExistentialPayloadType(swift::Type Ty);

  /// Return the DIBuilder.
  llvm::DIBuilder &getBuilder();
};

/// An RAII object that autorestores the debug location.
class AutoRestoreLocation {
  IRGenDebugInfo *DI;
  IRBuilder &Builder;
  llvm::DebugLoc SavedLocation;

public:
  AutoRestoreLocation(IRGenDebugInfo *DI, IRBuilder &Builder);
  /// Autorestore everything back to normal.
  ~AutoRestoreLocation();
};

/// An RAII object that temporarily switches to an artificial debug
/// location that has a valid scope, but no line information. This is
/// useful when emitting compiler-generated instructions (e.g.,
/// ARC-inserted calls to release()) that have no source location
/// associated with them. The DWARF specification allows the compiler
/// to use the special line number 0 to indicate code that cannot be
/// attributed to any source location.
class ArtificialLocation : public AutoRestoreLocation {
public:
  /// Set the current location to line 0, but within scope DS.
  ArtificialLocation(const SILDebugScope *DS, IRGenDebugInfo *DI,
                     IRBuilder &Builder);
};

/// An RAII object that temporarily switches to an empty
/// location. This is how the function prologue is represented.
class PrologueLocation : public AutoRestoreLocation {
public:
  /// Set the current location to an empty location.
  PrologueLocation(IRGenDebugInfo *DI, IRBuilder &Builder);
};

} // irgen
} // swift

#endif
