aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core-commands.c
diff options
context:
space:
mode:
authorRobin Haberkorn <rhaberkorn@fmsbw.de>2026-08-22 22:27:33 +0200
committerRobin Haberkorn <rhaberkorn@fmsbw.de>2026-08-22 22:27:33 +0200
commit2e097cec409182c3cb39b74489387480bf8a278f (patch)
tree47f501fa2634835cc0c70a9214f01b32f49b9d30 /src/core-commands.c
parent3ae89e34f6a04dd553f3871856521334a4dbe62f (diff)
optimize main state machine transitions with tail calls
* On newer GCC (>= 15) and Clang (>= 13) versions we can tail call at the end of input_cb() implementations to the next state's input_cb(), which will be optimized to jumps (often direct jumps). That is, the compilers always optimized tail calls, but we can guarantee tail calls with the __attribute__((musttail)) statement attribute. * Every `return &teco_state_xxx` has to be replaced with `TECO_RETURN(ctx, &teco_state_xxx, error)`. * This speeds up `-O2 -flto` builds by 18% (e.g. tested on grosciteco for sciteco(7)). Part of the speed up could also be because of inlining through TECO_RETURN(). * The other state machines (stringbuilding and q-reg spec) cannot currently be optimized the same way since they get their characters passed in from the "main" state machine. * All loops around callbacks could be optimized the same way. E.g. the undo token runner could also tail call into the next runner, but it's probably not important to optimize undo token executions.
Diffstat (limited to 'src/core-commands.c')
-rw-r--r--src/core-commands.c64
1 files changed, 35 insertions, 29 deletions
diff --git a/src/core-commands.c b/src/core-commands.c
index db3ae9d..8f84224 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -765,7 +765,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
teco_error_modifier_set(error, cmd);
return NULL;
}
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
}
switch (chr) {
@@ -794,7 +794,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
}
if (ctx->flags.mode == TECO_MODE_NORMAL)
teco_expressions_add_digit(chr, ctx->qreg_table_locals->radix);
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
case '*':
/*
@@ -806,7 +806,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
*/
if (teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) == 1 &&
teco_cmdline_ssm(SCI_GETCHARAT, 0, 0) == '*')
- return &teco_state_save_cmdline;
+ TECO_RETURN(ctx, &teco_state_save_cmdline, error);
/* treat as an operator */
break;
@@ -821,7 +821,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
if (ctx->parent.must_undo)
teco_undo_gint(ctx->nest_level);
ctx->nest_level++;
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
case '>':
if (ctx->flags.modifier_at) {
@@ -840,7 +840,7 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
teco_undo_gint(ctx->nest_level);
ctx->nest_level--;
}
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
/*
* Control Structures (conditionals)
@@ -858,9 +858,9 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
else if (ctx->flags.mode == TECO_MODE_NORMAL)
/* skip to end of conditional; skip ELSE-part */
ctx->flags.mode = TECO_MODE_PARSE_ONLY_COND;
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
- case '\'': return teco_state_endcond(ctx, "'", error);
+ case '\'': TECO_RETURN(ctx, teco_state_endcond(ctx, "'", error), error);
/*
* Word movement and deletion commands.
@@ -873,13 +873,13 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
* but should accept only one colon modifier.
*/
case 'w':
- case 'W': return teco_state_start_words(ctx, "W", 1, error);
+ case 'W': TECO_RETURN(ctx, teco_state_start_words(ctx, "W", 1, error), error);
case 'p':
- case 'P': return teco_state_start_words(ctx, "P", -1, error);
+ case 'P': TECO_RETURN(ctx, teco_state_start_words(ctx, "P", -1, error), error);
case 'v':
- case 'V': return teco_state_start_delete_words(ctx, "V", 1, error);
+ case 'V': TECO_RETURN(ctx, teco_state_start_delete_words(ctx, "V", 1, error), error);
case 'y':
- case 'Y': return teco_state_start_delete_words(ctx, "Y", -1, error);
+ case 'Y': TECO_RETURN(ctx, teco_state_start_delete_words(ctx, "Y", -1, error), error);
/*
* Modifiers
@@ -898,11 +898,11 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
if (ctx->parent.must_undo)
teco_undo_flags(ctx->flags);
ctx->flags.modifier_at = TRUE;
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
case ':':
if (ctx->flags.mode > TECO_MODE_NORMAL)
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
if (ctx->flags.modifier_colon >= 2) {
teco_error_modifier_set(error, ":");
return NULL;
@@ -910,18 +910,21 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
if (ctx->parent.must_undo)
teco_undo_flags(ctx->flags);
ctx->flags.modifier_colon++;
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
default:
/*
* <CTRL/x> commands implemented in teco_state_control
*/
- if (TECO_IS_CTL(chr))
- return teco_state_control_input(ctx, TECO_CTL_ECHO(chr), error);
+ if (TECO_IS_CTL(chr)) {
+ teco_state_t *next = teco_state_control_input(ctx, TECO_CTL_ECHO(chr), error);
+ TECO_RETURN(ctx, next, error);
+ }
}
- return teco_machine_main_transition_input(ctx, transitions, G_N_ELEMENTS(transitions),
- teco_ascii_toupper(chr), error);
+ teco_state_t *next = teco_machine_main_transition_input(ctx, transitions, G_N_ELEMENTS(transitions),
+ teco_ascii_toupper(chr), error);
+ TECO_RETURN(ctx, next, error);
}
TECO_DEFINE_STATE_START(teco_state_start,
@@ -1090,11 +1093,12 @@ teco_state_fcommand_input(teco_machine_main_t *ctx, gunichar chr, GError **error
};
switch (chr) {
- case '"': return teco_state_endcond(ctx, "F\"", error);
+ case '"': TECO_RETURN(ctx, teco_state_endcond(ctx, "F\"", error), error);
}
- return teco_machine_main_transition_input(ctx, transitions, G_N_ELEMENTS(transitions),
- teco_ascii_toupper(chr), error);
+ teco_state_t *next = teco_machine_main_transition_input(ctx, transitions, G_N_ELEMENTS(transitions),
+ teco_ascii_toupper(chr), error);
+ TECO_RETURN(ctx, next, error);
}
TECO_DEFINE_STATE_COMMAND(teco_state_fcommand,
@@ -1304,7 +1308,7 @@ teco_state_condcommand_input(teco_machine_main_t *ctx, gunichar chr, GError **er
ctx->flags.mode = TECO_MODE_PARSE_ONLY_COND;
}
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
}
TECO_DEFINE_STATE_COMMAND(teco_state_condcommand,
@@ -1740,8 +1744,9 @@ teco_state_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
* Currently you get error messages like 'Syntax error "F"' for ^F.
* The easiest way around would be g_prefix_error(error, "Control command");
*/
- return teco_machine_main_transition_input(ctx, transitions, G_N_ELEMENTS(transitions),
- teco_ascii_toupper(chr), error);
+ teco_state_t *next = teco_machine_main_transition_input(ctx, transitions, G_N_ELEMENTS(transitions),
+ teco_ascii_toupper(chr), error);
+ TECO_RETURN(ctx, next, error);
}
TECO_DEFINE_STATE_COMMAND(teco_state_control,
@@ -1754,7 +1759,7 @@ teco_state_ascii_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
if (ctx->flags.mode == TECO_MODE_NORMAL)
teco_expressions_push(chr);
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
}
/*$ ^^ ^^c
@@ -1939,7 +1944,7 @@ teco_state_ctlc_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
{
switch (chr) {
case TECO_CTL_KEY('C'): return teco_state_ctlc_control_input(ctx, 'C', error);
- case '^': return &teco_state_ctlc_control;
+ case '^': TECO_RETURN(ctx, &teco_state_ctlc_control, error);
}
return ctx->flags.mode > TECO_MODE_NORMAL
@@ -1992,7 +1997,7 @@ teco_state_ctlc_control_input(teco_machine_main_t *ctx, gunichar chr, GError **e
*/
if (chr == 'c' || chr == 'C') {
if (ctx->flags.mode > TECO_MODE_NORMAL)
- return &teco_state_start;
+ TECO_RETURN(ctx, &teco_state_start, error);
if (teco_undo_enabled) {
g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
@@ -2956,8 +2961,9 @@ teco_state_ecommand_input(teco_machine_main_t *ctx, gunichar chr, GError **error
/*
* FIXME: Should we return a special syntax error in case of failure?
*/
- return teco_machine_main_transition_input(ctx, transitions, G_N_ELEMENTS(transitions),
- teco_ascii_toupper(chr), error);
+ teco_state_t *next = teco_machine_main_transition_input(ctx, transitions, G_N_ELEMENTS(transitions),
+ teco_ascii_toupper(chr), error);
+ TECO_RETURN(ctx, next, error);
}
TECO_DEFINE_STATE_COMMAND(teco_state_ecommand,