// [AsmJit]
// Complete JIT Assembler for C++ Language.
//
// [License]
// Zlib - See COPYING file in this package.

// [Guard]
#ifndef _ASMJIT_CORE_COMPILERITEM_H
#define _ASMJIT_CORE_COMPILERITEM_H

// [Dependencies - AsmJit]
#include "../core/compiler.h"

// [Api-Begin]
#include "../core/apibegin.h"

namespace AsmJit {

// ============================================================================
// [AsmJit::CompilerItem]
// ============================================================================

//! @brief Compiler item.
//!
//! @ref CompilerItem represents items generated by compiler by calling its
//! methods to build functions, instruction stream, bind labels, etc... Each
//! item contains its type information and virtual methods which are called at
//! various compilation steps.
struct CompilerItem
{
  ASMJIT_NO_COPY(CompilerItem)

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! @brief Create new @ref CompilerItem.
  //!
  //! @note Always use @ref Compiler to create an item!
  ASMJIT_API CompilerItem(Compiler* compiler, uint32_t type);

  //! @brief Destroy @ref CompilerItem.
  //!
  //! @note @ref Compiler keeps links into all items so it can destroy them 
  //! when code generation finished or in the case that @ref Compiler was 
  //! destroyed early (for example if an error happened). Never destroy items
  //! manually!
  ASMJIT_API virtual ~CompilerItem();

  // --------------------------------------------------------------------------
  // [Accessors]
  // --------------------------------------------------------------------------

  //! @brief Get associated compiler instance.
  inline Compiler* getCompiler() const
  { return _compiler; }

  //! @brief Get previous item in the compiler stream.
  inline CompilerItem* getPrev() const
  { return _prev; }

  //! @brief Get next item in the compiler stream.
  inline CompilerItem* getNext() const
  { return _next; }

  //! @brief Get comment string.
  inline const char* getComment() const
  { return _comment; }

  //! @brief Get type of item, see @ref kCompilerItem.
  inline uint32_t getType() const
  { return _type; }

  //! @brief Get whether the item was translated.
  inline bool isTranslated() const
  { return _isTranslated; }

  //! @brief Get whether the item is unreachable.
  inline bool isUnreachable() const
  { return _isUnreachable; }

  //! @brief Get the item offset in the compiler stream.
  //!
  //! The offset is not byte offset, each item increments offset by 1 and this
  //! value is then used by register allocator. The offset is set by compiler
  //! by the register allocator, don't use it in your code.
  inline uint32_t getOffset() const
  { return _offset; }

  // --------------------------------------------------------------------------
  // [Interface]
  // --------------------------------------------------------------------------

  //! @brief Step 1. Extract item variables, update statistics, ...
  ASMJIT_API virtual void prepare(CompilerContext& cc);
  //! @brief Step 2. Translate instruction, alloc variables, ...
  ASMJIT_API virtual CompilerItem* translate(CompilerContext& cc);
  //! @brief Step 3. Emit to @c Assembler.
  ASMJIT_API virtual void emit(Assembler& a);
  //! @brief Step 4. Last post step (verify, add data, etc).
  ASMJIT_API virtual void post(Assembler& a);

  // --------------------------------------------------------------------------
  // [Misc]
  // --------------------------------------------------------------------------

  //! @brief Get maximum size of of this when serialized into @ref Assembler 
  //! item in bytes.
  ASMJIT_API virtual int getMaxSize() const;

  //! @brief Try to unuse the variable @a.
  //!
  //! Returns @c true only if the variable will be unused by the instruction,
  //! otherwise @c false is returned.
  ASMJIT_API virtual bool _tryUnuseVar(CompilerVar* v);

  // --------------------------------------------------------------------------
  // [Comment]
  // --------------------------------------------------------------------------

  //! @brief Set comment string to @a str.
  ASMJIT_API void setComment(const char* str);

  //! @brief Format comment string using @a fmt string and variable argument list.
  ASMJIT_API void formatComment(const char* fmt, ...);

