summaryrefslogtreecommitdiff
path: root/erts/emulator/beam/erl_fun.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/erl_fun.c')
-rw-r--r--erts/emulator/beam/erl_fun.c263
1 files changed, 169 insertions, 94 deletions
diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c
index c9eb42a0bd..17b3e12dd8 100644
--- a/erts/emulator/beam/erl_fun.c
+++ b/erts/emulator/beam/erl_fun.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2021. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2022. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,16 @@
#include "global.h"
#include "erl_fun.h"
#include "hash.h"
+#include "beam_common.h"
+
+/* Container structure for fun entries, allowing us to start `ErlFunEntry` with
+ * a field other than its `HashBucket`. */
+typedef struct erl_fun_entry_container {
+ /* !! MUST BE THE FIRST FIELD !! */
+ HashBucket bucket;
+
+ ErlFunEntry entry;
+} ErlFunEntryContainer;
static Hash erts_fun_table;
@@ -37,18 +47,10 @@ static erts_rwmtx_t erts_fun_table_lock;
#define erts_fun_write_lock() erts_rwmtx_rwlock(&erts_fun_table_lock)
#define erts_fun_write_unlock() erts_rwmtx_rwunlock(&erts_fun_table_lock)
-static HashValue fun_hash(ErlFunEntry* obj);
-static int fun_cmp(ErlFunEntry* obj1, ErlFunEntry* obj2);
-static ErlFunEntry* fun_alloc(ErlFunEntry* template);
-static void fun_free(ErlFunEntry* obj);
-
-/*
- * The address field of every fun that has no loaded code will point
- * to unloaded_fun[]. The -1 in unloaded_fun[0] will be interpreted
- * as an illegal arity when attempting to call a fun.
- */
-static BeamInstr unloaded_fun_code[4] = {NIL, NIL, -1, 0};
-static ErtsCodePtr unloaded_fun = &unloaded_fun_code[3];
+static HashValue fun_hash(ErlFunEntryContainer* obj);
+static int fun_cmp(ErlFunEntryContainer* obj1, ErlFunEntryContainer* obj2);
+static ErlFunEntryContainer* fun_alloc(ErlFunEntryContainer* template);
+static void fun_free(ErlFunEntryContainer* obj);
void
erts_init_fun_table(void)
@@ -97,61 +99,66 @@ int erts_fun_table_sz(void)
ErlFunEntry*
erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index,
- const byte* uniq, int index, int arity)
+ const byte* uniq, int index, int arity)
{
- ErlFunEntry template;
- ErlFunEntry* fe;
+ ErlFunEntryContainer template;
+ ErlFunEntryContainer *fc;
+ ErlFunEntry *tp;
erts_aint_t refc;
+ tp = &template.entry;
+
+ /* All fields are copied from the template when inserting a new entry. */
ASSERT(is_atom(mod));
- template.old_uniq = old_uniq;
- template.index = index;
- template.module = mod;
+ tp->old_index = old_index;
+ tp->old_uniq = old_uniq;
+ tp->index = index;
+ tp->module = mod;
+ tp->arity = arity;
+
+ sys_memcpy(tp->uniq, uniq, sizeof(tp->uniq));
+
erts_fun_write_lock();
- fe = (ErlFunEntry *) hash_put(&erts_fun_table, (void*) &template);
- sys_memcpy(fe->uniq, uniq, sizeof(fe->uniq));
- fe->old_index = old_index;
- fe->arity = arity;
- refc = erts_refc_inctest(&fe->refc, 0);
- if (refc < 2) /* New or pending delete */
- erts_refc_inc(&fe->refc, 1);
+ fc = (ErlFunEntryContainer*)hash_put(&erts_fun_table, (void*)&template);
+ refc = erts_refc_inctest(&fc->entry.refc, 0);
+ if (refc < 2) {
+ /* New or pending delete */
+ erts_refc_inc(&fc->entry.refc, 1);
+ }
erts_fun_write_unlock();
- return fe;
+
+ return &fc->entry;
}
-ErlFunEntry*
-erts_get_fun_entry(Eterm mod, int uniq, int index)
-{
- ErlFunEntry template;
- ErlFunEntry *ret;
+const ErtsCodeMFA *erts_get_fun_mfa(const ErlFunEntry *fe) {
+ ErtsCodePtr address = fe->dispatch.addresses[0];
- ASSERT(is_atom(mod));
- template.old_uniq = uniq;
- template.index = index;
- template.module = mod;
- erts_fun_read_lock();
- ret = (ErlFunEntry *) hash_get(&erts_fun_table, (void*) &template);
- if (ret) {
- erts_aint_t refc = erts_refc_inctest(&ret->refc, 1);
- if (refc < 2) /* Pending delete */
- erts_refc_inc(&ret->refc, 1);
+ if (address != beam_unloaded_fun) {
+ return erts_find_function_from_pc(address);
}
- erts_fun_read_unlock();
- return ret;
+
+ return NULL;
}
-int erts_is_fun_loaded(ErlFunEntry* fe) {
- int res = fe->address != unloaded_fun;
+void erts_set_fun_code(ErlFunEntry *fe, ErtsCodePtr address) {
+ int i;
- ASSERT(res == (0 != ((const BeamInstr*)fe->address)[0]));
+ for (i = 0; i < ERTS_ADDRESSV_SIZE; i++) {
+ fe->dispatch.addresses[i] = address;
+ }
+}
- return res;
+int erts_is_fun_loaded(const ErlFunEntry* fe) {
+ return fe->dispatch.addresses[0] != beam_unloaded_fun;
}
static void
erts_erase_fun_entry_unlocked(ErlFunEntry* fe)
{
- hash_erase(&erts_fun_table, (void *) fe);
+ ErlFunEntryContainer *fc = ErtsContainerStruct(fe, ErlFunEntryContainer,
+ entry);
+
+ hash_erase(&erts_fun_table, (void *) fc);
}
void
@@ -164,28 +171,33 @@ erts_erase_fun_entry(ErlFunEntry* fe)
*/
if (erts_refc_dectest(&fe->refc, -1) <= 0)
{
- if (fe->address != unloaded_fun)
- erts_exit(ERTS_ERROR_EXIT,
- "Internal error: "
- "Invalid reference count found on #Fun<%T.%d.%d>: "
- " About to erase fun still referred by code.\n",
- fe->module, fe->old_index, fe->old_uniq);
- erts_erase_fun_entry_unlocked(fe);
+ if (erts_is_fun_loaded(fe)) {
+ erts_exit(ERTS_ERROR_EXIT,
+ "Internal error: "
+ "Invalid reference count found on #Fun<%T.%d.%d>: "
+ " About to erase fun still referred by code.\n",
+ fe->module, fe->old_index, fe->old_uniq);
+ }
+ erts_erase_fun_entry_unlocked(fe);
}
erts_fun_write_unlock();
}
-static void fun_purge_foreach(ErlFunEntry *fe, struct erl_module_instance* modp)
+static void fun_purge_foreach(ErlFunEntryContainer *fc,
+ struct erl_module_instance* modp)
{
const char *fun_addr, *mod_start;
+ ErlFunEntry *fe = &fc->entry;
- fun_addr = (const char*)fe->address;
+ fun_addr = (const char*)fe->dispatch.addresses[0];
mod_start = (const char*)modp->code_hdr;
if (ErtsInArea(fun_addr, mod_start, modp->code_length)) {
- fe->pend_purge_address = fe->address;
+ fe->pend_purge_address = fe->dispatch.addresses[0];
ERTS_THR_WRITE_MEMORY_BARRIER;
- fe->address = unloaded_fun;
+
+ erts_set_fun_code(fe, beam_unloaded_fun);
+
erts_purge_state_add_fun(fe);
}
}
@@ -206,8 +218,8 @@ erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no)
for (ix = 0; ix < no; ix++) {
ErlFunEntry *fe = funs[ix];
- if (fe->address == unloaded_fun) {
- fe->address = fe->pend_purge_address;
+ if (fe->dispatch.addresses[0] == beam_unloaded_fun) {
+ erts_set_fun_code(fe, fe->pend_purge_address);
}
}
}
@@ -236,19 +248,82 @@ erts_fun_purge_complete(ErlFunEntry **funs, Uint no)
ERTS_THR_WRITE_MEMORY_BARRIER;
}
+
+ErlFunThing *erts_new_export_fun_thing(Eterm **hpp, Export *exp, int arity)
+{
+ ErlFunThing *funp;
+
+ funp = (ErlFunThing*)(*hpp);
+ *hpp += ERL_FUN_SIZE;
+
+ funp->thing_word = HEADER_FUN;
+ funp->next = NULL;
+ funp->entry.exp = exp;
+ funp->num_free = 0;
+ funp->creator = am_external;
+ funp->arity = arity;
+
+#ifdef DEBUG
+ {
+ const ErtsCodeMFA *mfa = &exp->info.mfa;
+ ASSERT(arity == mfa->arity);
+ }
+#endif
+
+ return funp;
+}
+
+ErlFunThing *erts_new_local_fun_thing(Process *p, ErlFunEntry *fe,
+ int arity, int num_free)
+{
+ ErlFunThing *funp;
+
+ funp = (ErlFunThing*) p->htop;
+ p->htop += ERL_FUN_SIZE + num_free;
+ erts_refc_inc(&fe->refc, 2);
+
+ funp->thing_word = HEADER_FUN;
+ funp->next = MSO(p).first;
+ MSO(p).first = (struct erl_off_heap_header*) funp;
+ funp->entry.fun = fe;
+ funp->num_free = num_free;
+ funp->creator = p->common.id;
+ funp->arity = arity;
+
+#ifdef DEBUG
+ {
+ /* FIXME: This assertion can fail because it may point to new code that
+ * has not been committed yet. This is an actual bug but the fix is too
+ * too involved and risky to release in a patch.
+ *
+ * As this problem has existed since the introduction of funs and is
+ * very unlikely to cause actual issues in the wild, we've decided to
+ * postpone the fix until OTP 26. See OTP-18016 for details. */
+ const ErtsCodeMFA *mfa = erts_get_fun_mfa(fe);
+ ASSERT(!mfa || funp->arity == mfa->arity - num_free);
+ ASSERT(arity == fe->arity);
+ }
+#endif
+
+ return funp;
+}
+
+
struct dump_fun_foreach_args {
fmtfn_t to;
void *to_arg;
};
static void
-dump_fun_foreach(ErlFunEntry *fe, struct dump_fun_foreach_args *args)
+dump_fun_foreach(ErlFunEntryContainer *fc, struct dump_fun_foreach_args *args)
{
+ ErlFunEntry *fe = &fc->entry;
+
erts_print(args->to, args->to_arg, "=fun\n");
erts_print(args->to, args->to_arg, "Module: %T\n", fe->module);
erts_print(args->to, args->to_arg, "Uniq: %d\n", fe->old_uniq);
erts_print(args->to, args->to_arg, "Index: %d\n",fe->old_index);
- erts_print(args->to, args->to_arg, "Address: %p\n", fe->address);
+ erts_print(args->to, args->to_arg, "Address: %p\n", fe->dispatch.addresses[0]);
erts_print(args->to, args->to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1));
}
@@ -266,48 +341,48 @@ erts_dump_fun_entries(fmtfn_t to, void *to_arg)
}
static HashValue
-fun_hash(ErlFunEntry* obj)
+fun_hash(ErlFunEntryContainer* obj)
{
- return (HashValue) (obj->old_uniq ^ obj->index ^ atom_val(obj->module));
+ ErlFunEntry *fe = &obj->entry;
+
+ return (HashValue) (fe->old_uniq ^ fe->index ^ atom_val(fe->module));
}
static int
-fun_cmp(ErlFunEntry* obj1, ErlFunEntry* obj2)
+fun_cmp(ErlFunEntryContainer* obj1, ErlFunEntryContainer* obj2)
{
- /*
- * OTP 23: Use 'index' (instead of 'old_index') when comparing fun
- * entries. In OTP 23, multiple make_fun2 instructions may refer to the
- * the same 'index' (for the wrapper function generated for the
- * 'fun F/A' syntax).
- *
- * This is safe when loading code compiled with OTP R15 and later,
- * because since R15 (2011), the 'index' has been reliably equal
- * to 'old_index'. The loader refuses to load modules compiled before
- * OTP R15.
- */
-
- return !(obj1->module == obj2->module &&
- obj1->old_uniq == obj2->old_uniq &&
- obj1->index == obj2->index);
+ ErlFunEntry* fe1 = &obj1->entry;
+ ErlFunEntry* fe2 = &obj2->entry;
+
+ return !(fe1->old_index == fe2->old_index &&
+ fe1->old_uniq == fe2->old_uniq &&
+ fe1->module == fe2->module &&
+ fe1->index == fe2->index &&
+ fe1->arity == fe2->arity &&
+ !sys_memcmp(fe1->uniq, fe2->uniq, sizeof(fe1->uniq)));
}
-static ErlFunEntry*
-fun_alloc(ErlFunEntry* template)
+static ErlFunEntryContainer*
+fun_alloc(ErlFunEntryContainer* template)
{
- ErlFunEntry* obj = (ErlFunEntry *) erts_alloc(ERTS_ALC_T_FUN_ENTRY,
- sizeof(ErlFunEntry));
-
- obj->old_uniq = template->old_uniq;
- obj->index = template->index;
- obj->module = template->module;
- erts_refc_init(&obj->refc, -1);
- obj->address = unloaded_fun;
- obj->pend_purge_address = NULL;
+ ErlFunEntryContainer* obj;
+
+ obj = (ErlFunEntryContainer *) erts_alloc(ERTS_ALC_T_FUN_ENTRY,
+ sizeof(ErlFunEntryContainer));
+
+ sys_memcpy(obj, template, sizeof(ErlFunEntryContainer));
+
+ erts_refc_init(&obj->entry.refc, -1);
+
+ erts_set_fun_code(&obj->entry, beam_unloaded_fun);
+
+ obj->entry.pend_purge_address = NULL;
+
return obj;
}
static void
-fun_free(ErlFunEntry* obj)
+fun_free(ErlFunEntryContainer* obj)
{
erts_free(ERTS_ALC_T_FUN_ENTRY, (void *) obj);
}