| 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 | |