// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Copyright (c) 1994-2006 Sun Microsystems Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistribution in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// - Neither the name of Sun Microsystems or the names of contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// The original source code covered by the above license above has been
// modified significantly by Google Inc.
// Copyright 2006-2008 the V8 project authors. All rights reserved.

// This implements a C++ assembler for dynamically generating machine code.
// It is heavily based on the v8 assembler, which has a long history of its
// own.  Relocation information has been removed, and in general things were
// made a bit simpler (and slower).  Everything is implemented inline.

#ifndef TRACELINE_ASSEMBLER_H_
#define TRACELINE_ASSEMBLER_H_

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

#include <stddef.h>

#include "logging.h"

#define ASSERT(x) CHECK(x)

enum Register {
  EAX = 0,
  ECX = 1,
  EDX = 2,
  EBX = 3,
  ESP = 4,
  EBP = 5,
  ESI = 6,
  EDI = 7
};

enum Condition {
  overflow      =  0,
  no_overflow   =  1,
  below         =  2,
  above_equal   =  3,
  equal         =  4,
  not_equal     =  5,
  below_equal   =  6,
  above         =  7,
  sign          =  8,
  not_sign      =  9,
  parity_even   = 10,
  parity_odd    = 11,
  less          = 12,
  greater_equal = 13,
  less_equal    = 14,
  greater       = 15,

  // aliases
  zero          = equal,
  not_zero      = not_equal,
  negative      = sign,
  positive      = not_sign
};

// Labels are used for branching, and marks an offset in the CodeBuffer.
// A label can be in 3 states:
//  - Unused, the label has never be used in an instruction.
//  - Linked, the label has been referenced (by a jump, for example), but the
//    target is not yet known, because the label is unbound.
//  - Bound, the label has been bound so the offset is known.
class Label {
 public:
  Label() { Unuse(); }
  ~Label() { ASSERT(!is_linked()); }

  void Unuse() {
    num_ = 0;
  }

  bool is_unused() const { return num_ == 0; }
  bool is_bound() const { return num_ == -1; }
  bool is_linked() const { return num_ > 0; }

  int binding_pos() const {
    ASSERT(is_bound());
    return table_[0];
  }

  int num_links() const {
    ASSERT(!is_bound());
    return num_;  // Will return 0 if unused.
  }

  int link_pos(int i) const {
    ASSERT(is_linked());
    ASSERT(i < num_);
    return table_[i];
  }

 private:
  void bind_to(int pos)  {
    ASSERT(!is_bound());
    table_[0] = pos;
    num_ = -1;
  }
  void link_to(int pos)  {
    ASSERT(!is_bound());
    ASSERT(num_ < kTableSize);

    table_[num_] = pos;
    ++num_;
  }

  static const int kTableSize = 3;

  // We store all links in a fixed size table.  When we're bound, we store the
  // binding position in the first entry of the table.
  int table_[kTableSize];
  // The number of entries in our table, if we're linked.  If 0, then we're
  // unusued.  If -1, then we are bound (and the pos is at table_[0]).
  int num_;

  friend class CodeBuffer;  // For binding, linking, etc
};


enum ScaleFactor {
  SCALE_TIMES_1 = 0,
  SCALE_TIMES_2 = 1,
  SCALE_TIMES_4 = 2,
  SCALE_TIMES_8 = 3
};


class Operand {
 public:
  explicit Operand(const Operand& x) : len_(x.len_) {
    memcpy(buf_, x.buf_, sizeof(buf_));
  }

  // reg
  explicit Operand(Register reg) {
    Init(reg);
  }

  // [disp/r]
  explicit Operand(int disp) {
    Init(disp);
  }

  // [base + disp/r]
  Operand(Register base, int disp) {
    Init(base, disp);
  }

  // [base + index*scale + disp/r]
  Operand(Register base, Register index, ScaleFactor scale, int disp) {
    Init(base, index, scale, disp);
  }

  // [index*scale + disp/r]
  Operand(Register index, ScaleFactor scale, int disp) {
    Init(index, scale, disp);
  }

  void set_reg(Register reg) {
    ASSERT(len_ > 0);
    buf_[0] = (buf_[0] & ~0x38) | static_cast<char>(reg << 3);
  }

  char* data() { return buf_; }
  int length() { return len_; }

 private:
  // reg
  void Init(Register reg) {
    set_modrm(3, reg);
  }

  // [disp/r]
  void Init(int disp) {
    set_modrm(0, EBP);
    set_dispr(disp);
  }

  // [base + disp/r]
  void Init(Register base, int disp) {
    if (disp == 0) {
      // [base]
      set_modrm(0, base);
      if (base == ESP) set_sib(SCALE_TIMES_1, ESP, base);
    } else if (is_int8(disp)) {
      // [base + disp8]
      set_modrm(1, base);
      if (base == ESP) set_sib(SCALE_TIMES_1, ESP, base);
      set_disp8(disp);
    } else {
      // [base + disp/r]
      set_modrm(2, base);
      if (base == ESP) set_sib(SCALE_TIMES_1, ESP, base);
      set_dispr(disp);
    }
  }

