Machine.h (5903B)
1 /* GRAPHITE2 LICENSING 2 3 Copyright 2010, SIL International 4 All rights reserved. 5 6 This library is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should also have received a copy of the GNU Lesser General Public 17 License along with this library in the file named "LICENSE". 18 If not, write to the Free Software Foundation, 51 Franklin Street, 19 Suite 500, Boston, MA 02110-1335, USA or visit their web page on the 20 internet at http://www.fsf.org/licenses/lgpl.html. 21 22 Alternatively, the contents of this file may be used under the terms of the 23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public 24 License, as published by the Free Software Foundation, either version 2 25 of the License or (at your option) any later version. 26 */ 27 // This general interpreter interface. 28 // Author: Tim Eves 29 30 // Build one of direct_machine.cpp or call_machine.cpp to implement this 31 // interface. 32 33 #pragma once 34 #include <cstring> 35 #include <limits> 36 #include <graphite2/Types.h> 37 #include "inc/Main.h" 38 39 #if defined(__GNUC__) 40 #if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ * 10) < 430 41 #define HOT 42 #if defined(__x86_64) 43 #define REGPARM(n) __attribute__((regparm(n))) 44 #else 45 #define REGPARM(n) 46 #endif 47 #else 48 #define HOT __attribute__((hot)) 49 #if defined(__x86_64) 50 #define REGPARM(n) __attribute__((hot, regparm(n))) 51 #else 52 #define REGPARM(n) 53 #endif 54 #endif 55 #else 56 #define HOT 57 #define REGPARM(n) 58 #endif 59 60 #if defined(__MINGW32__) 61 // MinGW's <limits> at some point includes winnt.h which #define's a 62 // DELETE macro, which conflicts with enum opcode below, so we undefine 63 // it here. 64 #undef DELETE 65 #endif 66 67 namespace graphite2 { 68 69 // Forward declarations 70 class Segment; 71 class Slot; 72 class SlotMap; 73 74 75 namespace vm 76 { 77 78 79 typedef void * instr; 80 typedef Slot * slotref; 81 82 enum {VARARGS = 0xff, MAX_NAME_LEN=32}; 83 84 enum opcode { 85 NOP = 0, 86 87 PUSH_BYTE, PUSH_BYTEU, PUSH_SHORT, PUSH_SHORTU, PUSH_LONG, 88 89 ADD, SUB, MUL, DIV, 90 MIN_, MAX_, 91 NEG, 92 TRUNC8, TRUNC16, 93 94 COND, 95 96 AND, OR, NOT, 97 EQUAL, NOT_EQ, 98 LESS, GTR, LESS_EQ, GTR_EQ, 99 100 NEXT, NEXT_N, COPY_NEXT, 101 PUT_GLYPH_8BIT_OBS, PUT_SUBS_8BIT_OBS, PUT_COPY, 102 INSERT, DELETE, 103 ASSOC, 104 CNTXT_ITEM, 105 106 ATTR_SET, ATTR_ADD, ATTR_SUB, 107 ATTR_SET_SLOT, 108 IATTR_SET_SLOT, 109 PUSH_SLOT_ATTR, PUSH_GLYPH_ATTR_OBS, 110 PUSH_GLYPH_METRIC, PUSH_FEAT, 111 PUSH_ATT_TO_GATTR_OBS, PUSH_ATT_TO_GLYPH_METRIC, 112 PUSH_ISLOT_ATTR, 113 114 PUSH_IGLYPH_ATTR, // not implemented 115 116 POP_RET, RET_ZERO, RET_TRUE, 117 IATTR_SET, IATTR_ADD, IATTR_SUB, 118 PUSH_PROC_STATE, PUSH_VERSION, 119 PUT_SUBS, PUT_SUBS2, PUT_SUBS3, 120 PUT_GLYPH, PUSH_GLYPH_ATTR, PUSH_ATT_TO_GLYPH_ATTR, 121 BITOR, BITAND, BITNOT, 122 BITSET, SET_FEAT, 123 MAX_OPCODE, 124 // private opcodes for internal use only, comes after all other on disk opcodes 125 TEMP_COPY = MAX_OPCODE 126 }; 127 128 struct opcode_t 129 { 130 instr impl[2]; 131 uint8 param_sz; 132 char name[MAX_NAME_LEN]; 133 }; 134 135 136 class Machine 137 { 138 public: 139 typedef int32 stack_t; 140 static size_t const STACK_ORDER = 10, 141 STACK_MAX = 1 << STACK_ORDER, 142 STACK_GUARD = 2; 143 144 class Code; 145 146 enum status_t { 147 finished = 0, 148 stack_underflow, 149 stack_not_empty, 150 stack_overflow, 151 slot_offset_out_bounds, 152 died_early 153 }; 154 155 Machine(SlotMap &) throw(); 156 static const opcode_t * getOpcodeTable() throw(); 157 158 CLASS_NEW_DELETE; 159 160 SlotMap & slotMap() const throw(); 161 status_t status() const throw(); 162 // operator bool () const throw(); 163 164 private: 165 void check_final_stack(const stack_t * const sp); 166 stack_t run(const instr * program, const byte * data, 167 slotref * & map) HOT; 168 169 SlotMap & _map; 170 stack_t _stack[STACK_MAX + 2*STACK_GUARD]; 171 status_t _status; 172 }; 173 174 inline Machine::Machine(SlotMap & map) throw() 175 : _map(map), _status(finished) 176 { 177 // Initialise stack guard +1 entries as the stack pointer points to the 178 // current top of stack, hence the first push will never write entry 0. 179 // Initialising the guard space like this is unnecessary and is only 180 // done to keep valgrind happy during fuzz testing. Hopefully loop 181 // unrolling will flatten this. 182 for (size_t n = STACK_GUARD + 1; n; --n) _stack[n-1] = 0; 183 } 184 185 inline SlotMap& Machine::slotMap() const throw() 186 { 187 return _map; 188 } 189 190 inline Machine::status_t Machine::status() const throw() 191 { 192 return _status; 193 } 194 195 inline void Machine::check_final_stack(const stack_t * const sp) 196 { 197 if (_status != finished) return; 198 199 stack_t const * const base = _stack + STACK_GUARD, 200 * const limit = base + STACK_MAX; 201 if (sp < base) _status = stack_underflow; // This should be impossible now. 202 else if (sp >= limit) _status = stack_overflow; // So should this. 203 else if (sp != base) _status = stack_not_empty; 204 } 205 206 } // namespace vm 207 } // namespace graphite2