diff options
Diffstat (limited to 'src/parser.h')
| -rw-r--r-- | src/parser.h | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/src/parser.h b/src/parser.h index 0c389cc..75a3432 100644 --- a/src/parser.h +++ b/src/parser.h @@ -25,6 +25,7 @@ #include "goto.h" #include "undo.h" #include "qreg.h" +#include "memory.h" #include "lexer.h" /* @@ -481,8 +482,12 @@ typedef enum { struct teco_machine_main_t { teco_machine_t parent; + /** currently executed macro */ + const gchar *macro; /** Program counter, i.e. pointer to the next character in the current macro frame */ gsize macro_pc; + /** possibly preliminary end of macro */ + gsize stop_pos; struct teco_machine_main_flags_t { teco_mode_t mode : 8; @@ -550,6 +555,71 @@ gboolean teco_machine_main_eval_at(teco_machine_main_t *ctx); gboolean teco_machine_main_step(teco_machine_main_t *ctx, const gchar *macro, gsize stop_pos, GError **error); +#if __has_attribute(musttail) + +/** + * Transition from a teco_machine_main input_cb() to another state. + * + * Instead of returning to the caller, this can tail call, + * which will often be optimized to direct jumps, turning + * state transitions into "threaded" code. + * In some macros this achieves up to 18% speedup. + * + * On the other hand, this works only from functions with + * compatible signatures (practically only from + * teco_state_t::input_cb()). + * When this optimization is used, other function calls + * may no longer be tail-call optimized. + * + * @see teco_machine_main_step + * @see teco_machine_input + */ +#define TECO_RETURN(CTX, STATE, ERROR) G_STMT_START { \ + teco_machine_main_t *const __ctx = (CTX); \ + teco_state_t *const __state = (STATE); \ + GError **const __error = (ERROR); \ + \ + if (!__state) \ + return NULL; \ + \ + if (G_UNLIKELY(teco_interface_is_interrupted())) { \ + teco_error_interrupted_set(__error); \ + return NULL; \ + } \ + \ + if (!teco_memory_check(0, __error)) \ + return NULL; \ + \ + if (__ctx->macro_pc >= __ctx->stop_pos) \ + return __state; /* terminate tail calls */ \ + \ + if (__state != __ctx->parent.current) { \ + if (__ctx->parent.must_undo) \ + teco_undo_ptr(__ctx->parent.current); \ + __ctx->parent.current = __state; \ + \ + if (__ctx->parent.current->initial_cb && \ + !__ctx->parent.current->initial_cb(&__ctx->parent, __error)) \ + return NULL; \ + } \ + \ + gunichar chr = g_utf8_get_char(__ctx->macro+__ctx->macro_pc); \ + __ctx->macro_pc = g_utf8_next_char(__ctx->macro+__ctx->macro_pc) - __ctx->macro; \ + \ + /* the caller's signature will not line up exactly with input_cb */ \ + typedef teco_state_t *(*caller_t)(teco_machine_main_t *, gunichar, GError **); \ + __attribute__((musttail)) return ((caller_t)__state->input_cb)(__ctx, chr, __error); \ +} G_STMT_END + +#else /* __has_attribute(musttail) */ + +/* + * This will effectively return into the teco_machine_main_step() loop. + */ +#define TECO_RETURN(CTX, STATE, ERROR) return (STATE) + +#endif /* !__has_attribute(musttail) */ + gboolean teco_execute_macro(const gchar *macro, gsize macro_len, teco_qreg_table_t *qreg_table_locals, GError **error); gboolean teco_execute_file(const gchar *filename, teco_qreg_table_t *qreg_table_locals, GError **error); |