  // [base + index*scale + disp/r]
  void Init(Register base,
            Register index,
            ScaleFactor scale,
            int disp) {
    ASSERT(index != ESP);  // illegal addressing mode
    if (disp == 0 && base != EBP) {
      // [base + index*scale]
      set_modrm(0, ESP);
      set_sib(scale, index, base);
    } else if (is_int8(disp)) {
      // [base + index*scale + disp8]
      set_modrm(1, ESP);
      set_sib(scale, index, base);
      set_disp8(disp);
    } else {
      // [base + index*scale + disp/r]
      set_modrm(2, ESP);
      set_sib(scale, index, base);
      set_dispr(disp);
    }
  }

  // [index*scale + disp/r]
  void Init(Register index,
            ScaleFactor scale,
            int disp) {
    ASSERT(index != ESP);  // illegal addressing mode
    // We can reduce instruction size by translating instructions of the form:
    //   8D044510000000    lea eax,[eax*2+0x10]
    // To the more concise scale=1 version:
    //   8D440010          lea eax,[eax+eax+0x10]
    if (scale == SCALE_TIMES_2) {
      Init(index, index, SCALE_TIMES_1, disp);
    } else {
      set_modrm(0, ESP);
      set_sib(scale, index, EBP);
      set_dispr(disp);
    }
  }

  // Returns true if this Operand is a wrapper for the specified register.
  bool is_reg(Register reg) const {
    return ((buf_[0] & 0xF8) == 0xC0)  // addressing mode is register only.
        && ((buf_[0] & 0x07) == reg);  // register codes match.
  }

  void set_modrm(int mod, Register rm) {  // reg == 0
    ASSERT((mod & -4) == 0);
    buf_[0] = mod << 6 | rm;
    len_ = 1;
  }

  void set_sib(ScaleFactor scale, Register index, Register base) {
    ASSERT(len_ == 1);
    ASSERT((scale & -4) == 0);
    buf_[1] = scale << 6 | index << 3 | base;
    len_ = 2;
  }

  void set_disp8(char disp) {
    ASSERT(len_ == 1 || len_ == 2);
    *reinterpret_cast<char*>(&buf_[len_++]) = disp;
  }

  void set_dispr(int disp) {
    ASSERT(len_ == 1 || len_ == 2);
    *reinterpret_cast<int*>(&buf_[len_]) = disp;
    len_ += sizeof(int);
  }

  bool is_int8(int x) { return x >= -128 && x <= 127; }

  // Mutable because reg in ModR/M byte is set by Assembler via set_reg().
  char buf_[6];
  // The number of bytes in buf_.
  unsigned int len_;
};

// A convenient wrapper around a buffer for emitting code or data, etc.
class CodeBuffer {
 public:
  // Use an externally managed buffer
  explicit CodeBuffer(char* buf) : pos_(0), buf_(buf) { }

  void* data() { return buf_; }
  int size() { return pos_; }

  void emit(unsigned char b) {
    buf_[pos_++] = b;
  }
  void emit_word(unsigned short w) {
    *reinterpret_cast<unsigned short*>(&buf_[pos_]) = w;
    pos_ += 2;
  }
  void emit_dword(unsigned int d) {
    *reinterpret_cast<unsigned int*>(&buf_[pos_]) = d;
    pos_ += 4;
  }

  void emit_bytes(const char* bytes, size_t size) {
    for (size_t i = 0; i < size; ++i)
      emit(bytes[i]);
  }

  void emit_bytes(const std::string& bytes) {
    emit_bytes(bytes.data(), bytes.size());
  }

  void put_dword_at(int pos, unsigned int d) {
    *reinterpret_cast<unsigned int*>(&buf_[pos]) = d;
  }

  // We pass by value so that we get a copy that we can modify.
  void emit_operand(Register reg, Operand operand) {
    operand.set_reg(reg);
    memcpy(&buf_[pos_], operand.data(), operand.length());
    pos_ += operand.length();
  }

  void bind(Label* l) {
    ASSERT(!l->is_bound());
    for (int i = 0; i < l->num_links(); ++i) {
      put_dword_at(l->link_pos(i), pos_ - (l->link_pos(i) + 4));
    }
    l->bind_to(pos_);
  }

  // TODO deprecate blah_imm and use blah(Immediate)

  void add(Register dst, Register src) {
    emit(0x01); emit(0xc0 | (src << 3) | dst);
  }
  void add_imm(Register dst, int d) {
    if (d >= -128 && d <= 127) {
      emit(0x83); emit(0xc0 | dst); emit(d & 0xff);
    } else {
      emit(0x81); emit(0xc0 | dst); emit_dword(d);
    }
  }

  void and_(Register r, unsigned int mask) {
    emit(0x81); emit(0xe0 | r); emit_dword(mask);
  }

  void call(Register r) {
    call(Operand(r));
  }
  void call(const Operand& dst) {
    emit(0xff); emit_operand(EDX, dst);
  }