  // --------------------------------------------------------------------------
  // [Protected]
  // --------------------------------------------------------------------------

protected:
  //! @brief Mark item as translated and return next.
  inline CompilerItem* translated()
  {
    ASMJIT_ASSERT(_isTranslated == false);

    _isTranslated = true;
    return _next;
  }

  // --------------------------------------------------------------------------
  // [Members]
  // --------------------------------------------------------------------------

public:
  //! @brief Compiler which owns this item.
  Compiler* _compiler;
  //! @brief Previous item.
  CompilerItem* _prev;
  //! @brief Next item.
  CompilerItem* _next;
  //! @brief Inline comment string, initially set to NULL.
  const char* _comment;

  //! @brief Type of the item, see @ref kCompilerItem.
  uint32_t _type : 8;
  //! @brief Whether the item was translated, see @c translate().
  uint32_t _isTranslated : 1;
  //! @brief Whether the item is unreachable.
  uint32_t _isUnreachable : 1;
  //! @brief Reserved for future use.
  uint32_t _reserved : 22;

  //! @brief Stream offset (not byte-offset).
  uint32_t _offset;
};

// ============================================================================
// [AsmJit::CompilerMark]
// ============================================================================

//! @brief Compiler mark item.
//!
//! This item does nothing and it's only used by @ref Compiler to mark some
//! specific location in the compiler stream.
struct CompilerMark : public CompilerItem
{
  ASMJIT_NO_COPY(CompilerMark)

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! @brief Create a new @ref CompilerMark instance.
  ASMJIT_API CompilerMark(Compiler* compiler);
  //! @brief Destroy the @ref CompilerMark instance.
  ASMJIT_API virtual ~CompilerMark();

  // --------------------------------------------------------------------------
  // [Misc]
  // --------------------------------------------------------------------------

  ASMJIT_API virtual int getMaxSize() const;
};

// ============================================================================
// [AsmJit::CompilerComment]
// ============================================================================

//! @brief Compiler comment item.
//!
//! Comments allows to comment your assembler stream for better debugging
//! and visualization. Comments are usually ignored in release builds unless
//! the logger is present.
struct CompilerComment : public CompilerItem
{
  ASMJIT_NO_COPY(CompilerComment)

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! @brief Create a new @ref CompilerComment instance.
  ASMJIT_API CompilerComment(Compiler* compiler, const char* comment = NULL);
  //! @brief Destroy the @ref CompilerComment instance.
  ASMJIT_API virtual ~CompilerComment();

  // --------------------------------------------------------------------------
  // [Interface]
  // --------------------------------------------------------------------------

  ASMJIT_API virtual void emit(Assembler& a);

  // --------------------------------------------------------------------------
  // [Misc]
  // --------------------------------------------------------------------------

  ASMJIT_API virtual int getMaxSize() const;
};

// ============================================================================
// [AsmJit::CompilerEmbed]
// ============================================================================

//! @brief Compiler embed item.
//!
//! Embed item is used to embed data into final assembler stream. The data is
//! considered to be RAW, there is no analysis.
struct CompilerEmbed : public CompilerItem
{
  ASMJIT_NO_COPY(CompilerEmbed)

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! @brief Create a new @ref CompilerEmbed instance.
  ASMJIT_API CompilerEmbed(Compiler* compiler, const void* data, size_t length);
  //! @brief Destroy the @ref CompilerEmbed instance.
  ASMJIT_API virtual ~CompilerEmbed();

  // --------------------------------------------------------------------------
  // [Accessors]
  // --------------------------------------------------------------------------

  //! @brief Get pointer to embedded data.
  uint8_t* getData() const
  { return const_cast<uint8_t*>(_data); }

  //! @brief Get length of embedded data.
  size_t getLength() const
  { return _length; }

  // --------------------------------------------------------------------------
  // [Interface]
  // --------------------------------------------------------------------------

