From 4490c7c271cb78b3d4adf7d03765f86959cb9192 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sat, 24 Aug 2024 09:43:58 -0400 Subject: [PATCH] Add fast-lanes for '++'/2 and '--'/2 on nil arguments Currently appending an empty list to another list causes the left-hand side list to be fully copied. We can skip the append operation when the right-hand side is nil since it shouldn't change the left-hand side list. In this fast-lane we scan the list to determine if it is improper so that cases like `[1, 2 | 3] ++ []` behave the same as cases like `[1, 2 | 3] ++ [4]`: return a badarg error. This patch adds similar fast-lanes for '--'/2: we can return the first argument when either the first or second arguments are nil. --- erts/emulator/beam/erl_bif_lists.c | 40 +++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index 5dcd53d6139c..08080b713707 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -240,6 +240,20 @@ static Eterm append(Export *bif_entry, BIF_ALIST_2) { ErtsAppendContext context; int res; + /* Skip appending for `Lhs ++ []`: return `Lhs` instead. */ + if (is_nil(rhs)) { + /* Check that the `lhs` is not an improper list. */ + Eterm list = lhs; + while (is_list(list)) { + list = CDR(list_val(list)); + } + if (is_not_nil(list)) { + BIF_ERROR(BIF_P, BADARG); + } + + return lhs; + } + res = append_start(BIF_P, lhs, rhs, &context); if (res == 0) { @@ -976,7 +990,31 @@ static int subtract_start(Process *p, Eterm lhs, Eterm rhs, static Eterm subtract(Export *bif_entry, BIF_ALIST_2) { Eterm lhs = BIF_ARG_1, rhs = BIF_ARG_2; - if ((is_list(lhs) || is_nil(lhs)) && (is_list(rhs) || is_nil(rhs))) { + if (is_nil(lhs) && is_list(rhs)) { + /* Fast-lane when the `lhs` is nil: return nil. + * First check that `rhs` is not an improper list. */ + Eterm list = rhs; + while (is_list(list)) { + list = CDR(list_val(list)); + } + if (is_not_nil(list)) { + BIF_ERROR(BIF_P, BADARG); + } + + return lhs; + } else if (is_list(lhs) && is_nil(rhs)) { + /* Similar fast-lane when the rhs is nil: return the lhs list. + * First check that `lhs` is not an improper list. */ + Eterm list = lhs; + while (is_list(list)) { + list = CDR(list_val(list)); + } + if (is_not_nil(list)) { + BIF_ERROR(BIF_P, BADARG); + } + + return lhs; + } else if ((is_list(lhs) || is_nil(lhs)) && (is_list(rhs) || is_nil(rhs))) { /* We start with the context on the stack in the hopes that we won't * have to trap. */ ErtsSubtractContext context;