  void cmp(Register r1, Register r2) {
    emit(0x39); emit(0xc0 | (r2 << 3) | r1);
  }

  void cmp_imm(Register r, int d) {
    if (d >= -128 && d <= 127) {
      emit(0x83); emit(0xf8 | r); emit(d & 0xff);
    } else {
      emit(0x81); emit(0xf8 | r); emit_dword(d);
    }
  }

  void fs() {
    emit(0x64);
  }

  // Atomically increment the dword at |mem| with the increment amount in the
  // register |inc|.  Will replace |inc| with the old unincremented value.
  void inc_atomic(Register mem, Register inc) {
    // lock xadd [mem], inc
    emit(0xF0); emit(0x0F); emit(0xC1); emit((inc << 3) | mem);
  }

  void int3() {
    emit(0xcc);
  }

  void jcc(Condition cc, Label* l) {
    emit(0x0f); emit(0x80 | cc);
    if (l->is_bound()) {
      emit_dword(l->binding_pos() - (pos_ + 4));
    } else {
      // Will fix up when the label is bound.
      l->link_to(pos_);
      emit_dword(0);
    }
  }

  void jmp(Register r) {
    emit(0xff); emit(0xe0 | r);
  }

  void jmp(Label* l) {
    if (l->is_bound()) {
      jmp_rel(l->binding_pos() - (pos_ + 5));
    } else {
      // Will fix up when the label is bound.
      l->link_to(pos_ + 1);
      jmp_rel(0);
    }
  }

  void jmp_rel(int i) {
    emit(0xe9); emit_dword(i);
  }

  void jmp_rel_short(char c) {
    emit(0xeb); emit(c);
  }

  void lea(Register dst, const Operand& src) {
    emit(0x8d); emit_operand(dst, src);
  }

  void lodsb() {
    emit(0xac);
  }
  void lodsd() {
    emit(0xad);
  }

  void loop(Label* l) {
    ASSERT(l->is_bound());
    int pos = l->binding_pos() - (pos_ + 2);
    ASSERT(pos >= -128 && pos < 0);

    emit(0xe2); emit(pos & 0xff);
  }

  void mov(Register dst, Register src) {
    emit(0x89); emit(0xc0 | (src << 3) | dst);
  }
  void mov(Register dst, const Operand& src) {
    emit(0x8b); emit_operand(dst, src);
  }
  void mov_imm(Register r, unsigned int d) {
    emit(0xb8 | r); emit_dword(d);
  }

  void movsb() {
    emit(0xa4);
  }
  void movsd() {
    emit(0xa5);
  }

  void or_(Register r, unsigned int mask) {
    emit(0x81); emit(0xc8 | r); emit_dword(mask);
  }

  void pop(Register r) {
    emit(0x58 | r);
  }
  void pop(const Operand& dst) {
    emit(0x8f); emit_operand(EAX, dst);
  }

  void push(Register r) {
    emit(0x50 | r);
  }
  void push(const Operand& src) {
    emit(0xff); emit_operand(ESI, src);
  }
  void push_imm(int i) {
    if (i >= -128 && i <= 127) {
      emit(0x6a); emit(i & 0xff);
    } else {
      emit(0x68); emit_dword(i);
    }
  }

  // Puts the cycle counter into edx:eax.
  void rdtsc() {
    emit(0x0F); emit(0x31);
  }

  void rep() {
    emit(0xf3);
  }

  void ret() {
    ret(0);
  }
  void ret(short c) {
    if (c == 0) {
      emit(0xc3);
    } else {
      emit(0xc2); emit_word(c);
    }
  }

  void spin() {
    jmp_rel_short(-2);
  }

  void stosb() {
    emit(0xaa);
  }
  void stosd() {
    emit(0xab);
  }

  void sysenter() {
    emit(0x0f); emit(0x34);
  }

  // Puts a unique cpu identifier into eax, using sidt to fingerprint cores.
  void which_cpu() {
    // Make space
    push(EAX);
    push(EAX);
    // sidt [esp+2]
    emit(0x0f); emit(0x01); emit_operand(ECX, Operand(ESP, 2));
    pop(EAX);
    pop(EAX);  // sidt address
  }

  // Puts a unique identifier for the thread we're executing on into eax.
  void which_thread() {
    // mov eax, [fs:0x24]
    emit(0x64); emit(0xa1); emit_dword(0x24);
    // TODO: We could do this but it will use an encoding that is 1 byte bigger.
    // fs(); mov(EAX, Operand(0x24));
  }

  void xchg(Register r1, Register r2) {
    if (r1 == EAX) {
      emit(0x90 | r2);
    } else if (r2 == EAX) {
      emit(0x90 | r1);
    } else {
      xchg(r1, Operand(r2));
    }
  }
  void xchg(Register r1, const Operand& oper) {
    emit(0x87); emit_operand(r1, oper);
  }

 private:
  int pos_;
  char* buf_;
};

#endif  // TRACELINE_ASSEMBLER_H_
