From 990976907841dd3122c82992ba9965ee313e988c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 21 Jan 2019 07:04:46 +0100 Subject: Optimize the is_function/2 guard test The is_function2 instruction is executed surprisingly frequently when running dialyzer or the compiler. It cannot hurt to optimize it a little. --- erts/emulator/beam/beam_emu.c | 14 +++++++ erts/emulator/beam/beam_load.c | 79 +++++++++++++++++++++++++++++++++------- erts/emulator/beam/instrs.tab | 8 +++- erts/emulator/beam/ops.tab | 9 +++-- erts/emulator/test/fun_SUITE.erl | 10 +++++ 5 files changed, 102 insertions(+), 18 deletions(-) diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 6b34024a5a..4ef06464f4 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -404,6 +404,7 @@ static BeamInstr* apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg) NOINLINE; static Eterm new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) NOINLINE; +static int is_function2(Eterm Term, Uint arity); static Eterm erts_gc_new_map(Process* p, Eterm* reg, Uint live, Uint n, BeamInstr* ptr) NOINLINE; static Eterm erts_gc_new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal, @@ -2662,6 +2663,19 @@ new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free) return make_fun(funp); } +static int +is_function2(Eterm Term, Uint arity) +{ + if (is_fun(Term)) { + ErlFunThing* funp = (ErlFunThing *) fun_val(Term); + return funp->arity == arity; + } else if (is_export(Term)) { + Export* exp = (Export *) (export_val(Term)[1]); + return exp->info.mfa.arity == arity; + } + return 0; +} + static Eterm get_map_element(Eterm map, Eterm key) { Uint32 hx; diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 400a58a75c..7ff55e8927 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4297,6 +4297,72 @@ gen_make_fun2(LoaderState* stp, GenOpArg idx) return op; } +static GenOp* +gen_is_function2(LoaderState* stp, GenOpArg Fail, GenOpArg Fun, GenOpArg Arity) +{ + GenOp* op; + int literal_arity = Arity.type == TAG_i; + int fun_is_reg = Fun.type == TAG_x || Fun.type == TAG_y; + + NEW_GENOP(stp, op); + op->next = NULL; + + if (fun_is_reg &&literal_arity) { + /* + * Most common case. Fun in a register and arity + * is an integer literal. + */ + if (Arity.val > MAX_ARG) { + /* Arity is negative or too big. */ + op->op = genop_jump_1; + op->arity = 1; + op->a[0] = Fail; + return op; + } else { + op->op = genop_hot_is_function2_3; + op->arity = 3; + op->a[0] = Fail; + op->a[1] = Fun; + op->a[2].type = TAG_u; + op->a[2].val = Arity.val; + return op; + } + } else { + /* + * Handle extremely uncommon cases by a slower sequence. + */ + GenOp* move_fun; + GenOp* move_arity; + + NEW_GENOP(stp, move_fun); + NEW_GENOP(stp, move_arity); + + move_fun->next = move_arity; + move_arity->next = op; + + move_fun->arity = 2; + move_fun->op = genop_move_2; + move_fun->a[0] = Fun; + move_fun->a[1].type = TAG_x; + move_fun->a[1].val = 1022; + + move_arity->arity = 2; + move_arity->op = genop_move_2; + move_arity->a[0] = Arity; + move_arity->a[1].type = TAG_x; + move_arity->a[1].val = 1023; + + op->op = genop_cold_is_function2_3; + op->arity = 3; + op->a[0] = Fail; + op->a[1].type = TAG_x; + op->a[1].val = 1022; + op->a[2].type = TAG_x; + op->a[2].val = 1023; + return move_fun; + } +} + static GenOp* tuple_append_put5(LoaderState* stp, GenOpArg Arity, GenOpArg Dst, GenOpArg* Puts, GenOpArg S1, GenOpArg S2, GenOpArg S3, @@ -4462,19 +4528,6 @@ is_empty_map(LoaderState* stp, GenOpArg Lit) return is_flatmap(term) && flatmap_get_size(flatmap_val(term)) == 0; } -/* - * Predicate to test whether the given literal is an export. - */ -static int -literal_is_export(LoaderState* stp, GenOpArg Lit) -{ - Eterm term; - - ASSERT(Lit.type == TAG_q); - term = stp->literals[Lit.val].term; - return is_export(term); -} - /* * Pseudo predicate map_key_sort that will sort the Rest operand for * map instructions as a side effect. diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab index df60e889f3..e55c4a112d 100644 --- a/erts/emulator/beam/instrs.tab +++ b/erts/emulator/beam/instrs.tab @@ -709,12 +709,18 @@ is_function(Fail, Src) { } } -is_function2(Fail, Fun, Arity) { +cold_is_function2(Fail, Fun, Arity) { if (erl_is_function(c_p, $Fun, $Arity) != am_true ) { $FAIL($Fail); } } +hot_is_function2(Fail, Fun, Arity) { + if (!is_function2($Fun, $Arity)) { + $FAIL($Fail); + } +} + is_integer(Fail, Src) { if (is_not_integer($Src)) { $FAIL($Fail); diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index ee99c9e563..8e730e42d6 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -719,11 +719,12 @@ is_boolean Fail=f ac => jump Fail is_boolean f? xy %hot -is_function2 Fail=f Literal=q Arity | literal_is_export(Literal) => -is_function2 Fail=f c Arity => jump Fail -is_function2 Fail=f Fun a => jump Fail +is_function2 Fail=f Fun Arity => gen_is_function2(Fail, Fun, Arity) -is_function2 f? S s +%cold +cold_is_function2 f? x x +%hot +hot_is_function2 f? S t # Allocating & initializing. allocate Need Regs | init Y => allocate_init Need Regs Y diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index f8a879182e..4042b58ff2 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -710,6 +710,16 @@ t_is_function2(Config) when is_list(Config) -> bad_arity({}), bad_arity({a,b}), bad_arity(self()), + + %% Bad arity argument in guard test. + Fun = fun erlang:abs/1, + ok = if + is_function(Fun, -1) -> error; + is_function(Fun, 256) -> error; + is_function(Fun, a) -> error; + is_function(Fun, Fun) -> error; + true -> ok + end, ok. bad_arity(A) -> -- cgit v1.2.3