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
44namespace graphite2 {
45
46// Forward declarations
47class Segment;
48class Slot;
49class SlotMap;
50
51
52namespace vm
53{
54
55
56typedef void * instr;
57typedef Slot * slotref;
58
59enum {VARARGS = 0xff, MAX_NAME_LEN=32};
60
61enum 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
105struct opcode_t
106{
107 instr impl[2];
108 uint8 param_sz;
109 char name[MAX_NAME_LEN];
110};
111
112
113class Machine
114{
115public:
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
141private:
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
151inline 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
162inline SlotMap& Machine::slotMap() const throw()
163{
164 return _map;
165}
166
167inline Machine::status_t Machine::status() const throw()
168{
169 return _status;
170}
171
172inline 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