// AsmJit - Machine code generation for C++
//
//  * Official AsmJit Home Page: https://asmjit.com
//  * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

#ifndef ASMJIT_CORE_FORMATTER_H_INCLUDED
#define ASMJIT_CORE_FORMATTER_H_INCLUDED

#include "../core/inst.h"
#include "../core/string.h"

#ifndef ASMJIT_NO_LOGGING

ASMJIT_BEGIN_NAMESPACE

//! \addtogroup asmjit_logging
//! \{

// ============================================================================
// [Forward Declarations]
// ============================================================================

class BaseEmitter;
struct Operand_;

#ifndef ASMJIT_NO_BUILDER
class BaseBuilder;
class BaseNode;
#endif

// ============================================================================
// [asmjit::FormatOptions]
// ============================================================================

//! Formatting options used by \ref Logger and \ref Formatter.
class FormatOptions {
public:
  //! Format flags, see \ref Flags.
  uint32_t _flags;
  //! Indentation by type, see \ref IndentationType.
  uint8_t _indentation[4];

  //! Flags can enable a logging feature.
  enum Flags : uint32_t {
    //! No flags.
    kNoFlags = 0u,

    //! Show also binary form of each logged instruction (Assembler).
    kFlagMachineCode = 0x00000001u,
    //! Show a text explanation of some immediate values.
    kFlagExplainImms = 0x00000002u,
    //! Use hexadecimal notation of immediate values.
    kFlagHexImms = 0x00000004u,
    //! Use hexadecimal notation of address offsets.
    kFlagHexOffsets = 0x00000008u,
    //! Show casts between virtual register types (Compiler).
    kFlagRegCasts = 0x00000010u,
    //! Show positions associated with nodes (Compiler).
    kFlagPositions = 0x00000020u,
    //! Annotate nodes that are lowered by passes.
    kFlagAnnotations = 0x00000040u,

    // TODO: These must go, keep this only for formatting.
    //! Show an additional output from passes.
    kFlagDebugPasses = 0x00000080u,
    //! Show an additional output from RA.
    kFlagDebugRA = 0x00000100u
  };

  //! Describes indentation type of code, label, or comment in logger output.
  enum IndentationType : uint32_t {
    //! Indentation used for instructions and directives.
    kIndentationCode = 0u,
    //! Indentation used for labels and function nodes.
    kIndentationLabel = 1u,
    //! Indentation used for comments (not inline comments).
    kIndentationComment = 2u,
    //! \cond INTERNAL
    //! Reserved for future use.
    kIndentationReserved = 3u
    //! \endcond
  };

  //! \name Construction & Destruction
  //! \{

  //! Creates a default-initialized FormatOptions.
  constexpr FormatOptions() noexcept
    : _flags(0),
      _indentation { 0, 0, 0, 0 } {}

  constexpr FormatOptions(const FormatOptions& other) noexcept = default;
  inline FormatOptions& operator=(const FormatOptions& other) noexcept = default;

  //! Resets FormatOptions to its default initialized state.
  inline void reset() noexcept {
    _flags = 0;
    _indentation[0] = 0;
    _indentation[1] = 0;
    _indentation[2] = 0;
    _indentation[3] = 0;
  }

  //! \}

  //! \name Accessors
  //! \{

  //! Returns format flags.
  constexpr uint32_t flags() const noexcept { return _flags; }
  //! Tests whether the given `flag` is set in format flags.
  constexpr bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
  //! Resets all format flags to `flags`.
  inline void setFlags(uint32_t flags) noexcept { _flags = flags; }
  //! Adds `flags` to format flags.
  inline void addFlags(uint32_t flags) noexcept { _flags |= flags; }
  //! Removes `flags` from format flags.
  inline void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; }

  //! Returns indentation for the given `type`, see \ref IndentationType.
  constexpr uint8_t indentation(uint32_t type) const noexcept { return _indentation[type]; }
  //! Sets indentation for the given `type`, see \ref IndentationType.
  inline void setIndentation(uint32_t type, uint32_t n) noexcept { _indentation[type] = uint8_t(n); }
  //! Resets indentation for the given `type` to zero.
  inline void resetIndentation(uint32_t type) noexcept { _indentation[type] = uint8_t(0); }

  //! \}
};

// ============================================================================
// [asmjit::Formatter]
// ============================================================================

//! Provides formatting functionality to format operands, instructions, and nodes.
namespace Formatter {

//! Appends a formatted `typeId` to the output string `sb`.
ASMJIT_API Error formatTypeId(
  String& sb,
  uint32_t typeId) noexcept;

//! Appends a formatted `featureId` to the output string `sb`.
//!
//! See \ref BaseFeatures.
ASMJIT_API Error formatFeature(
  String& sb,
  uint32_t arch,
  uint32_t featureId) noexcept;

//! Appends a formatted register to the output string `sb`.
//!
//! \note Emitter is optional, but it's required to format virtual registers,
//! which won't be formatted properly if the `emitter` is not provided.
ASMJIT_API Error formatRegister(
  String& sb,
  uint32_t formatFlags,
  const BaseEmitter* emitter,
  uint32_t arch,
  uint32_t regType,
  uint32_t regId) noexcept;

//! Appends a formatted label to the output string `sb`.
//!
//! \note Emitter is optional, but it's required to format named labels
//! properly, otherwise the formatted as it is an anonymous label.
ASMJIT_API Error formatLabel(
  String& sb,
  uint32_t formatFlags,
  const BaseEmitter* emitter,
  uint32_t labelId) noexcept;

//! Appends a formatted operand to the output string `sb`.
//!
//! \note Emitter is optional, but it's required to format named labels and
//! virtual registers. See \ref formatRegister() and \ref formatLabel() for
//! more details.
ASMJIT_API Error formatOperand(
  String& sb,
  uint32_t formatFlags,
  const BaseEmitter* emitter,
  uint32_t arch,
  const Operand_& op) noexcept;

//! Appends a formatted instruction to the output string `sb`.
//!
//! \note Emitter is optional, but it's required to format named labels and
//! virtual registers. See \ref formatRegister() and \ref formatLabel() for
//! more details.
ASMJIT_API Error formatInstruction(
  String& sb,
  uint32_t formatFlags,
  const BaseEmitter* emitter,
  uint32_t arch,
  const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept;

#ifndef ASMJIT_NO_BUILDER
//! Appends a formatted node to the output string `sb`.
//!
//! The `node` must belong to the provided `builder`.
ASMJIT_API Error formatNode(
  String& sb,
  uint32_t formatFlags,
  const BaseBuilder* builder,
  const BaseNode* node) noexcept;

//! Appends formatted nodes to the output string `sb`.
//!
//! All nodes that are part of the given `builder` will be appended.
ASMJIT_API Error formatNodeList(
  String& sb,
  uint32_t formatFlags,
  const BaseBuilder* builder) noexcept;

//! Appends formatted nodes to the output string `sb`.
//!
//! This function works the same as \ref formatNode(), but appends more nodes
//! to the output string, separating each node with a newline '\n' character.
ASMJIT_API Error formatNodeList(
  String& sb,
  uint32_t formatFlags,
  const BaseBuilder* builder,
  const BaseNode* begin,
  const BaseNode* end) noexcept;
#endif

} // {Formatter}

//! \}

ASMJIT_END_NAMESPACE

#endif

#endif // ASMJIT_CORE_FORMATTER_H_INCLUDED