  ASMJIT_API virtual void emit(Assembler& a);

  // --------------------------------------------------------------------------
  // [Misc]
  // --------------------------------------------------------------------------

  ASMJIT_API virtual int getMaxSize() const;

  // --------------------------------------------------------------------------
  // [Members]
  // --------------------------------------------------------------------------

  //! @brief Data length.
  size_t _length;
  //! @brief Data buffer (that will be embedded to the assembler stream).
  uint8_t _data[sizeof(void*)];
};

// ============================================================================
// [AsmJit::CompilerAlign]
// ============================================================================

//! @brief Compiler align item.
struct CompilerAlign : public CompilerItem
{
  ASMJIT_NO_COPY(CompilerAlign)

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! @brief Create a new @ref CompilerAlign instance.
  ASMJIT_API CompilerAlign(Compiler* compiler, uint32_t size = 0);
  //! @brief Destroy the @ref CompilerAlign instance.
  ASMJIT_API virtual ~CompilerAlign();

  // --------------------------------------------------------------------------
  // [Accessors]
  // --------------------------------------------------------------------------

  //! @brief Get align size in bytes.
  inline uint32_t getSize() const
  { return _size; }

  //! @brief Set align size in bytes to @a size.
  inline void setSize(uint32_t size)
  { _size = size; }

  // --------------------------------------------------------------------------
  // [Misc]
  // --------------------------------------------------------------------------

  ASMJIT_API virtual int getMaxSize() const;

  // --------------------------------------------------------------------------
  // [Members]
  // --------------------------------------------------------------------------

  //! @brief Align size.
  uint32_t _size;
};

// ============================================================================
// [AsmJit::CompilerHint]
// ============================================================================

//! @brief Compiler variable hint item.
struct CompilerHint : public CompilerItem
{
  ASMJIT_NO_COPY(CompilerHint)

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! @brief Create a new @ref CompilerHint instance.
  ASMJIT_API CompilerHint(Compiler* compiler, CompilerVar* var, uint32_t hintId, uint32_t hintValue);
  //! @brief Destroy the @ref CompilerHint instance.
  ASMJIT_API virtual ~CompilerHint();

  // --------------------------------------------------------------------------
  // [Accessors]
  // --------------------------------------------------------------------------

  //! @brief Get variable.
  inline CompilerVar* getVar() const
  { return _var; }

  //! @brief Get hint it (see @ref kVarHint).
  inline uint32_t getHintId() const
  { return _hintId; }
  
  //! @brief Set hint it (see @ref kVarHint).
  inline void setHintId(uint32_t hintId)
  { _hintId = hintId; }

  //! @brief Get hint value.
  inline uint32_t getHintValue() const
  { return _hintValue; }
  
  //! @brief Set hint value.
  inline void setHintValue(uint32_t hintValue)
  { _hintValue = hintValue; }

  // --------------------------------------------------------------------------
  // [Members]
  // --------------------------------------------------------------------------

  //! @brief Variable.
  CompilerVar* _var;
  //! @brief Variable hint id.
  uint32_t _hintId;
  //! @brief Variable hint value.
  uint32_t _hintValue;
};

// ============================================================================
// [AsmJit::CompilerTarget]
// ============================================================================

//! @brief Compiler target item.
struct CompilerTarget : public CompilerItem
{
  ASMJIT_NO_COPY(CompilerTarget)

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! @brief Create a new @ref CompilerTarget instance.
  ASMJIT_API CompilerTarget(Compiler* compiler, const Label& target);
  //! @brief Destroy the @ref CompilerTarget instance.
  ASMJIT_API virtual ~CompilerTarget();

  // --------------------------------------------------------------------------
  // [Accessors]
  // --------------------------------------------------------------------------

  //! @brief Return label bound to this target.
  inline const Label& getLabel() const
  { return _label; }

  //! @brief Get first jmp instruction.
  inline CompilerInst* getFrom() const
  { return _from; }

