assembler.h 7.12 KB
Newer Older
Kevin Modzelewski's avatar
Kevin Modzelewski committed
1
// Copyright (c) 2014-2015 Dropbox, Inc.
Kevin Modzelewski's avatar
Kevin Modzelewski committed
2
//
Kevin Modzelewski's avatar
Kevin Modzelewski committed
3 4 5
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Kevin Modzelewski's avatar
Kevin Modzelewski committed
6
//
Kevin Modzelewski's avatar
Kevin Modzelewski committed
7
//    http://www.apache.org/licenses/LICENSE-2.0
Kevin Modzelewski's avatar
Kevin Modzelewski committed
8
//
Kevin Modzelewski's avatar
Kevin Modzelewski committed
9 10 11 12 13 14 15 16 17 18 19
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PYSTON_ASMWRITING_ASSEMBLER_H
#define PYSTON_ASMWRITING_ASSEMBLER_H

#include <unordered_set>

20
#include "asm_writing/disassemble.h"
Kevin Modzelewski's avatar
Kevin Modzelewski committed
21
#include "asm_writing/types.h"
22
#include "codegen/stackmaps.h"
23
#include "core/ast.h"
24
#include "core/options.h"
Kevin Modzelewski's avatar
Kevin Modzelewski committed
25 26 27 28 29 30 31

namespace pyston {
namespace assembler {

class BoxedClass;

enum ConditionCode {
32 33
    COND_OVERFLOW = 0,     // OF=1: O
    COND_NOT_OVERFLOW = 1, // OF=0: NO
Kevin Modzelewski's avatar
Kevin Modzelewski committed
34
    // next 4 are unsigned:
35 36 37 38
    COND_BELOW = 2,         // CF=1: B/NAE/C
    COND_NOT_BELOW = 3,     // CF=0: NB/AE/C
    COND_EQUAL = 4,         // ZF=0: Z/E
    COND_NOT_EQUAL = 5,     // ZF=1: NZ/NE
Kevin Modzelewski's avatar
Kevin Modzelewski committed
39
    COND_NOT_ZERO = 5,      // ZF=1: NZ/NE
40 41 42 43 44 45
    COND_NOT_ABOVE = 6,     // CF=1: ZF=1: BE/NA
    COND_ABOVE = 7,         // CF=0: ZF=0: NBE/A
    COND_SIGN = 8,          // SF=1: S
    COND_NOT_SIGN = 9,      // SF=0: NS
    COND_PARITY_EVEN = 0xA, // PF=1: P/PE
    COND_PARITY_ODD = 0xB,  // PF=0: NP/PO
Kevin Modzelewski's avatar
Kevin Modzelewski committed
46
    // next 4 are signed:
47 48 49 50
    COND_LESS = 0xC,        // SF!=OF: L/NGE
    COND_NOT_LESS = 0xD,    // SF==OF: NL/GE
    COND_NOT_GREATER = 0xE, // ZF=1 || SF!=OF: LE/NG
    COND_GREATER = 0xF,     // ZF=0 && SF==OF: NLE/G
Kevin Modzelewski's avatar
Kevin Modzelewski committed
51 52
};

Travis Hance's avatar
Travis Hance committed
53 54 55 56 57
enum class MovType {
    Q,
    L,
    B,
    ZBL,
58 59 60 61 62 63 64 65 66 67
    SBL,
    ZWL,
    SWL,
    ZBQ,
    SBQ,
    ZWQ,
    SWQ,
    SLQ,

    ZLQ = L,
Travis Hance's avatar
Travis Hance committed
68 69
};

Kevin Modzelewski's avatar
Kevin Modzelewski committed
70
class Assembler {
71 72 73
private:
    uint8_t* const start_addr, *const end_addr;
    uint8_t* addr;
74
    bool failed; // if the rewrite failed at the assembly-generation level for some reason
75 76 77 78

    static const uint8_t OPCODE_ADD = 0b000, OPCODE_SUB = 0b101;
    static const uint8_t REX_B = 1, REX_X = 2, REX_R = 4, REX_W = 8;

79 80 81 82
#ifndef NDEBUG
    AssemblyLogger logger;
#endif

83 84 85 86 87 88 89 90
private:
    void emitByte(uint8_t b);
    void emitInt(int64_t n, int bytes);
    void emitRex(uint8_t rex);
    void emitModRM(uint8_t mod, uint8_t reg, uint8_t rm);
    void emitSIB(uint8_t scalebits, uint8_t index, uint8_t base);
    void emitArith(Immediate imm, Register reg, int opcode);

91 92
    int getModeFromOffset(int offset) const;

93
public:
94 95
    Assembler(uint8_t* start, int size) : start_addr(start), end_addr(start + size), addr(start_addr), failed(false) {}

96
#ifndef NDEBUG
97
    inline void comment(const llvm::Twine& msg) {
98 99 100 101 102 103 104 105 106 107 108 109
        if (ASSEMBLY_LOGGING) {
            logger.log_comment(msg, addr - start_addr);
        }
    }
    inline std::string dump() {
        if (ASSEMBLY_LOGGING) {
            return logger.finalize_log(start_addr, addr);
        } else {
            return "";
        }
    }
#else
110
    inline void comment(const llvm::Twine& msg) {}
111 112 113
    inline std::string dump() { return ""; }
#endif

114
    bool hasFailed() { return failed; }
115 116 117 118

