michael@0: /* GRAPHITE2 LICENSING michael@0: michael@0: Copyright 2010, SIL International michael@0: All rights reserved. michael@0: michael@0: This library is free software; you can redistribute it and/or modify michael@0: it under the terms of the GNU Lesser General Public License as published michael@0: by the Free Software Foundation; either version 2.1 of License, or michael@0: (at your option) any later version. michael@0: michael@0: This program is distributed in the hope that it will be useful, michael@0: but WITHOUT ANY WARRANTY; without even the implied warranty of michael@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU michael@0: Lesser General Public License for more details. michael@0: michael@0: You should also have received a copy of the GNU Lesser General Public michael@0: License along with this library in the file named "LICENSE". michael@0: If not, write to the Free Software Foundation, 51 Franklin Street, michael@0: Suite 500, Boston, MA 02110-1335, USA or visit their web page on the michael@0: internet at http://www.fsf.org/licenses/lgpl.html. michael@0: michael@0: Alternatively, the contents of this file may be used under the terms of the michael@0: Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public michael@0: License, as published by the Free Software Foundation, either version 2 michael@0: of the License or (at your option) any later version. michael@0: */ michael@0: // This general interpreter interface. michael@0: // Author: Tim Eves michael@0: michael@0: // Build one of direct_machine.cpp or call_machine.cpp to implement this michael@0: // interface. michael@0: michael@0: #pragma once michael@0: #include michael@0: #include michael@0: #include "inc/Main.h" michael@0: michael@0: #if defined(__GNUC__) michael@0: #if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ * 10) < 430 michael@0: #define HOT michael@0: #if defined(__x86_64) michael@0: #define REGPARM(n) __attribute__((regparm(n))) michael@0: #else michael@0: #define REGPARM(n) michael@0: #endif michael@0: #else michael@0: #define HOT __attribute__((hot)) michael@0: #if defined(__x86_64) michael@0: #define REGPARM(n) __attribute__((hot, regparm(n))) michael@0: #else michael@0: #define REGPARM(n) michael@0: #endif michael@0: #endif michael@0: #else michael@0: #define HOT michael@0: #define REGPARM(n) michael@0: #endif michael@0: michael@0: namespace graphite2 { michael@0: michael@0: // Forward declarations michael@0: class Segment; michael@0: class Slot; michael@0: class SlotMap; michael@0: michael@0: michael@0: namespace vm michael@0: { michael@0: michael@0: michael@0: typedef void * instr; michael@0: typedef Slot * slotref; michael@0: michael@0: enum {VARARGS = 0xff, MAX_NAME_LEN=32}; michael@0: michael@0: enum opcode { michael@0: NOP = 0, michael@0: michael@0: PUSH_BYTE, PUSH_BYTEU, PUSH_SHORT, PUSH_SHORTU, PUSH_LONG, michael@0: michael@0: ADD, SUB, MUL, DIV, michael@0: MIN_, MAX_, michael@0: NEG, michael@0: TRUNC8, TRUNC16, michael@0: michael@0: COND, michael@0: michael@0: AND, OR, NOT, michael@0: EQUAL, NOT_EQ, michael@0: LESS, GTR, LESS_EQ, GTR_EQ, michael@0: michael@0: NEXT, NEXT_N, COPY_NEXT, michael@0: PUT_GLYPH_8BIT_OBS, PUT_SUBS_8BIT_OBS, PUT_COPY, michael@0: INSERT, DELETE, michael@0: ASSOC, michael@0: CNTXT_ITEM, michael@0: michael@0: ATTR_SET, ATTR_ADD, ATTR_SUB, michael@0: ATTR_SET_SLOT, michael@0: IATTR_SET_SLOT, michael@0: PUSH_SLOT_ATTR, PUSH_GLYPH_ATTR_OBS, michael@0: PUSH_GLYPH_METRIC, PUSH_FEAT, michael@0: PUSH_ATT_TO_GATTR_OBS, PUSH_ATT_TO_GLYPH_METRIC, michael@0: PUSH_ISLOT_ATTR, michael@0: michael@0: PUSH_IGLYPH_ATTR, // not implemented michael@0: michael@0: POP_RET, RET_ZERO, RET_TRUE, michael@0: IATTR_SET, IATTR_ADD, IATTR_SUB, michael@0: PUSH_PROC_STATE, PUSH_VERSION, michael@0: PUT_SUBS, PUT_SUBS2, PUT_SUBS3, michael@0: PUT_GLYPH, PUSH_GLYPH_ATTR, PUSH_ATT_TO_GLYPH_ATTR, michael@0: MAX_OPCODE, michael@0: // private opcodes for internal use only, comes after all other on disk opcodes michael@0: TEMP_COPY = MAX_OPCODE michael@0: }; michael@0: michael@0: struct opcode_t michael@0: { michael@0: instr impl[2]; michael@0: uint8 param_sz; michael@0: char name[MAX_NAME_LEN]; michael@0: }; michael@0: michael@0: michael@0: class Machine michael@0: { michael@0: public: michael@0: typedef int32 stack_t; michael@0: static size_t const STACK_ORDER = 10, michael@0: STACK_MAX = 1 << STACK_ORDER, michael@0: STACK_GUARD = 2; michael@0: michael@0: class Code; michael@0: michael@0: enum status_t { michael@0: finished = 0, michael@0: stack_underflow, michael@0: stack_not_empty, michael@0: stack_overflow, michael@0: slot_offset_out_bounds michael@0: }; michael@0: michael@0: Machine(SlotMap &) throw(); michael@0: static const opcode_t * getOpcodeTable() throw(); michael@0: michael@0: CLASS_NEW_DELETE; michael@0: michael@0: SlotMap & slotMap() const throw(); michael@0: status_t status() const throw(); michael@0: operator bool () const throw(); michael@0: michael@0: private: michael@0: void check_final_stack(const stack_t * const sp); michael@0: stack_t run(const instr * program, const byte * data, michael@0: slotref * & map) HOT; michael@0: michael@0: SlotMap & _map; michael@0: stack_t _stack[STACK_MAX + 2*STACK_GUARD]; michael@0: status_t _status; michael@0: }; michael@0: michael@0: inline Machine::Machine(SlotMap & map) throw() michael@0: : _map(map), _status(finished) michael@0: { michael@0: // Initialise stack guard +1 entries as the stack pointer points to the michael@0: // current top of stack, hence the first push will never write entry 0. michael@0: // Initialising the guard space like this is unnecessary and is only michael@0: // done to keep valgrind happy during fuzz testing. Hopefully loop michael@0: // unrolling will flatten this. michael@0: for (size_t n = STACK_GUARD + 1; n; --n) _stack[n-1] = 0; michael@0: } michael@0: michael@0: inline SlotMap& Machine::slotMap() const throw() michael@0: { michael@0: return _map; michael@0: } michael@0: michael@0: inline Machine::status_t Machine::status() const throw() michael@0: { michael@0: return _status; michael@0: } michael@0: michael@0: inline void Machine::check_final_stack(const int32 * const sp) michael@0: { michael@0: stack_t const * const base = _stack + STACK_GUARD, michael@0: * const limit = base + STACK_MAX; michael@0: if (sp < base) _status = stack_underflow; // This should be impossible now. michael@0: else if (sp >= limit) _status = stack_overflow; // So should this. michael@0: else if (sp != base) _status = stack_not_empty; michael@0: } michael@0: michael@0: } // namespace vm michael@0: } // namespace graphite2 michael@0: michael@0: michael@0: