Skip to content

Commit

Permalink
compiler: Optimize most pure bifs to be guard bifs in try/catch
Browse files Browse the repository at this point in the history
When put in a try, calling a guard bif with no side-effects and 6 other
bifs ([PR-9042](erlang#9042)) are optimized
to remove the try/catch. However, most pure bifs can be safely optimized
in the same way in order to gain performance. This PR introduces a new
internal instruction `call_pseudo_guard_bif` that enables the compiler
to remove the try/catch around the bif. The instruction is designed to
call the bif in the same way, but having more optimized error-handling
in the runtime system.

Example code:

    try float_to_list(A, utf8)
    catch _:_ -> error
    end.

Before, assembly for the bif call after optimizations:

    {label,2}.
    {allocate,1,1}.
    {'try',{y,0},{f,3}}.
    {line,[{location,"zlc.erl",23}]}.
    {call_ext,1,{extfunc,erlang,float_to_list,1}}.
    {try_end,{y,0}}.
    {deallocate,1}.
    return.

Now, assembly for the bif call after optimizations:

    {label,2}.
    {allocate,0,1}.
    {line,[{location,"zlc.erl",23}]}.
    {call_pseudo_guard_bif,1,{extfunc,erlang,float_to_list,1},{f,3}}.
    {deallocate,0}.
    return.

Bifs that are optimized for try/catch in this change are listed in
`pbif.tab`.
  • Loading branch information
lucioleKi committed Nov 22, 2024
1 parent 270cec6 commit e67f514
Show file tree
Hide file tree
Showing 25 changed files with 694 additions and 102 deletions.
96 changes: 96 additions & 0 deletions erts/emulator/beam/emu/bif_instrs.tab
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,102 @@ i_bif_body.call(Bif, Dst) {
goto post_error_handling;
}

i_call_pseudo_guard_bif(Live, Bif, Exp, Fail) {
Export *export;
ErtsBifFunc bf;

Eterm result;
ErlHeapFragment *live_hf_end;

bf = (ErtsBifFunc) $Bif;
export = (Export*) $Exp;

if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) {
/*
* If we have run out of reductions, do a context
* switch before calling the BIF.
*/
c_p->arity = GET_EXPORT_ARITY(export);
c_p->current = &export->info.mfa;
goto context_switch3;
}

if (ERTS_UNLIKELY(export->is_bif_traced)) {
$SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_EXPORT(export);
}

ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_EXPORT_MODULE(export), bf);

PRE_BIF_SWAPOUT(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
c_p->fcalls = FCALLS - 1;
if (FCALLS <= 0) {
save_calls(c_p, export);
}
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);

ERTS_ASSERT_TRACER_REFS(&c_p->common);

result = (*bf)(c_p, reg, I);

ERTS_ASSERT_TRACER_REFS(&c_p->common);

/* Only heavy BIFs may GC. */
ASSERT(E == c_p->stop);

ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
if (ERTS_IS_GC_AFTER_BIF_DESIRED(c_p)) {
Uint arity = GET_EXPORT_ARITY(export);
result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result,
reg, arity);
E = c_p->stop;
}
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);

/*
* We have to update the cache if we are enabled in order
* to make sure no bookkeeping is done after we disabled
* msacc. We don't always do this as it is quite expensive.
*/
if (ERTS_MSACC_IS_ENABLED_CACHED_X()) {
ERTS_MSACC_UPDATE_CACHE_X();
}
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR);
if (ERTS_LIKELY(is_value(result))) {
x(0) = result;
CHECK_TERM(x(0));
$NEXT0();
} else if (c_p->freason == TRAP) {
/*
* Set the continuation pointer to return to next
* instruction after the trap (either by a return from
* erlang code or by nif_bif.epilogue() when the BIF
* is done).
*/
$SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
SET_I(c_p->i);
$DISPATCH();
}

/*
* Error handling. SWAPOUT is not needed because it was done above.
*/
ASSERT(c_p->stop == E);
$FAIL($Fail);
//| -no_next
}

//
// length/1 is the only guard BIF that does not execute in constant
// time. Here follows special instructions to allow the calculation of
Expand Down
9 changes: 9 additions & 0 deletions erts/emulator/beam/emu/ops.tab
Original file line number Diff line number Diff line change
Expand Up @@ -1541,6 +1541,15 @@ gc_bif3 Fail=f _Live Bif S1 S2 S3 Dst => i_bif3 S3 S2 S1 Fail Bif Dst
unsupported_guard_bif/3
unsupported_guard_bif _A _B _C | never() => _

#
# Pseudo-guard BIFs
#

call_pseudo_guard_bif Live Func Fail =>
i_call_pseudo_guard_bif Live Func Func Fail

i_call_pseudo_guard_bif t b e f

#
# R13B03
#
Expand Down
8 changes: 7 additions & 1 deletion erts/emulator/beam/jit/arm/beam_asm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,12 @@ class BeamModuleAssembler : public BeamAssembler,
const ArgWord &Bif,
const ArgRegister &Dst);

void emit_i_bif_pure(const ArgWord &Bif,
const ArgExport &Exp,
const ArgWord &Live,
const ArgLabel &Fail,
const ArgRegister &Dst);

void emit_error(int code);
void emit_error(int reason, const ArgSource &Src);

Expand Down Expand Up @@ -1299,7 +1305,7 @@ class BeamModuleAssembler : public BeamAssembler,

void emit_validate_unicode(Label next, Label fail, a64::Gp value);

void ubif_comment(const ArgWord &Bif);
void ubif_comment(const ArgWord &Bif, const ArgExport &Exp);

void emit_cmp_immed_to_bool(arm::CondCode cc,
const ArgSource &LHS,
Expand Down
3 changes: 3 additions & 0 deletions erts/emulator/beam/jit/arm/beam_asm_global.hpp.pl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
call_bif_shared
call_light_bif_shared
call_nif_yield_helper
call_pseudo_guard_bif_shared
catch_end_shared
call_nif_early
call_nif_shared
Expand Down Expand Up @@ -191,6 +192,8 @@ sub gen_list {
void emit_i_length_common(Label fail, int state_size);
void emit_call_bif_common(bool return_error);
void emit_raise_badarg(const ErtsCodeMFA *mfa);
void emit_bif_bit_size_helper(Label fail);
Expand Down
111 changes: 90 additions & 21 deletions erts/emulator/beam/jit/arm/instr_bif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,31 @@ extern "C"
#include "erl_msacc.h"
}

void BeamModuleAssembler::ubif_comment(const ArgWord &Bif) {
void BeamModuleAssembler::ubif_comment(const ArgWord &Bif,
const ArgExport &Exp) {
if (logger.file()) {
ErtsCodeMFA *mfa = ubif2mfa((void *)Bif.get());
if (mfa) {
comment("UBIF: %T/%d", mfa->function, mfa->arity);
BeamFile_ImportEntry *import;
const Export *exp;
bool is_pure;

import = &beam->imports.entries[Exp.val];
exp = erts_active_export_entry(import->module,
import->function,
import->arity);

is_pure = exp->bif_number != -1 &&
bif_table[exp->bif_number].kind == BIF_KIND_PURE;

if (is_pure) {
comment("fake UBIF: %T:%T/%d",
import->module,
import->function,
import->arity);
} else {
ErtsCodeMFA *mfa = ubif2mfa((void *)Bif.get());
if (mfa) {
comment("UBIF: %T/%d", mfa->function, mfa->arity);
}
}
}
}
Expand Down Expand Up @@ -108,25 +128,27 @@ void BeamGlobalAssembler::emit_i_bif_body_shared() {
void BeamModuleAssembler::emit_i_bif1(const ArgSource &Src1,
const ArgLabel &Fail,
const ArgWord &Bif,
const ArgExport &Exp,
const ArgRegister &Dst) {
auto src1 = load_source(Src1);

a.str(src1.reg, getXRef(0));

ubif_comment(Bif);
ubif_comment(Bif, Exp);
emit_i_bif(Fail, Bif, Dst);
}

void BeamModuleAssembler::emit_i_bif2(const ArgSource &Src1,
const ArgSource &Src2,
const ArgLabel &Fail,
const ArgWord &Bif,
const ArgExport &Exp,
const ArgRegister &Dst) {
auto [src1, src2] = load_sources(Src1, TMP1, Src2, TMP2);

a.stp(src1.reg, src2.reg, getXRef(0));

ubif_comment(Bif);
ubif_comment(Bif, Exp);
emit_i_bif(Fail, Bif, Dst);
}

Expand All @@ -135,14 +157,15 @@ void BeamModuleAssembler::emit_i_bif3(const ArgSource &Src1,
const ArgSource &Src3,
const ArgLabel &Fail,
const ArgWord &Bif,
const ArgExport &Exp,
const ArgRegister &Dst) {
auto [src1, src2] = load_sources(Src1, TMP1, Src2, TMP2);
auto src3 = load_source(Src3, TMP3);

a.stp(src1.reg, src2.reg, getXRef(0));
a.str(src3.reg, getXRef(2));

ubif_comment(Bif);
ubif_comment(Bif, Exp);
emit_i_bif(Fail, Bif, Dst);
}

Expand All @@ -168,12 +191,13 @@ void BeamModuleAssembler::emit_i_bif(const ArgLabel &Fail,

void BeamModuleAssembler::emit_nofail_bif1(const ArgSource &Src1,
const ArgWord &Bif,
const ArgExport &Exp,
const ArgRegister &Dst) {
auto src1 = load_source(Src1);

a.str(src1.reg, getXRef(0));

ubif_comment(Bif);
ubif_comment(Bif, Exp);
mov_arg(ARG4, Bif);
fragment_call(ga->get_i_bif_guard_shared());
mov_arg(Dst, ARG1);
Expand All @@ -182,12 +206,13 @@ void BeamModuleAssembler::emit_nofail_bif1(const ArgSource &Src1,
void BeamModuleAssembler::emit_nofail_bif2(const ArgSource &Src1,
const ArgSource &Src2,
const ArgWord &Bif,
const ArgExport &Exp,
const ArgRegister &Dst) {
auto [src1, src2] = load_sources(Src1, TMP1, Src2, TMP2);

a.stp(src1.reg, src2.reg, getXRef(0));

ubif_comment(Bif);
ubif_comment(Bif, Exp);
mov_arg(ARG4, Bif);
fragment_call(ga->get_i_bif_guard_shared());
mov_arg(Dst, ARG1);
Expand Down Expand Up @@ -364,13 +389,8 @@ static Eterm debug_call_light_bif(Process *c_p,

/* It is important that the below code is as optimized as possible.
* When doing any changes, make sure to look at the estone bif_dispatch
* benchmark to make sure you don't introduce any regressions.
*
* ARG3 = entry
* ARG4 = export entry
* ARG8 = BIF pointer
*/
void BeamGlobalAssembler::emit_call_light_bif_shared() {
* benchmark to make sure you don't introduce any regressions. */
void BeamGlobalAssembler::emit_call_bif_common(bool return_error) {
arm::Mem entry_mem = TMP_MEM1q, export_mem = TMP_MEM2q,
mbuf_mem = TMP_MEM3q;

Expand Down Expand Up @@ -528,11 +548,16 @@ void BeamGlobalAssembler::emit_call_light_bif_shared() {

a.bind(error);
{
/* raise_exception_shared expects current PC in ARG2 and MFA in
* ARG4. */
a.ldp(ARG2, ARG4, entry_mem);
add(ARG4, ARG4, offsetof(Export, info.mfa));
a.b(labels[raise_exception_shared]);
if (return_error) {
a.mov(XREG0, THE_NON_VALUE);
a.ret(a64::x30);
} else {
/* raise_exception_shared expects current PC in
* ARG2 and MFA in ARG4. */
a.ldp(ARG2, ARG4, entry_mem);
add(ARG4, ARG4, offsetof(Export, info.mfa));
a.b(labels[raise_exception_shared]);
}
}
}

Expand Down Expand Up @@ -577,6 +602,30 @@ void BeamGlobalAssembler::emit_call_light_bif_shared() {
}
}

/*
* ARG3 = entry
* ARG4 = export entry
* ARG8 = BIF pointer
*
* If successful, the result is returned in XREG0.
* In case of error, an exception is raised.
*/
void BeamGlobalAssembler::emit_call_light_bif_shared() {
emit_call_bif_common(false);
}

/*
* ARG3 = entry
* ARG4 = export entry
* ARG8 = BIF pointer
*
* If successful, the result is returned in XREG0.
* In case of error, XREG0 is THE_NON_VALUE on return.
*/
void BeamGlobalAssembler::emit_call_pseudo_guard_bif_shared() {
emit_call_bif_common(true);
}

void BeamModuleAssembler::emit_call_light_bif(const ArgWord &Bif,
const ArgExport &Exp) {
Label entry = a.newLabel();
Expand All @@ -594,6 +643,26 @@ void BeamModuleAssembler::emit_call_light_bif(const ArgWord &Bif,
fragment_call(ga->get_call_light_bif_shared());
}

void BeamModuleAssembler::emit_i_call_pseudo_guard_bif(const ArgWord &Live,
const ArgWord &Bif,
const ArgExport &Exp,
const ArgLabel &Fail) {
Label entry = a.newLabel();
BeamFile_ImportEntry *e = &beam->imports.entries[Exp.get()];

a.bind(entry);

mov_arg(ARG4, Exp);
mov_arg(ARG8, Bif);
a.adr(ARG3, entry);

if (logger.file()) {
comment("BIF: %T:%T/%d", e->module, e->function, e->arity);
}
fragment_call(ga->get_call_pseudo_guard_bif_shared());
emit_branch_if_not_value(XREG0, resolve_beam_label(Fail, dispUnknown));
}

void BeamModuleAssembler::emit_send() {
Label entry = a.newLabel();

Expand Down
3 changes: 2 additions & 1 deletion erts/emulator/beam/jit/arm/instr_guard_bifs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,12 +802,13 @@ void BeamModuleAssembler::emit_bif_hd(const ArgSource &Src,
*/

void BeamModuleAssembler::emit_bif_is_map_key(const ArgWord &Bif,
const ArgExport &Exp,
const ArgLabel &Fail,
const ArgSource &Key,
const ArgSource &Src,
const ArgRegister &Dst) {
if (!exact_type<BeamTypeId::Map>(Src)) {
emit_i_bif2(Key, Src, Fail, Bif, Dst);
emit_i_bif2(Key, Src, Fail, Bif, Exp, Dst);
return;
}

Expand Down
Loading

0 comments on commit e67f514

Please sign in to comment.