Skip to content

Commit

Permalink
Add fast-lanes for '++'/2 and '--'/2 on nil arguments
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
the-mikedavis committed Aug 26, 2024
1 parent 494647a commit 4490c7c
Showing 1 changed file with 39 additions and 1 deletion.
40 changes: 39 additions & 1 deletion erts/emulator/beam/erl_bif_lists.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 4490c7c

Please sign in to comment.