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 direct threaded interpreter implmentation for machine.h |
5 | // Author: Tim Eves |
6 | |
7 | // Build either this interpreter or the call_machine implementation. |
8 | // The direct threaded interpreter is relies upon a gcc feature called |
9 | // labels-as-values so is only portable to compilers that support the |
10 | // extension (gcc only as far as I know) however it should build on any |
11 | // architecture gcc supports. |
12 | // This is twice as fast as the call threaded model and is likely faster on |
13 | // inorder processors with short pipelines and little branch prediction such |
14 | // as the ARM and possibly Atom chips. |
15 | |
16 | |
17 | #include <cassert> |
18 | #include <cstring> |
19 | #include "inc/Machine.h" |
20 | #include "inc/Segment.h" |
21 | #include "inc/Slot.h" |
22 | #include "inc/Rule.h" |
23 | |
24 | #define STARTOP(name) name: { |
25 | #define ENDOP }; goto *((sp - sb)/Machine::STACK_MAX ? &&end : *++ip); |
26 | #define EXIT(status) { push(status); goto end; } |
27 | |
28 | #define do_(name) &&name |
29 | |
30 | |
31 | using namespace graphite2; |
32 | using namespace vm; |
33 | |
34 | namespace { |
35 | |
36 | // The GCC manual has this to say about labels as values: |
37 | // The &&foo expressions for the same label might have different values |
38 | // if the containing function is inlined or cloned. If a program relies |
39 | // on them being always the same, __attribute__((__noinline__,__noclone__)) |
40 | // should be used to prevent inlining and cloning. |
41 | // |
42 | // is_return in Code.cpp relies on being able to do comparisons, so it needs |
43 | // them to be always the same. |
44 | // |
45 | // The GCC manual further adds: |
46 | // If &&foo is used in a static variable initializer, inlining and |
47 | // cloning is forbidden. |
48 | // |
49 | // In this file, &&foo *is* used in a static variable initializer, and it's not |
50 | // entirely clear whether this should prevent inlining of the function or not. |
51 | // In practice, though, clang 7 can end up inlining the function with ThinLTO, |
52 | // which breaks at least is_return. https://bugs.llvm.org/show_bug.cgi?id=39241 |
53 | // So all in all, we need at least the __noinline__ attribute. __noclone__ |
54 | // is not supported by clang. |
55 | __attribute__((__noinline__)) |
56 | const void * direct_run(const bool get_table_mode, |
57 | const instr * program, |
58 | const byte * data, |
59 | Machine::stack_t * stack, |
60 | slotref * & __map, |
61 | uint8 _dir, |
62 | Machine::status_t & status, |
63 | SlotMap * __smap=0) |
64 | { |
65 | // We need to define and return to opcode table from within this function |
66 | // other inorder to take the addresses of the instruction bodies. |
67 | #include "inc/opcode_table.h" |
68 | if (get_table_mode) |
69 | return opcode_table; |
70 | |
71 | // Declare virtual machine registers |
72 | const instr * ip = program; |
73 | const byte * dp = data; |
74 | Machine::stack_t * sp = stack + Machine::STACK_GUARD, |
75 | * const sb = sp; |
76 | SlotMap & smap = *__smap; |
77 | Segment & seg = smap.segment; |
78 | slotref is = *__map, |
79 | * map = __map, |
80 | * const mapb = smap.begin()+smap.context(); |
81 | uint8 dir = _dir; |
82 | int8 flags = 0; |
83 | |
84 | // start the program |
85 | goto **ip; |
86 | |
87 | // Pull in the opcode definitions |
88 | #include "inc/opcodes.h" |
89 | |
90 | end: |
91 | __map = map; |
92 | *__map = is; |
93 | return sp; |
94 | } |
95 | |
96 | } |
97 | |
98 | const opcode_t * Machine::getOpcodeTable() throw() |
99 | { |
100 | slotref * dummy; |
101 | Machine::status_t dumstat = Machine::finished; |
102 | return static_cast<const opcode_t *>(direct_run(true, 0, 0, 0, dummy, 0, dumstat)); |
103 | } |
104 | |
105 | |
106 | Machine::stack_t Machine::run(const instr * program, |
107 | const byte * data, |
108 | slotref * & is) |
109 | { |
110 | assert(program != 0); |
111 | |
112 | const stack_t *sp = static_cast<const stack_t *>( |
113 | direct_run(false, program, data, _stack, is, _map.dir(), _status, &_map)); |
114 | const stack_t ret = sp == _stack+STACK_GUARD+1 ? *sp-- : 0; |
115 | check_final_stack(sp); |
116 | return ret; |
117 | } |
118 | |