| // 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_ |