  //! @brief Get register allocator state for this target.
  inline CompilerState* getState() const
  { return _state; }

  //! @brief Get number of jumps to this target.
  inline uint32_t getJumpsCount() const
  { return _jumpsCount; }

  // --------------------------------------------------------------------------
  // [Misc]
  // --------------------------------------------------------------------------

  ASMJIT_API virtual int getMaxSize() const;

  // --------------------------------------------------------------------------
  // [Members]
  // --------------------------------------------------------------------------

  //! @brief Label.
  Label _label;
  //! @brief First jump instruction that points to this target (label).
  CompilerInst* _from;
  //! @brief State at this location.
  CompilerState* _state;
  //! @brief Count of jumps here.
  uint32_t _jumpsCount;
};

// ============================================================================
// [AsmJit::CompilerInst]
// ============================================================================

//! @brief Compiler instruction item.
struct CompilerInst : public CompilerItem
{
  ASMJIT_NO_COPY(CompilerInst)

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! @brief Create a new @ref CompilerInst instance.
  ASMJIT_API CompilerInst(Compiler* compiler, uint32_t code, Operand* opData, uint32_t opCount);
  //! @brief Destroy the @ref CompilerInst instance.
  ASMJIT_API virtual ~CompilerInst();

  // --------------------------------------------------------------------------
  // [Accessors]
  // --------------------------------------------------------------------------

  //! @brief Get instruction code, see @c kInstCode.
  inline uint32_t getCode() const
  { return _code; }

  //! @brief Set instruction code to @a code.
  //!
  //! Please do not modify instruction code if you are not know what you are
  //! doing. Incorrect instruction code or operands can raise assertion() at
  //! runtime.
  inline void setCode(uint32_t code)
  { _code = code; }

  //! @brief Get emit options (compiler specific).
  inline uint32_t getEmitOptions() const
  { return _emitOptions; }

  //! @brief Get instruction flags (compiler specific).
  inline uint32_t getInstFlags() const
  { return _instFlags; }

  //! @brief Get whether the instruction has flag @a flag.
  inline bool hasInstFlag(uint8_t flag) const
  { return (_instFlags & flag) != 0; }

  //! @brief Set instruction @a flag.
  inline void setInstFlag(uint8_t flag)
  { _instFlags |= flag; }

  //! @brief Clear instruction @a flag.
  inline void clearInstFlag(uint8_t flag)
  { _instFlags &= ~flag; }

  //! @brief Get count of operands in operands array.
  inline uint32_t getOperandsCount() const
  { return _operandsCount; }

  //! @brief Get count of variables in variables array.
  inline uint32_t getVariablesCount() const
  { return _variablesCount; }

  //! @brief Get operands array (3 operands total).
  inline Operand* getOperands() { return _operands; }
  //! @brief Get operands array (3 operands total).
  inline const Operand* getOperands() const { return _operands; }

  // --------------------------------------------------------------------------
  // [GetJumpTarget]
  // --------------------------------------------------------------------------

  //! @brief Get possible jump target.
  //!
  //! If this instruction is conditional or normal jump then return value is
  //! the label location (@ref CompilerTarget), otherwise the return value is 
  //! @c NULL.
  ASMJIT_API virtual CompilerTarget* getJumpTarget() const;

  // --------------------------------------------------------------------------
  // [Members]
  // --------------------------------------------------------------------------

  //! @brief Instruction code, see @c kInstCode.
  uint32_t _code;

  //! @brief Emit options.
  uint8_t _emitOptions;
  //! @brief Instruction flags.
  uint8_t _instFlags;
  //! @brief Operands count.
  uint8_t _operandsCount;
  //! @brief Variables count.
  uint8_t _variablesCount;

  //! @brief Operands.
  Operand* _operands;
};

} // AsmJit namespace

// [Api-End]
#include "../core/apiend.h"

// [Guard]
#endif // _ASMJIT_CORE_COMPILERITEM_H