    void nop() { emitByte(0x90); }
    void trap() { emitByte(0xcc); }

119 120
    // emits a movabs if the immediate is a 64bit value or force_64bit_load = true otherwise it emits a 32bit mov
    void mov(Immediate imm, Register dest, bool force_64bit_load = false);
121 122 123 124 125 126 127 128 129
    // not sure if we should use the 'q' suffix here, but this is the most ambiguous one;
    // this does a 64-bit store of a 32-bit value.
    void movq(Immediate imm, Indirect dest);
    void mov(Register src, Register dest);
    void mov(Register src, Indirect dest);
    void movsd(XMMRegister src, XMMRegister dest);
    void movsd(XMMRegister src, Indirect dest);
    void movsd(Indirect src, XMMRegister dest);

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    void movss(Indirect src, XMMRegister dest);
    void cvtss2sd(XMMRegister src, XMMRegister dest);

    void mov(Indirect scr, Register dest);
    void movq(Indirect scr, Register dest);
    void movl(Indirect scr, Register dest);
    void movb(Indirect scr, Register dest);
    void movzbl(Indirect scr, Register dest);
    void movsbl(Indirect scr, Register dest);
    void movzwl(Indirect scr, Register dest);
    void movswl(Indirect scr, Register dest);
    void movzbq(Indirect scr, Register dest);
    void movsbq(Indirect scr, Register dest);
    void movzwq(Indirect scr, Register dest);
    void movswq(Indirect scr, Register dest);
    void movslq(Indirect scr, Register dest);

    void mov_generic(Indirect src, Register dest, MovType type);

149 150 151 152 153
    void push(Register reg);
    void pop(Register reg);

    void add(Immediate imm, Register reg);
    void sub(Immediate imm, Register reg);
Travis Hance's avatar
Travis Hance committed
154

155 156
    void incl(Indirect mem);
    void decl(Indirect mem);
157

Travis Hance's avatar
Travis Hance committed
158 159 160 161
    void incl(Immediate mem);
    void decl(Immediate mem);

    void call(Immediate imm); // the value is the offset
162
    void callq(Register reg);
163
    void retq();
164
    void leave();
165 166 167 168 169 170

    void cmp(Register reg1, Register reg2);
    void cmp(Register reg, Immediate imm);
    void cmp(Indirect mem, Immediate imm);
    void cmp(Indirect mem, Register reg);

171 172
    void lea(Indirect mem, Register reg);

173 174 175 176
    void test(Register reg1, Register reg2);

    void jmp_cond(JumpDestination dest, ConditionCode condition);
    void jmp(JumpDestination dest);
177
    void jmp(Indirect dest);
178
    void jmpq(Register dest);
179 180 181 182 183 184 185 186 187 188 189 190
    void je(JumpDestination dest);
    void jne(JumpDestination dest);

    void set_cond(Register reg, ConditionCode condition);
    void sete(Register reg);
    void setz(Register reg) { sete(reg); }
    void setne(Register reg);
    void setnz(Register reg) { setne(reg); }


    // Macros:
    uint8_t* emitCall(void* func_addr, Register scratch);
191 192
    void emitBatchPop(int scratch_rbp_offset, int scratch_size, const std::vector<GenericRegister>& to_push);
    void emitBatchPush(int scratch_rbp_offset, int scratch_size, const std::vector<GenericRegister>& to_push);
193 194 195
    void fillWithNops();
    void fillWithNopsExcept(int bytes);
    void emitAnnotation(int num);
196
    void skipBytes(int num);
197

Travis Hance's avatar
Travis Hance committed
198
    uint8_t* startAddr() const { return start_addr; }
199 200
    int bytesLeft() const { return end_addr - addr; }
    int bytesWritten() const { return addr - start_addr; }
201
    uint8_t* curInstPointer() { return addr; }
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    void setCurInstPointer(uint8_t* ptr) { addr = ptr; }
    bool isExactlyFull() const { return addr == end_addr; }
    uint8_t* getStartAddr() { return start_addr; }
};

// This class helps generating a forward jump with a relative offset.
// It keeps track of the current assembler offset at construction time and in the destructor patches the
// generated conditional jump with the correct offset depending on the number of bytes emitted in between.
class ForwardJump {
private:
    const int max_jump_size = 128;
    Assembler& assembler;
    ConditionCode condition;
    uint8_t* jmp_inst;

public:
    ForwardJump(Assembler& assembler, ConditionCode condition);
    ~ForwardJump();
Kevin Modzelewski's avatar
Kevin Modzelewski committed
220 221
};

222 223
uint8_t* initializePatchpoint2(uint8_t* start_addr, uint8_t* slowpath_start, uint8_t* end_addr, StackInfo stack_info,
                               const std::unordered_set<int>& live_outs);
Kevin Modzelewski's avatar
Kevin Modzelewski committed
224 225 226 227
}
}

#endif