Skip to content

Commit b535d08

Browse files
committed
Make *_to_atom/2 functions BIFs
`binary_to_atom`, `binary_to_existing_atom`, `list_to_atom`, `list_to_existing_atom` used to be NIFs, starting from OTP-28 are optimized to BIFs. Add support to `call_ext` and other call instructions to `GCBIFFunctionType` in order to make those NIFs work with OTP < 28 too. Also these functions are used as BIFs when used as guards, while they are used as NIFs in other cases. See also: erlang/otp@ad10c97 Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent 73ec5a0 commit b535d08

File tree

6 files changed

+293
-152
lines changed

6 files changed

+293
-152
lines changed

src/libAtomVM/bif.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
#include <math.h>
2525

2626
#include "atom.h"
27+
#include "bitstring.h"
2728
#include "defaultatoms.h"
2829
#include "dictionary.h"
30+
#include "interop.h"
2931
#include "overflow_helpers.h"
3032
#include "term.h"
3133
#include "trace.h"
34+
#include "unicode.h"
3235
#include "utils.h"
3336

3437
//Ignore warning caused by gperf generated code
@@ -1512,3 +1515,176 @@ term bif_erlang_size_1(Context *ctx, uint32_t fail_label, int live, term arg1)
15121515

15131516
RAISE_ERROR_BIF(fail_label, BADARG_ATOM);
15141517
}
1518+
1519+
static term list_to_atom(Context *ctx, term a_list, bool create_new, term *error_reason);
1520+
1521+
term bif_erlang_list_to_atom_1(Context *ctx, uint32_t fail_label, int live, term arg1)
1522+
{
1523+
UNUSED(live);
1524+
1525+
term error_reason;
1526+
term result = list_to_atom(ctx, arg1, true, &error_reason);
1527+
if (UNLIKELY(term_is_invalid_term(result))) {
1528+
RAISE_ERROR_BIF(fail_label, error_reason);
1529+
}
1530+
return result;
1531+
}
1532+
1533+
term bif_erlang_list_to_existing_atom_1(Context *ctx, uint32_t fail_label, int live, term arg1)
1534+
{
1535+
UNUSED(live);
1536+
1537+
term error_reason;
1538+
term result = list_to_atom(ctx, arg1, false, &error_reason);
1539+
if (UNLIKELY(term_is_invalid_term(result))) {
1540+
RAISE_ERROR_BIF(fail_label, error_reason);
1541+
}
1542+
return result;
1543+
}
1544+
1545+
static term list_to_atom(Context *ctx, term a_list, bool create_new, term *error_reason)
1546+
{
1547+
if (UNLIKELY(!term_is_list(a_list))) {
1548+
*error_reason = BADARG_ATOM;
1549+
return term_invalid_term();
1550+
}
1551+
1552+
int ok;
1553+
char *atom_string = interop_list_to_utf8_string(a_list, &ok);
1554+
if (UNLIKELY(!ok)) {
1555+
*error_reason = OUT_OF_MEMORY_ATOM;
1556+
return term_invalid_term();
1557+
}
1558+
int atom_string_len = strlen(atom_string);
1559+
if (UNLIKELY(atom_string_len > 255)) {
1560+
free(atom_string);
1561+
*error_reason = SYSTEM_LIMIT_ATOM;
1562+
return term_invalid_term();
1563+
}
1564+
1565+
AtomString atom = malloc(atom_string_len + 1);
1566+
if (IS_NULL_PTR(atom)) {
1567+
free(atom_string);
1568+
*error_reason = OUT_OF_MEMORY_ATOM;
1569+
return term_invalid_term();
1570+
}
1571+
((uint8_t *) atom)[0] = atom_string_len;
1572+
memcpy(((char *) atom) + 1, atom_string, atom_string_len);
1573+
free(atom_string);
1574+
1575+
enum AtomTableCopyOpt atom_opts = AtomTableCopyAtom;
1576+
if (!create_new) {
1577+
atom_opts |= AtomTableAlreadyExisting;
1578+
}
1579+
long global_atom_index = atom_table_ensure_atom(ctx->global->atom_table, atom, atom_opts);
1580+
free((void *) atom);
1581+
if (UNLIKELY(global_atom_index == ATOM_TABLE_NOT_FOUND)) {
1582+
*error_reason = BADARG_ATOM;
1583+
return term_invalid_term();
1584+
} else if (UNLIKELY(global_atom_index == ATOM_TABLE_ALLOC_FAIL)) {
1585+
*error_reason = OUT_OF_MEMORY_ATOM;
1586+
return term_invalid_term();
1587+
}
1588+
return term_from_atom_index(global_atom_index);
1589+
}
1590+
1591+
term bif_erlang_binary_to_atom_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2)
1592+
{
1593+
UNUSED(live);
1594+
1595+
term error_reason;
1596+
term result = binary_to_atom(ctx, arg1, arg2, true, &error_reason);
1597+
if (UNLIKELY(term_is_invalid_term(result))) {
1598+
RAISE_ERROR_BIF(fail_label, error_reason);
1599+
}
1600+
return result;
1601+
}
1602+
1603+
term bif_erlang_binary_to_existing_atom_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2)
1604+
{
1605+
UNUSED(live);
1606+
1607+
term error_reason;
1608+
term result = binary_to_atom(ctx, arg1, arg2, false, &error_reason);
1609+
if (UNLIKELY(term_is_invalid_term(result))) {
1610+
RAISE_ERROR_BIF(fail_label, error_reason);
1611+
}
1612+
return result;
1613+
}
1614+
1615+
term binary_to_atom(Context *ctx, term a_binary, term encoding, bool create_new, term *error_reason)
1616+
{
1617+
if (UNLIKELY(!term_is_binary(a_binary))) {
1618+
*error_reason = BADARG_ATOM;
1619+
return term_invalid_term();
1620+
}
1621+
1622+
const char *atom_string = term_binary_data(a_binary);
1623+
size_t atom_string_len = term_binary_size(a_binary);
1624+
if (UNLIKELY(atom_string_len > 255)) {
1625+
*error_reason = SYSTEM_LIMIT_ATOM;
1626+
return term_invalid_term();
1627+
}
1628+
1629+
bool encode_latin1_to_utf8 = false;
1630+
if (UNLIKELY((encoding == LATIN1_ATOM)
1631+
&& !unicode_buf_is_ascii((const uint8_t *) atom_string, atom_string_len))) {
1632+
encode_latin1_to_utf8 = true;
1633+
} else if (UNLIKELY((encoding != LATIN1_ATOM) && (encoding != UNICODE_ATOM)
1634+
&& (encoding != UTF8_ATOM))) {
1635+
*error_reason = BADARG_ATOM;
1636+
return term_invalid_term();
1637+
}
1638+
1639+
AtomString atom;
1640+
if (LIKELY(!encode_latin1_to_utf8)) {
1641+
size_t i = 0;
1642+
while (i < atom_string_len) {
1643+
uint32_t codepoint;
1644+
size_t codepoint_size;
1645+
if (UNLIKELY(bitstring_utf8_decode(
1646+
(uint8_t *) atom_string + i, atom_string_len, &codepoint, &codepoint_size))
1647+
!= UnicodeTransformDecodeSuccess) {
1648+
*error_reason = BADARG_ATOM;
1649+
return term_invalid_term();
1650+
}
1651+
i += codepoint_size;
1652+
}
1653+
1654+
atom = malloc(atom_string_len + 1);
1655+
((uint8_t *) atom)[0] = atom_string_len;
1656+
memcpy(((char *) atom) + 1, atom_string, atom_string_len);
1657+
} else {
1658+
// * 2 is the worst case size
1659+
size_t buf_len = atom_string_len * 2;
1660+
atom = malloc(buf_len + 1);
1661+
uint8_t *atom_data = ((uint8_t *) atom) + 1;
1662+
size_t out_pos = 0;
1663+
for (size_t i = 0; i < atom_string_len; i++) {
1664+
size_t out_size;
1665+
bitstring_utf8_encode(((uint8_t) atom_string[i]), &atom_data[out_pos], &out_size);
1666+
out_pos += out_size;
1667+
}
1668+
if (out_pos > 255) {
1669+
free((void *) atom);
1670+
*error_reason = SYSTEM_LIMIT_ATOM;
1671+
return term_invalid_term();
1672+
}
1673+
((uint8_t *) atom)[0] = out_pos;
1674+
}
1675+
1676+
enum AtomTableCopyOpt atom_opts = AtomTableCopyAtom;
1677+
if (!create_new) {
1678+
atom_opts |= AtomTableAlreadyExisting;
1679+
}
1680+
long global_atom_index = atom_table_ensure_atom(ctx->global->atom_table, atom, atom_opts);
1681+
free((void *) atom);
1682+
if (UNLIKELY(global_atom_index == ATOM_TABLE_NOT_FOUND)) {
1683+
*error_reason = BADARG_ATOM;
1684+
return term_invalid_term();
1685+
} else if (UNLIKELY(global_atom_index == ATOM_TABLE_ALLOC_FAIL)) {
1686+
*error_reason = OUT_OF_MEMORY_ATOM;
1687+
return term_invalid_term();
1688+
}
1689+
return term_from_atom_index(global_atom_index);
1690+
}

