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