src/libAtomVM/bif.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ term bif_erlang_max_2(Context *ctx, uint32_t fail_label, term arg1, term arg2);
112112

113113
term bif_erlang_size_1(Context *ctx, uint32_t fail_label, int live, term arg1);
114114

115+
term bif_erlang_list_to_atom_1(Context *ctx, uint32_t fail_label, int live, term arg1);
116+
term bif_erlang_list_to_existing_atom_1(Context *ctx, uint32_t fail_label, int live, term arg1);
117+
term bif_erlang_binary_to_atom_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2);
118+
term bif_erlang_binary_to_existing_atom_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2);
119+
120+
// helpers:
121+
term binary_to_atom(Context *ctx, term a_binary, term encoding, bool create_new, term *error_reason);
122+
115123
#ifdef __cplusplus
116124
}
117125
#endif

src/libAtomVM/bifs.gperf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,7 @@ erlang:map_get/2, {.bif.base.type = BIFFunctionType, .bif.bif2_ptr = bif_erlang_
9292
erlang:min/2, {.bif.base.type = BIFFunctionType, .bif.bif2_ptr = bif_erlang_min_2}
9393
erlang:max/2, {.bif.base.type = BIFFunctionType, .bif.bif2_ptr = bif_erlang_max_2}
9494
erlang:size/1, {.gcbif.base.type = GCBIFFunctionType, .gcbif.gcbif1_ptr = bif_erlang_size_1}
95+
erlang:list_to_atom/1, {.gcbif.base.type = GCBIFFunctionType, .gcbif.gcbif1_ptr = bif_erlang_list_to_atom_1}
96+
erlang:list_to_existing_atom/1, {.gcbif.base.type = GCBIFFunctionType, .gcbif.gcbif1_ptr = bif_erlang_list_to_existing_atom_1}
97+
erlang:binary_to_atom/2, {.gcbif.base.type = GCBIFFunctionType, .gcbif.gcbif2_ptr = bif_erlang_binary_to_atom_2}
98+
erlang:binary_to_existing_atom/2, {.gcbif.base.type = GCBIFFunctionType, .gcbif.gcbif2_ptr = bif_erlang_binary_to_existing_atom_2}

0 commit comments

Comments
 (0)