summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Gustavsson <bjorn@erlang.org>2021-09-24 07:36:43 +0200
committerBjörn Gustavsson <bjorn@erlang.org>2021-10-14 04:59:22 +0200
commit4510ef190d337da0daa1b91423405352af455432 (patch)
tree51dd73fb0483fb2f018ac6fedf912ec3747a235c
parent844a90a6b3b8d503b4111c36067e5d1c4a785210 (diff)
downloaderlang-4510ef190d337da0daa1b91423405352af455432.tar.gz
Implement the bs_create_bin instruction
Implement the bs_create_bin instruction for the JITs and the emulator. Consider this example module: -module(foo). -export([bin/2, int/2, float/2]). bin(A, B) -> <<A/binary, B/binary>>. int(A, B) -> <<A/integer, B/integer>>. float(A, B) -> <<A/float, B/float>>. var_int(A, Size) -> <<A:Size>>. With the extended error information introduced in this commit, failures to build binaries will be reported like this: 1> c(foo). {ok,foo} 2> foo:bin(2, <<>>). ** exception error: bad argument in function foo:bin/2 (foo.erl, line 4) *** failed constructing binary: segment 1, type 'binary': not a binary 3> foo:bin(<<>>, 42). ** exception error: bad argument in function foo:bin/2 (foo.erl, line 4) *** failed constructing binary: segment 2, type 'binary': not a binary 4> foo:bin(<<>>, <<1:1>>). ** exception error: bad argument in function foo:bin/2 (foo.erl, line 4) *** failed constructing binary: segment 2, type 'binary': the size of the given binary/bitstring is not a multiple of the unit for the segment 5> foo:int(a, 42). ** exception error: bad argument in function foo:int/2 (foo.erl, line 5) *** failed constructing binary: segment 1, type 'integer': not an integer 6> foo:float(<<>>, <<>>). ** exception error: bad argument in function foo:float/2 (foo.erl, line 6) *** failed constructing binary: segment 1, type 'float': not a float or an integer 7> foo:var_int(42, -1). ** exception error: bad argument in function foo:var_int/2 (foo.erl, line 6) *** failed constructing binary: segment 1, type 'integer': the size is negative 8> foo:var_int(42, a). ** exception error: bad argument in function foo:var_int/2 (foo.erl, line 6) *** failed constructing binary: segment 1, type 'integer': the size is not an integer 9> foo:var_int(42, 1 bsl 64). ** exception error: a system limit has been reached in function foo:var_int/2 (foo.erl, line 6) *** failed constructing binary: segment 1, type 'integer': the size is too large Closes #4971.
-rw-r--r--erts/emulator/beam/atom.names8
-rw-r--r--erts/emulator/beam/beam_common.c5
-rw-r--r--erts/emulator/beam/beam_debug.c49
-rw-r--r--erts/emulator/beam/emu/beam_emu.c54
-rw-r--r--erts/emulator/beam/emu/bs_instrs.tab434
-rw-r--r--erts/emulator/beam/emu/emu_load.c15
-rw-r--r--erts/emulator/beam/emu/generators.tab160
-rw-r--r--erts/emulator/beam/emu/ops.tab16
-rw-r--r--erts/emulator/beam/erl_bits.c82
-rw-r--r--erts/emulator/beam/erl_bits.h25
-rw-r--r--erts/emulator/beam/global.h1
-rw-r--r--erts/emulator/beam/jit/arm/beam_asm.hpp2
-rw-r--r--erts/emulator/beam/jit/arm/generators.tab56
-rw-r--r--erts/emulator/beam/jit/arm/instr_bs.cpp590
-rw-r--r--erts/emulator/beam/jit/arm/ops.tab16
-rw-r--r--erts/emulator/beam/jit/beam_jit_common.cpp111
-rw-r--r--erts/emulator/beam/jit/beam_jit_common.hpp32
-rw-r--r--erts/emulator/beam/jit/x86/beam_asm.hpp1
-rw-r--r--erts/emulator/beam/jit/x86/generators.tab56
-rw-r--r--erts/emulator/beam/jit/x86/instr_bs.cpp534
-rw-r--r--erts/emulator/beam/jit/x86/ops.tab16
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl115
-rw-r--r--erts/emulator/test/exception_SUITE.erl11
-rwxr-xr-xerts/emulator/utils/beam_makeops1
-rw-r--r--lib/compiler/src/beam_asm.erl11
-rw-r--r--lib/compiler/src/beam_clean.erl4
-rw-r--r--lib/compiler/src/beam_disasm.erl21
-rwxr-xr-xlib/compiler/src/genop.tab6
-rw-r--r--lib/kernel/src/erl_erts_errors.erl54
29 files changed, 2425 insertions, 61 deletions
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 55b662abc9..49b9fd0e9f 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -297,9 +297,11 @@ atom flags
atom flush
atom flush_monitor_messages
atom force
+atom format_bs_fail
atom format_cpu_topology
atom free
atom fullsweep_after
+atom function
atom functions
atom function_clause
atom garbage_collect
@@ -358,6 +360,7 @@ atom inherit
atom init
atom initial_call
atom input
+atom integer
atom internal
atom internal_error
atom instruction_counts
@@ -451,6 +454,7 @@ atom namelist
atom native
atom native_addresses
atom need_gc
+atom negative_size
atom Neq='=/='
atom Neqeq='/='
atom net_kernel
@@ -546,6 +550,7 @@ atom prepare_on_load
atom print
atom priority
atom private
+atom private_append
atom process
atom processes
atom processes_used
@@ -711,10 +716,11 @@ atom unregister
atom urun
atom use_stdio
atom used
-atom utf8
+atom utf8 utf16 utf32
atom unblock
atom unblock_normal
atom uniq
+atom unit
atom unless_suspending
atom unloaded
atom unloaded_only
diff --git a/erts/emulator/beam/beam_common.c b/erts/emulator/beam/beam_common.c
index 0ef3922301..49f55b1378 100644
--- a/erts/emulator/beam/beam_common.c
+++ b/erts/emulator/beam/beam_common.c
@@ -996,10 +996,13 @@ save_stacktrace(Process* c_p, ErtsCodePtr pc, Eterm* reg,
args = make_arglist(c_p, reg, bif_mfa->arity);
} else {
+ if (c_p->freason & EXF_HAS_EXT_INFO && is_map(c_p->fvalue)) {
+ error_info = c_p->fvalue;
+ }
non_bif_stacktrace:
-
s->current = c_p->current;
+
/*
* For a function_clause error, the arguments are in the beam
* registers and c_p->current is set.
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index e995ca28db..0f8ae9fdea 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -898,6 +898,55 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr)
}
}
break;
+ case op_i_bs_create_bin_jItdW:
+ {
+ int n = unpacked[-1];
+ int i = 0;
+ Eterm type = 0;
+
+ while (n > 0) {
+ switch (i % 5) {
+ case 0: /* Type */
+ type = ap[i];
+ erts_print(to, to_arg, " `%d`", type);
+ break;
+ case 1: /* Unit */
+ case 2: /* Flags */
+ erts_print(to, to_arg, " `%d`", (Eterm) ap[i]);
+ break;
+ case 4: /* Size */
+ if (type == BSC_BINARY_FIXED_SIZE ||
+ type == BSC_FLOAT_FIXED_SIZE ||
+ type == BSC_INTEGER_FIXED_SIZE ||
+ type == BSC_STRING ||
+ type == BSC_UTF32) {
+ erts_print(to, to_arg, " `%d`", ap[i]);
+ break;
+ }
+
+ /*FALLTHROUGH*/
+ case 3: /* Src */
+ if (type == BSC_STRING) {
+ erts_print(to, to_arg, " ");
+ print_byte_string(to, to_arg, (byte *) ap[i], ap[i+1]);
+ break;
+ }
+ switch (loader_tag(ap[i])) {
+ case LOADER_X_REG:
+ erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[i]));
+ break;
+ case LOADER_Y_REG:
+ erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[i]) - CP_SIZE);
+ break;
+ default:
+ erts_print(to, to_arg, " `%T`", (Eterm) ap[i]);
+ break;
+ }
+ }
+ i++, size++, n--;
+ }
+ }
+ break;
}
erts_print(to, to_arg, "\n");
diff --git a/erts/emulator/beam/emu/beam_emu.c b/erts/emulator/beam/emu/beam_emu.c
index b16e23c61e..ec71dd2088 100644
--- a/erts/emulator/beam/emu/beam_emu.c
+++ b/erts/emulator/beam/emu/beam_emu.c
@@ -713,3 +713,57 @@ erts_beam_jump_table(void)
return 1;
#endif
}
+
+void
+erts_prepare_bs_construct_fail_info(Process* c_p, const BeamInstr* p, Eterm reason, Eterm Info)
+{
+ Eterm* hp = HeapFragOnlyAlloc(c_p, MAP3_SZ+3+1);
+ Eterm cause_tuple;
+ Eterm op;
+ Eterm error_info;
+ Uint segment;
+
+ segment = p[2] >> 3;
+
+ switch (p[0]) {
+ case BSC_APPEND:
+ case BSC_PRIVATE_APPEND:
+ case BSC_BINARY:
+ case BSC_BINARY_FIXED_SIZE:
+ case BSC_BINARY_ALL:
+ op = am_binary;
+ break;
+ case BSC_FLOAT:
+ case BSC_FLOAT_FIXED_SIZE:
+ op = am_float;
+ break;
+ case BSC_INTEGER:
+ case BSC_INTEGER_FIXED_SIZE:
+ op = am_integer;
+ break;
+ case BSC_STRING:
+ op = am_string;
+ break;
+ case BSC_UTF8:
+ op = am_utf8;
+ break;
+ case BSC_UTF16:
+ op = am_utf16;
+ break;
+ case BSC_UTF32:
+ op = am_utf32;
+ break;
+ default:
+ op = am_none;
+ break;
+ }
+
+ cause_tuple = TUPLE3(hp, make_small(segment), op, Info);
+ hp += 4;
+ error_info = MAP3(hp,
+ am_cause, cause_tuple,
+ am_function, am_format_bs_fail,
+ am_module, am_erl_erts_errors);
+ c_p->fvalue = error_info;
+ c_p->freason = reason | EXF_HAS_EXT_INFO;
+}
diff --git a/erts/emulator/beam/emu/bs_instrs.tab b/erts/emulator/beam/emu/bs_instrs.tab
index a5e6bb46de..e143331e51 100644
--- a/erts/emulator/beam/emu/bs_instrs.tab
+++ b/erts/emulator/beam/emu/bs_instrs.tab
@@ -835,6 +835,440 @@ i_bs_validate_unicode_retract(Fail, Src, Ms) {
}
}
+BS_GET_TERM(Term, Dst) {
+ $Dst = $Term;
+ switch (loader_tag($Dst)) {
+ case LOADER_X_REG:
+ $Dst = x(loader_x_reg_index($Dst));
+ break;
+ case LOADER_Y_REG:
+ $Dst = y(loader_y_reg_index($Dst));
+ break;
+ }
+}
+
+BS_LOAD_UNIT(Ptr, Dst) {
+ $Dst = $Ptr[1];
+}
+
+BS_LOAD_FLAGS(Ptr, Dst) {
+ $Dst = $Ptr[2];
+}
+
+BS_LOAD_SRC(Ptr, Dst) {
+ $BS_GET_TERM($Ptr[3], $Dst);
+}
+
+BS_LOAD_STRING_SRC(Ptr, Dst) {
+ $Dst = (byte *) $Ptr[3];
+}
+
+BS_LOAD_SIZE(Ptr, Dst) {
+ $BS_GET_TERM($Ptr[4], $Dst);
+}
+
+BS_LOAD_FIXED_SIZE(Ptr, Dst) {
+ $Dst = $Ptr[4];
+}
+
+BS_FAIL_INFO(Fail, Reason, ErrorType) {
+ erts_prepare_bs_construct_fail_info(c_p, p, $Reason, $ErrorType);
+ $FAIL_HEAD_OR_BODY($Fail);
+}
+
+BS_FAIL_INFO_SYSTEM_LIMIT(Fail) {
+ $BS_FAIL_INFO($Fail, SYSTEM_LIMIT, am_size);
+}
+
+i_bs_create_bin(Fail, Alloc, Live, Dst, N) {
+ //| -no_prefetch
+ int n = $N;
+ const BeamInstr* p_start = $NEXT_INSTRUCTION;
+ const BeamInstr* p_end = p_start + n;
+ const BeamInstr* p;
+ Uint num_bytes;
+ Uint alloc = $Alloc;
+ Eterm new_binary;
+
+ /* We count the total number of bits in an unsigned integer. To avoid
+ * having to check for overflow when adding to `num_bits`, we ensure that the
+ * signed size of each segment fits in a word. */
+ Uint num_bits = 0;
+
+ /* Calculate size of binary in bits. */
+ for (p = p_start; p < p_end; p += BSC_NUM_ARGS) {
+ Eterm Src;
+ Eterm Size;
+ Uint unit;
+ Uint fixed_size;
+
+ switch (p[0]) {
+ case BSC_APPEND:
+ case BSC_PRIVATE_APPEND:
+ break;
+ case BSC_BINARY_ALL:
+ {
+ Uint low_bits;
+ Uint byte_size;
+ Uint high_bits;
+
+ $BS_LOAD_SRC(p, Src);
+ if (is_not_binary(Src)) {
+ $BS_FAIL_INFO($Fail, BADARG, am_type);
+ }
+ byte_size = binary_size(Src);
+ high_bits = byte_size >> ((sizeof(Uint) * 8)-3);
+ low_bits = (byte_size << 3) + binary_bitsize(Src);
+ if (high_bits == 0) {
+ num_bits += low_bits;
+ } else {
+ $BS_FAIL_INFO($Fail, SYSTEM_LIMIT, am_size);
+ }
+ }
+ break;
+ case BSC_BINARY_FIXED_SIZE:
+ case BSC_FLOAT_FIXED_SIZE:
+ case BSC_INTEGER_FIXED_SIZE:
+ $BS_LOAD_FIXED_SIZE(p, fixed_size);
+ num_bits += fixed_size;
+ break;
+ case BSC_STRING:
+ $BS_LOAD_FIXED_SIZE(p, fixed_size);
+ num_bits += fixed_size * 8;
+ break;
+ case BSC_BINARY:
+ case BSC_FLOAT:
+ case BSC_INTEGER:
+ $BS_LOAD_UNIT(p, unit);
+ $BS_LOAD_SIZE(p, Size);
+ if (is_small(Size)) {
+ Sint signed_size = signed_val(Size);
+ Uint size;
+ if (signed_size >= 0) {
+ $BS_SAFE_MUL(signed_size, unit, $BS_FAIL_INFO_SYSTEM_LIMIT($Fail), size);
+ if (size >> (sizeof(Uint) * 8 - 1) != 0) {
+ /* The signed size does not fit in a word. */
+ $BS_FAIL_INFO($Fail, SYSTEM_LIMIT, am_size);
+ }
+ num_bits += size;
+ } else {
+ $BS_FAIL_INFO($Fail, BADARG, am_negative_size);
+ }
+ } else {
+#ifdef ARCH_64
+ /* The size must fit in a small on 64-bit platforms. */
+ if (is_big(Size)) {
+ if (!big_sign(Size)) {
+ $BS_FAIL_INFO($Fail, SYSTEM_LIMIT, am_size);
+ } else {
+ $BS_FAIL_INFO($Fail, BADARG, am_negative_size);
+ }
+ } else {
+ /* Not an integer. */
+ $BS_FAIL_INFO($Fail, BADARG, am_size);
+ }
+#else
+ Uint size;
+
+ if (!term_to_Uint(Size, &size)) {
+ if (size == BADARG) {
+ /* Not an integer or a negative integer. Determine which. */
+ if (is_big(Size)) {
+ /* Negative integer. */
+ $BS_FAIL_INFO($Fail, BADARG, am_negative_size);
+ }
+ /* Not an integer. */
+ $BS_FAIL_INFO($Fail, BADARG, am_size);
+ }
+ /* Huge positive integer. */
+ $BS_FAIL_INFO_SYSTEM_LIMIT($Fail);
+ }
+ $BS_SAFE_MUL(size, unit, $BS_FAIL_INFO_SYSTEM_LIMIT($Fail), size);
+ if ((size >> 31) != 0) {
+ $BS_FAIL_INFO_SYSTEM_LIMIT($Fail);
+ } else {
+ num_bits += size;
+ }
+#endif
+ }
+ break;
+ case BSC_UTF8:
+ {
+ int num_bytes;
+
+ /*
+ * Calculate the number of bits needed to encode the
+ * source operand to UTF-8. If the source operand is
+ * invalid (e.g. wrong type or range) we return a
+ * nonsense integer result (32). We can get away
+ * with that because we KNOW that full error checking
+ * will be done in the construction phase.
+ */
+
+ $BS_LOAD_SRC(p, Src);
+ if (Src < make_small(0x80UL)) {
+ num_bytes = 1;
+ } else if (Src < make_small(0x800UL)) {
+ num_bytes = 2;
+ } else if (Src < make_small(0x10000UL)) {
+ num_bytes = 3;
+ } else {
+ num_bytes = 4;
+ }
+ num_bits += num_bytes * 8;
+ }
+ break;
+ case BSC_UTF16:
+ {
+ int num_bytes = 2;
+
+ /*
+ * Calculate the number of bits needed to encode the
+ * source operarand to UTF-16. If the source operand
+ * is invalid (e.g. wrong type or range) we return a
+ * nonsense integer result (16 or 32). We can get away
+ * with that because we KNOW that full error checking
+ * will be done in the construction phase.
+ */
+
+ $BS_LOAD_SRC(p, Src);
+ if (Src >= make_small(0x10000UL)) {
+ num_bytes = 4;
+ }
+ num_bits += num_bytes * 8;
+ }
+ break;
+ case BSC_UTF32:
+ $BS_LOAD_SRC(p, Src);
+
+ /*
+ * There is no need to untag the integer, but it IS
+ * necessary to make sure it is small (if the term is a
+ * bignum, it could slip through the test, and there is no
+ * further test that would catch it, since bit syntax
+ * construction silently masks too big numbers).
+ */
+ if (is_not_small(Src) || Src > make_small(0x10FFFFUL) ||
+ (make_small(0xD800UL) <= Src && Src <= make_small(0xDFFFUL))) {
+ $BS_FAIL_INFO($Fail, BADARG, am_type);
+ }
+ num_bits += 4 * 8;
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ }
+
+ c_p->fcalls = FCALLS;
+
+ /* Allocate binary. */
+ p = p_start;
+ if (p[0] == BSC_APPEND) {
+ Uint live = $Live;
+ Uint unit;
+ Eterm Src;
+
+ $BS_LOAD_UNIT(p, unit);
+ $BS_LOAD_SRC(p, Src);
+ HEAVY_SWAPOUT;
+ reg[live] = Src;
+ new_binary = erts_bs_append_checked(c_p, reg, live, num_bits, alloc, unit);
+ HEAVY_SWAPIN;
+ if (is_non_value(new_binary)) {
+ $BS_FAIL_INFO($Fail, c_p->freason, c_p->fvalue);
+ }
+ p_start += BSC_NUM_ARGS;
+ } else if (p[0] == BSC_PRIVATE_APPEND) {
+ Uint unit;
+ Eterm Src;
+
+ if (alloc) {
+ $test_heap(alloc, $Live);
+ }
+
+ $BS_LOAD_UNIT(p, unit);
+ $BS_LOAD_SRC(p, Src);
+ new_binary = erts_bs_private_append_checked(c_p, Src, num_bits, unit);
+ if (is_non_value(new_binary)) {
+ $BS_FAIL_INFO($Fail, c_p->freason, c_p->fvalue);
+ }
+ p_start += BSC_NUM_ARGS;
+ } else {
+ num_bytes = ((Uint64)num_bits+(Uint64)7) >> 3;
+ if (num_bits & 7) {
+ alloc += ERL_SUB_BIN_SIZE;
+ }
+ if (num_bytes <= ERL_ONHEAP_BIN_LIMIT) {
+ alloc += heap_bin_size(num_bytes);
+ } else {
+ alloc += PROC_BIN_SIZE;
+ }
+
+ /* num_bits = Number of bits to build
+ * num_bytes = Number of bytes to allocate in the binary
+ * alloc = Total number of words to allocate on heap
+ */
+ erts_bin_offset = 0;
+ erts_writable_bin = 0;
+ if (num_bytes <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin* hb;
+
+ $test_heap(alloc, $Live);
+ hb = (ErlHeapBin *) HTOP;
+ HTOP += heap_bin_size(num_bytes);
+ hb->thing_word = header_heap_bin(num_bytes);
+ hb->size = num_bytes;
+ erts_current_bin = (byte *) hb->data;
+ new_binary = make_binary(hb);
+ } else {
+ Binary* bptr;
+ ProcBin* pb;
+
+ $TEST_BIN_VHEAP(num_bytes / sizeof(Eterm), alloc, $Live);
+
+ /*
+ * Allocate the binary struct itself.
+ */
+ bptr = erts_bin_nrml_alloc(num_bytes);
+ erts_current_bin = (byte *) bptr->orig_bytes;
+
+ /*
+ * Now allocate the ProcBin on the heap.
+ */
+ pb = (ProcBin *) HTOP;
+ HTOP += PROC_BIN_SIZE;
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->size = num_bytes;
+ pb->next = MSO(c_p).first;
+ MSO(c_p).first = (struct erl_off_heap_header*) pb;
+ pb->val = bptr;
+ pb->bytes = (byte*) bptr->orig_bytes;
+ pb->flags = 0;
+ OH_OVERHEAD(&(MSO(c_p)), pb->size / sizeof(Eterm));
+ new_binary = make_binary(pb);
+ }
+
+ if (num_bits & 7) {
+ ErlSubBin* sb;
+
+ sb = (ErlSubBin *) HTOP;
+ HTOP += ERL_SUB_BIN_SIZE;
+ sb->thing_word = HEADER_SUB_BIN;
+ sb->size = num_bytes - 1;
+ sb->bitsize = num_bits & 7;
+ sb->offs = 0;
+ sb->bitoffs = 0;
+ sb->is_writable = 0;
+ sb->orig = new_binary;
+ new_binary = make_binary(sb);
+ }
+ HEAP_SPACE_VERIFIED(0);
+ }
+
+ /* Construct the segments. */
+ for (p = p_start; p < p_end; p += BSC_NUM_ARGS) {
+ Eterm Src;
+ Eterm Size;
+ Eterm flags;
+ Eterm unit;
+ Sint _size;
+
+ if(p[0] == BSC_STRING) {
+ byte* string;
+ $BS_LOAD_STRING_SRC(p, string);
+ $BS_LOAD_FIXED_SIZE(p, Size);
+ erts_new_bs_put_string(ERL_BITS_ARGS_2(string, Size));
+ continue;
+ }
+
+ $BS_LOAD_SRC(p, Src);
+
+ switch (p[0]) {
+ case BSC_BINARY_ALL:
+ $BS_LOAD_UNIT(p, unit);
+ if (!erts_new_bs_put_binary_all(c_p, Src, unit)) {
+ $BS_FAIL_INFO($Fail, BADARG, am_type);
+ }
+ break;
+ case BSC_BINARY:
+ $BS_LOAD_UNIT(p, unit);
+ $BS_LOAD_FLAGS(p, flags);
+ $BS_LOAD_SIZE(p, Size);
+ $BS_GET_UNCHECKED_FIELD_SIZE(Size, unit, $BADARG($Fail), _size);
+ if (!erts_new_bs_put_binary(c_p, Src, _size)) {
+ $BS_FAIL_INFO($Fail, BADARG, c_p->fvalue);
+ }
+ break;
+ case BSC_BINARY_FIXED_SIZE:
+ $BS_LOAD_FIXED_SIZE(p, Size);
+ if (!erts_new_bs_put_binary(c_p, Src, Size)) {
+ $BS_FAIL_INFO($Fail, BADARG, c_p->fvalue);
+ }
+ break;
+ case BSC_FLOAT:
+ $BS_LOAD_UNIT(p, unit);
+ $BS_LOAD_FLAGS(p, flags);
+ $BS_LOAD_SIZE(p, Size);
+ $BS_GET_UNCHECKED_FIELD_SIZE(Size, unit, $BADARG($Fail), _size);
+ if (!erts_new_bs_put_float(c_p, Src, _size, flags)) {
+ $BS_FAIL_INFO($Fail, BADARG, c_p->fvalue);
+ }
+ break;
+ case BSC_FLOAT_FIXED_SIZE:
+ $BS_LOAD_FLAGS(p, flags);
+ $BS_LOAD_FIXED_SIZE(p, Size);
+ if (!erts_new_bs_put_float(c_p, Src, Size, flags)) {
+ $BS_FAIL_INFO($Fail, BADARG, c_p->fvalue);
+ }
+ break;
+ case BSC_INTEGER:
+ {
+ Sint _size;
+
+ $BS_LOAD_UNIT(p, unit);
+ $BS_LOAD_FLAGS(p, flags);
+ $BS_LOAD_SIZE(p, Size);
+ $BS_GET_UNCHECKED_FIELD_SIZE(Size, unit, $BADARG($Fail), _size);
+ if (!erts_new_bs_put_integer(ERL_BITS_ARGS_3(Src, _size, flags))) {
+ $BS_FAIL_INFO($Fail, BADARG, am_type);
+ }
+ }
+ break;
+ case BSC_INTEGER_FIXED_SIZE:
+ case BSC_UTF32:
+ $BS_LOAD_FLAGS(p, flags);
+ $BS_LOAD_FIXED_SIZE(p, Size);
+ if (!erts_new_bs_put_integer(ERL_BITS_ARGS_3(Src, Size, flags))) {
+ $BS_FAIL_INFO($Fail, BADARG, am_type);
+ }
+ break;
+ case BSC_UTF8:
+ if (!erts_bs_put_utf8(ERL_BITS_ARGS_1(Src))) {
+ $BS_FAIL_INFO($Fail, BADARG, am_type);
+ }
+ break;
+ case BSC_UTF16:
+ $BS_LOAD_FLAGS(p, flags);
+ $BS_LOAD_SRC(p, Src);
+ if (!erts_bs_put_utf16(ERL_BITS_ARGS_2(Src, flags))) {
+ $BS_FAIL_INFO($Fail, BADARG, am_type);
+ }
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ }
+
+ FCALLS = c_p->fcalls;
+
+ /* Return the resulting binary. */
+ $REFRESH_GEN_DEST();
+ $Dst = new_binary;
+ I += n;
+}
+
//
// Matching of binaries.
diff --git a/erts/emulator/beam/emu/emu_load.c b/erts/emulator/beam/emu/emu_load.c
index fa0372be87..41d5aac41d 100644
--- a/erts/emulator/beam/emu/emu_load.c
+++ b/erts/emulator/beam/emu/emu_load.c
@@ -1376,6 +1376,21 @@ int beam_load_emit_op(LoaderState *stp, BeamOp *tmp_op) {
case op_i_bs_match_string_yfWW:
new_string_patch(stp, ci-1);
break;
+ case op_i_bs_create_bin_jItdW:
+ {
+ int n = tmp_op->arity;
+ BeamInstr* p = &code[ci-n];
+ BeamInstr* end_p = &code[ci];
+ while (p < end_p) {
+ switch (*p) {
+ case BSC_STRING:
+ new_string_patch(stp, p+3-code);
+ break;
+ }
+ p += 5;
+ }
+ }
+ break;
case op_catch_yf:
/* code[ci-3] &&lb_catch_yf
* code[ci-2] y-register offset in E
diff --git a/erts/emulator/beam/emu/generators.tab b/erts/emulator/beam/emu/generators.tab
index f9988f2399..397ddb97e7 100644
--- a/erts/emulator/beam/emu/generators.tab
+++ b/erts/emulator/beam/emu/generators.tab
@@ -831,3 +831,163 @@ gen.init_yregs(N, Yregs) {
$INIT_YREGS(S, N.val);
return first;
}
+
+gen.create_bin(Fail, Alloc, Live, Unit, Dst, N, Segments) {
+ BeamOp* op;
+ int fixed_args;
+ BeamOpArg* src;
+ BeamOpArg* dst;
+ BeamOpArg* endp;
+
+ endp = Segments + N.val;
+ N.val = 5*N.val/6;
+
+ $NewBeamOp(S, op);
+ $BeamOpNameArity(op, i_bs_create_bin, 5);
+ fixed_args = op->arity;
+ $BeamOpArity(op, (N.val + fixed_args));
+
+ op->a[0] = Fail;
+ op->a[1] = Alloc;
+ op->a[2] = Live;
+ op->a[3] = Dst;
+ op->a[4] = N;
+
+ for (src = Segments, dst = op->a+fixed_args; src < endp; src += 6, dst += 5) {
+ UWord unit;
+ BeamOpArg Flags;
+ Uint flags = 0;
+ BeamOpArg Size;
+ Uint type;
+ Uint segment;
+
+ ASSERT(src[0].type = TAG_a);
+ ASSERT(src[1].type == TAG_u);
+ ASSERT(src[2].type == TAG_u);
+ segment = src[1].val;
+
+ /* Get unit. */
+ dst[1] = src[2];
+ unit = dst[1].val;
+
+ /* Translate flags. */
+ Flags = src[3]; /* Flags */
+ if (Flags.type != TAG_n) {
+ if (Flags.type == TAG_q) {
+ Eterm term = beamfile_get_literal(&S->beam, Flags.val);
+ while (is_list(term)) {
+ Eterm* consp = list_val(term);
+ Eterm elem = CAR(consp);
+ switch (elem) {
+ case am_little:
+ flags |= BSF_LITTLE;
+ break;
+ case am_native:
+ flags |= BSF_NATIVE;
+ break;
+ }
+ term = CDR(consp);
+ }
+ ASSERT(is_nil(term));
+ }
+ }
+ Flags.type = TAG_u;
+ Flags.val = flags;
+ $NativeEndian(Flags);
+ Flags.val = (segment << 3) | Flags.val;
+ dst[2] = Flags;
+
+ /* Store source. */
+ dst[3] = src[4]; /* Src */
+
+ /* Get size */
+ Size = src[5]; /* Size */
+
+ /* Translate type. */
+ switch (src[0].val) {
+ case am_append:
+ type = BSC_APPEND;
+ break;
+ case am_private_append:
+ type = BSC_PRIVATE_APPEND;
+ break;
+ case am_binary:
+ {
+ UWord bits;
+ type = BSC_BINARY;
+ if (Size.type == TAG_a && Size.val == am_all) {
+ type = BSC_BINARY_ALL;
+ } else if (Size.type == TAG_i &&
+ (Sint) Size.val >= 0 &&
+ beam_load_safe_mul(Size.val, unit, &bits) &&
+ (bits >> (sizeof(Uint)-1)*8) == 0) {
+ type = BSC_BINARY_FIXED_SIZE;
+ Size.type = TAG_u;
+ Size.val = bits;
+ unit = 0;
+ }
+ }
+ break;
+ case am_float:
+ {
+ UWord bits;
+ type = BSC_FLOAT;
+ if (Size.type == TAG_i &&
+ (Sint) Size.val >= 0 &&
+ beam_load_safe_mul(Size.val, unit, &bits) &&
+ (bits >> (sizeof(Uint)-1)*8) == 0) {
+ type = BSC_FLOAT_FIXED_SIZE;
+ Size.type = TAG_u;
+ Size.val = bits;
+ unit = 0;
+ }
+ }
+ break;
+ case am_integer:
+ {
+ UWord bits;
+ type = BSC_INTEGER;
+ if (Size.type == TAG_i &&
+ (Sint) Size.val >= 0 &&
+ beam_load_safe_mul(Size.val, unit, &bits) &&
+ (bits >> (sizeof(Uint)-1)*8) == 0) {
+ type = BSC_INTEGER_FIXED_SIZE;
+ Size.type = TAG_u;
+ Size.val = bits;
+ unit = 0;
+ }
+ }
+ break;
+ case am_string:
+ type = BSC_STRING;
+ ASSERT(Size.type == TAG_i);
+ ASSERT(unit == 8);
+ Size.type = TAG_u;
+ Size.val = Size.val; /* Size of string in bytes. */
+ unit = 0;
+ break;
+ case am_utf8:
+ type = BSC_UTF8;
+ break;
+ case am_utf16:
+ type = BSC_UTF16;
+ break;
+ case am_utf32:
+ type = BSC_UTF32;
+ Size.type = TAG_u;
+ Size.val = 32;
+ break;
+ default:
+ abort();
+ }
+ dst[0].type = TAG_u;
+ dst[0].val = type;
+
+ /* Store value of unit. */
+ dst[1].val = unit;
+
+ /* Store size. */
+ dst[4] = Size;
+ }
+ return op;
+}
diff --git a/erts/emulator/beam/emu/ops.tab b/erts/emulator/beam/emu/ops.tab
index c50b12513d..0148ebb112 100644
--- a/erts/emulator/beam/emu/ops.tab
+++ b/erts/emulator/beam/emu/ops.tab
@@ -1177,9 +1177,19 @@ bs_skip_utf32 Fail=f Ms=xy Live=u Flags=u => \
i_bs_validate_unicode_retract j s S
%hot
-#
-# Constructing binaries
-#
+# ================================================================
+# New binary construction (OTP 25).
+# ================================================================
+
+bs_create_bin Fail Alloc=u Live=u Unit=u Dst=xy N=u Segments=* => \
+ create_bin(Fail, Alloc, Live, Unit, Dst, N, Segments)
+
+i_bs_create_bin j I t d W *
+
+# ================================================================
+# Old instruction for constructing binaries (up to OTP 24).
+# ================================================================
+
%warm
bs_init2 Fail Sz Words Regs Flags Dst | binary_too_big(Sz) => system_limit Fail
diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c
index e4dcb729f8..2fb0d26059 100644
--- a/erts/emulator/beam/erl_bits.c
+++ b/erts/emulator/beam/erl_bits.c
@@ -1021,10 +1021,12 @@ erts_new_bs_put_binary(Process *c_p, Eterm arg, Uint num_bits)
ERL_BITS_DEFINE_STATEP(c_p);
if (!is_binary(arg)) {
- return 0;
+ c_p->fvalue = am_type;
+ return 0;
}
ERTS_GET_BINARY_BYTES(arg, bptr, bitoffs, bitsize);
if (num_bits > 8*binary_size(arg)+bitsize) {
+ c_p->fvalue = am_short;
return 0;
}
copy_binary_to_buffer(erts_current_bin, erts_bin_offset, bptr, bitoffs, num_bits);
@@ -1098,6 +1100,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
#endif
} else if (is_big(arg)) {
if (big_to_double(arg, &u.f64) < 0) {
+ c_p->fvalue = am_no_float;
return 0;
}
#ifdef DOUBLE_MIDDLE_ENDIAN
@@ -1108,6 +1111,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
b = u.i32[1];
#endif
} else {
+ c_p->fvalue = am_type;
return 0;
}
} else if (num_bits == 32) {
@@ -1130,6 +1134,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
} else if (is_big(arg)) {
double f64;
if (big_to_double(arg, &f64) < 0) {
+ c_p->fvalue = am_no_float;
return 0;
}
ERTS_FP_CHECK_INIT(c_p);
@@ -1137,6 +1142,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
ERTS_FP_ERROR(c_p,u.f32,;);
a = u.i32;
} else {
+ c_p->fvalue = am_type;
return 0;
}
} else if (num_bits == 16) {
@@ -1159,6 +1165,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
} else if (is_big(arg)) {
double f64;
if (big_to_double(arg, &f64) < 0) {
+ c_p->fvalue = am_no_float;
return 0;
}
ERTS_FP_CHECK_INIT(c_p);
@@ -1166,9 +1173,11 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
u.f16 = FP16_FROM_FP64(f64);
a = u.i16;
} else {
+ c_p->fvalue = am_type;
return 0;
}
} else {
+ c_p->fvalue = am_invalid;
return 0;
}
@@ -1265,6 +1274,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
#endif
} else if (is_big(arg)) {
if (big_to_double(arg, &f64) < 0) {
+ c_p->fvalue = am_no_float;
return 0;
}
#ifdef DOUBLE_MIDDLE_ENDIAN
@@ -1273,6 +1283,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
bptr = (byte *) &f64;
#endif
} else {
+ c_p->fvalue = am_type;
return 0;
}
#ifdef DOUBLE_MIDDLE_ENDIAN
@@ -1293,6 +1304,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
bptr = (byte *) &f32;
} else if (is_big(arg)) {
if (big_to_double(arg, &f64) < 0) {
+ c_p->fvalue = am_no_float;
return 0;
}
ERTS_FP_CHECK_INIT(c_p);
@@ -1300,6 +1312,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
ERTS_FP_ERROR(c_p,f32,;);
bptr = (byte *) &f32;
} else {
+ c_p->fvalue = am_type;
return 0;
}
} else if (num_bits == 16) {
@@ -1315,6 +1328,7 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
bptr = (byte *) &f16;
} else if (is_big(arg)) {
if (big_to_double(arg, &f64) < 0) {
+ c_p->fvalue = am_no_float;
return 0;
}
ERTS_FP_CHECK_INIT(c_p);
@@ -1322,9 +1336,11 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags)
f16 = FP16_FROM_FP64(f64);
bptr = (byte *) &f16;
} else {
+ c_p->fvalue = am_type;
return 0;
}
} else {
+ c_p->fvalue = am_invalid;
return 0;
}
if (BIT_IS_MACHINE_ENDIAN(flags)) {
@@ -1369,19 +1385,10 @@ void increase_proc_bin_sz(Process* p, ProcBin* pb, Uint new_size)
Eterm
erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
- Uint extra_words, Uint unit)
+ Uint extra_words, Uint unit)
{
- Eterm bin; /* Given binary */
- Eterm* ptr;
- Eterm hdr;
- ErlSubBin* sb;
- ProcBin* pb;
- Binary* binp;
- Uint heap_need;
- Uint build_size_in_bits;
- Uint used_size_in_bits;
Uint unsigned_bits;
- ERL_BITS_DEFINE_STATEP(c_p);
+ Uint build_size_in_bits;
/*
* Check and untag the requested build size.
@@ -1389,7 +1396,8 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
if (is_small(build_size_term)) {
Sint signed_bits = signed_val(build_size_term);
if (signed_bits < 0) {
- goto badarg;
+ c_p->freason = BADARG;
+ return THE_NON_VALUE;
}
build_size_in_bits = (Uint) signed_bits;
} else if (term_to_Uint(build_size_term, &unsigned_bits)) {
@@ -1398,12 +1406,33 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
c_p->freason = unsigned_bits;
return THE_NON_VALUE;
}
+ return erts_bs_append_checked(c_p, reg, live, build_size_in_bits,
+ extra_words, unit);
+}
+
+Eterm
+erts_bs_append_checked(Process* c_p, Eterm* reg, Uint live,
+ Uint build_size_in_bits, Uint extra_words,
+ Uint unit)
+{
+ Eterm bin; /* Given binary */
+ Eterm* ptr;
+ Eterm hdr;
+ ErlSubBin* sb;
+ ProcBin* pb;
+ Binary* binp;
+ Uint heap_need;
+ Uint used_size_in_bits;
+ ERL_BITS_DEFINE_STATEP(c_p);
/*
* Check the binary argument.
*/
bin = reg[live];
if (!is_boxed(bin)) {
+ type_error:
+ c_p->fvalue = am_type;
+
badarg:
c_p->freason = BADARG;
return THE_NON_VALUE;
@@ -1411,7 +1440,7 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
ptr = boxed_val(bin);
hdr = *ptr;
if (!is_binary_header(hdr)) {
- goto badarg;
+ goto type_error;
}
if (hdr != HEADER_SUB_BIN) {
goto not_writable;
@@ -1434,6 +1463,7 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
if (unit > 1) {
if ((unit == 8 && (erts_bin_offset & 7) != 0) ||
(erts_bin_offset % unit) != 0) {
+ c_p->fvalue = am_unit;
goto badarg;
}
}
@@ -1447,6 +1477,7 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
}
if((ERTS_UINT_MAX - build_size_in_bits) < erts_bin_offset) {
+ c_p->fvalue = am_size;
c_p->freason = SYSTEM_LIMIT;
return THE_NON_VALUE;
}
@@ -1526,6 +1557,7 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
if (unit > 1) {
if ((unit == 8 && (erts_bin_offset & 7) != 0) ||
(erts_bin_offset % unit) != 0) {
+ c_p->fvalue = am_unit;
goto badarg;
}
}
@@ -1535,6 +1567,7 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
}
if((ERTS_UINT_MAX - build_size_in_bits) < erts_bin_offset) {
+ c_p->fvalue = am_size;
c_p->freason = SYSTEM_LIMIT;
return THE_NON_VALUE;
}
@@ -1599,14 +1632,8 @@ erts_bs_append(Process* c_p, Eterm* reg, Uint live, Eterm build_size_term,
Eterm
erts_bs_private_append(Process* p, Eterm bin, Eterm build_size_term, Uint unit)
{
- Eterm* ptr;
- ErlSubBin* sb;
- ProcBin* pb;
- Binary* binp;
- Uint build_size_in_bits;
- Uint pos_in_bits_after_build;
Uint unsigned_bits;
- ERL_BITS_DEFINE_STATEP(p);
+ Uint build_size_in_bits;
/*
* Check and untag the requested build size.
@@ -1624,6 +1651,18 @@ erts_bs_private_append(Process* p, Eterm bin, Eterm build_size_term, Uint unit)
p->freason = unsigned_bits;
return THE_NON_VALUE;
}
+ return erts_bs_private_append_checked(p, bin, build_size_in_bits, unit);
+}
+
+Eterm
+erts_bs_private_append_checked(Process* p, Eterm bin, Uint build_size_in_bits, Uint unit)
+{
+ Eterm* ptr;
+ ErlSubBin* sb;
+ ProcBin* pb;
+ Binary* binp;
+ Uint pos_in_bits_after_build;
+ ERL_BITS_DEFINE_STATEP(p);
ptr = boxed_val(bin);
ASSERT(*ptr == HEADER_SUB_BIN);
@@ -1640,6 +1679,7 @@ erts_bs_private_append(Process* p, Eterm bin, Eterm build_size_term, Uint unit)
erts_bin_offset = 8*sb->size + sb->bitsize;
if((ERTS_UINT_MAX - build_size_in_bits) < erts_bin_offset) {
+ p->fvalue = am_size;
p->freason = SYSTEM_LIMIT;
return THE_NON_VALUE;
}
diff --git a/erts/emulator/beam/erl_bits.h b/erts/emulator/beam/erl_bits.h
index 25aff39047..5ac6dd9ca7 100644
--- a/erts/emulator/beam/erl_bits.h
+++ b/erts/emulator/beam/erl_bits.h
@@ -182,7 +182,10 @@ Eterm erts_bs_get_utf8(ErlBinMatchBuffer* mb);
Eterm erts_bs_get_utf16(ErlBinMatchBuffer* mb, Uint flags);
Eterm erts_bs_append(Process* p, Eterm* reg, Uint live, Eterm build_size_term,
Uint extra_words, Uint unit);
+Eterm erts_bs_append_checked(Process* p, Eterm* reg, Uint live, Uint size,
+ Uint extra_words, Uint unit);
Eterm erts_bs_private_append(Process* p, Eterm bin, Eterm sz, Uint unit);
+Eterm erts_bs_private_append_checked(Process* p, Eterm bin, Uint size, Uint unit);
Eterm erts_bs_init_writable(Process* p, Eterm sz);
/*
@@ -204,7 +207,7 @@ Eterm erts_extract_sub_binary(Eterm **hp, Eterm base_bin, byte *base_data,
#define EXTRACT_SUB_BIN_HEAP_NEED (heap_bin_size(ERL_ONHEAP_BIN_LIMIT))
/*
- * Flags for bs_get_* / bs_put_* / bs_init* instructions.
+ * Flags for bs_create_bin / bs_get_* / bs_put_* / bs_init* instructions.
*/
#define BSF_ALIGNED 1 /* Field is guaranteed to be byte-aligned. */
@@ -213,4 +216,24 @@ Eterm erts_extract_sub_binary(Eterm **hp, Eterm base_bin, byte *base_data,
#define BSF_EXACT 8 /* Size in bs_init is exact. */
#define BSF_NATIVE 16 /* Native endian. */
+/*
+ * Binary construction operations.
+ */
+
+#define BSC_APPEND 0
+#define BSC_PRIVATE_APPEND 1
+#define BSC_BINARY 2
+#define BSC_BINARY_FIXED_SIZE 3
+#define BSC_BINARY_ALL 4
+#define BSC_FLOAT 5
+#define BSC_FLOAT_FIXED_SIZE 6
+#define BSC_INTEGER 7
+#define BSC_INTEGER_FIXED_SIZE 8
+#define BSC_STRING 9
+#define BSC_UTF8 10
+#define BSC_UTF16 11
+#define BSC_UTF32 12
+
+#define BSC_NUM_ARGS 5
+
#endif /* __ERL_BITS_H__ */
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 904d394fca..2d5fced7c8 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1236,6 +1236,7 @@ void print_pass_through(int, byte*, int);
int catchlevel(Process*);
void init_emulator(void);
void process_main(ErtsSchedulerData *);
+void erts_prepare_bs_construct_fail_info(Process* c_p, const BeamInstr* p, Eterm reason, Eterm Info);
void erts_dirty_process_main(ErtsSchedulerData *);
Eterm build_stacktrace(Process* c_p, Eterm exc);
Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value);
diff --git a/erts/emulator/beam/jit/arm/beam_asm.hpp b/erts/emulator/beam/jit/arm/beam_asm.hpp
index fbd42ba962..bc873991a1 100644
--- a/erts/emulator/beam/jit/arm/beam_asm.hpp
+++ b/erts/emulator/beam/jit/arm/beam_asm.hpp
@@ -800,6 +800,8 @@ class BeamGlobalAssembler : public BeamAssembler {
_(bif_tuple_size_guard) \
_(bs_add_guard_shared) \
_(bs_add_body_shared) \
+ _(bs_bit_size_shared) \
+ _(bs_create_bin_error_shared) \
_(bs_get_tail_shared) \
_(bs_size_check_shared) \
_(call_bif_shared) \
diff --git a/erts/emulator/beam/jit/arm/generators.tab b/erts/emulator/beam/jit/arm/generators.tab
index abe030f51f..684649f47d 100644
--- a/erts/emulator/beam/jit/arm/generators.tab
+++ b/erts/emulator/beam/jit/arm/generators.tab
@@ -495,3 +495,59 @@ gen.func_end(Func_Label, Entry_Label) {
return op;
}
+
+gen.create_bin(Fail, Alloc, Live, Unit, Dst, N, Segments) {
+ BeamOp* op;
+ int fixed_args;
+ int i;
+
+ $NewBeamOp(S, op);
+ $BeamOpNameArity(op, i_bs_create_bin, 4);
+ fixed_args = op->arity;
+ $BeamOpArity(op, (N.val + fixed_args));
+
+ op->a[0] = Fail;
+ op->a[1] = Alloc;
+ op->a[2] = Live;
+ op->a[3] = Dst;
+
+ for (i = 0; i < N.val; i += 6) {
+ BeamOpArg Flags;
+ Uint flags = 0;
+
+ /* Copy all but flags. */
+ op->a[i+fixed_args+0] = Segments[i+0];
+ op->a[i+fixed_args+1] = Segments[i+1];
+ op->a[i+fixed_args+2] = Segments[i+2];
+ op->a[i+fixed_args+4] = Segments[i+4];
+ op->a[i+fixed_args+5] = Segments[i+5];
+
+ /* Translate flags. */
+ Flags = Segments[i+3]; /* Flags */
+ if (Flags.type != TAG_n) {
+ if (Flags.type == TAG_q) {
+ Eterm term = beamfile_get_literal(&S->beam, Flags.val);
+ while (is_list(term)) {
+ Eterm* consp = list_val(term);
+ Eterm elem = CAR(consp);
+ switch (elem) {
+ case am_little:
+ flags |= BSF_LITTLE;
+ break;
+ case am_native:
+ flags |= BSF_NATIVE;
+ break;
+ }
+ term = CDR(consp);
+ }
+ ASSERT(is_nil(term));
+ }
+ }
+ Flags.type = TAG_u;
+ Flags.val = flags;
+ $NativeEndian(Flags);
+ op->a[i+fixed_args+3] = Flags;
+ }
+
+ return op;
+}
diff --git a/erts/emulator/beam/jit/arm/instr_bs.cpp b/erts/emulator/beam/jit/arm/instr_bs.cpp
index 93a65f613a..3a8d6652f5 100644
--- a/erts/emulator/beam/jit/arm/instr_bs.cpp
+++ b/erts/emulator/beam/jit/arm/instr_bs.cpp
@@ -1355,3 +1355,593 @@ void BeamModuleAssembler::emit_bs_init_writable() {
a.mov(XREG0, ARG1);
}
+
+void BeamGlobalAssembler::emit_bs_create_bin_error_shared() {
+ a.mov(XREG0, a64::x30);
+
+ emit_enter_runtime<Update::eStack | Update::eHeap>(0);
+
+ a.mov(ARG3, ARG1);
+ a.mov(ARG2, ARG4);
+ a.mov(ARG1, c_p);
+ runtime_call<3>(beam_jit_bs_construct_fail_info);
+
+ emit_leave_runtime<Update::eStack | Update::eHeap>(0);
+
+ a.mov(ARG4, ZERO);
+ a.mov(ARG2, XREG0);
+ a.b(labels[raise_exception_shared]);
+}
+
+/*
+ * ARG1 = term
+ *
+ * If the term in ARG1 is a binary on enty, on return
+ * ARG1 will contain the size of the binary in bits and
+ * sign flag will be cleared.
+ *
+ * If the term is not a binary, the sign flag will be set.
+ */
+void BeamGlobalAssembler::emit_bs_bit_size_shared() {
+ Label not_sub_bin = a.newLabel();
+ Label fail = a.newLabel();
+
+ arm::Gp boxed_ptr = emit_ptr_val(ARG1, ARG1);
+
+ emit_is_boxed(fail, boxed_ptr);
+
+ a.ldur(TMP1, emit_boxed_val(boxed_ptr));
+ a.and_(TMP1, TMP1, imm(_TAG_HEADER_MASK));
+ a.cmp(TMP1, imm(_TAG_HEADER_SUB_BIN));
+ a.cond_ne().b(not_sub_bin);
+
+ a.ldur(TMP1, emit_boxed_val(boxed_ptr, sizeof(Eterm)));
+ a.ldurb(TMP2.w(), emit_boxed_val(boxed_ptr, offsetof(ErlSubBin, bitsize)));
+
+ a.adds(ARG1, TMP2, TMP1, arm::lsl(3));
+ a.ret(a64::x30);
+
+ a.bind(not_sub_bin);
+ ERTS_CT_ASSERT(_TAG_HEADER_REFC_BIN + 4 == _TAG_HEADER_HEAP_BIN);
+ a.and_(TMP1, TMP1, imm(~4));
+ a.cmp(TMP1, imm(_TAG_HEADER_REFC_BIN));
+ a.cond_ne().b(fail);
+
+ a.ldur(TMP1, emit_boxed_val(boxed_ptr, sizeof(Eterm)));
+ a.lsl(ARG1, TMP1, imm(3));
+ a.tst(ARG1, ARG1);
+
+ a.ret(a64::x30);
+
+ a.bind(fail);
+ mov_imm(ARG1, -1);
+ a.tst(ARG1, ARG1);
+
+ a.ret(a64::x30);
+}
+
+struct BscSegment {
+ BscSegment()
+ : src(ArgVal(ArgVal::Immediate, NIL)),
+ size(ArgVal(ArgVal::Immediate, NIL)), effectiveSize(-1){};
+
+ Eterm type;
+ Uint unit;
+ Uint flags;
+ ArgVal src;
+ ArgVal size;
+
+ Uint error_info;
+ Sint effectiveSize;
+};
+
+void BeamModuleAssembler::emit_i_bs_create_bin(const ArgVal &Fail,
+ const ArgVal &Alloc,
+ const ArgVal &Live0,
+ const ArgVal &Dst,
+ const Span<ArgVal> &args) {
+ Uint num_bits = 0;
+ std::size_t n = args.size();
+ std::vector<BscSegment> segments;
+ Label error = a.newLabel();
+ ArgVal Live = Live0;
+ arm::Gp sizeReg;
+
+ /*
+ * Collect information about each segment and calculate sizes of
+ * fixed segments.
+ */
+ for (std::size_t i = 0; i < n; i += 6) {
+ BscSegment seg;
+ Uint bsc_op;
+ Uint bsc_segment;
+
+ ASSERT(args[i].isImmed());
+ ASSERT(args[i + 1].getType() == TAG_u);
+ ASSERT(args[i + 2].getType() == TAG_u);
+ ASSERT(args[i + 3].getType() == TAG_u);
+ seg.type = args[i].getValue();
+ bsc_segment = args[i + 1].getValue();
+ seg.unit = args[i + 2].getValue();
+ seg.flags = args[i + 3].getValue();
+ seg.src = args[i + 4];
+ seg.size = args[i + 5];
+
+ switch (seg.type) {
+ case am_float:
+ bsc_op = BSC_OP_FLOAT;
+ break;
+ case am_integer:
+ bsc_op = BSC_OP_INTEGER;
+ break;
+ case am_utf8:
+ bsc_op = BSC_OP_UTF8;
+ break;
+ case am_utf16:
+ bsc_op = BSC_OP_UTF16;
+ break;
+ case am_utf32:
+ bsc_op = BSC_OP_UTF32;
+ break;
+ default:
+ bsc_op = BSC_OP_BINARY;
+ break;
+ }
+
+ /*
+ * Save segment number and operation for use in extended
+ * error information.
+ */
+ seg.error_info = BSC_SET_SEGMENT_OP(bsc_segment, bsc_op);
+
+ /*
+ * Attempt to calculate the effective size of this segment.
+ * Give up is variable or invalid.
+ */
+ if (seg.size.isImmed() && seg.unit != 0) {
+ Eterm size = seg.size.getValue();
+ if (is_small(size)) {
+ Uint unsigned_size = unsigned_val(size);
+ if ((unsigned_size >> (sizeof(Eterm) - 1) * 8) == 0) {
+ /* This multiplication cannot overflow. */
+ Uint seg_size = seg.unit * unsigned_size;
+ seg.effectiveSize = seg_size;
+ num_bits += seg_size;
+ }
+ }
+ }
+
+ if (seg.effectiveSize < 0 && seg.type != am_append &&
+ seg.type != am_private_append) {
+ /* At least one segment will need a dynamic size
+ * calculation. */
+ sizeReg = ARG8;
+ }
+
+ segments.insert(segments.end(), seg);
+ }
+
+ if (Fail.getValue() != 0) {
+ error = resolve_beam_label(Fail, dispUnknown);
+ } else {
+ Label past_error = a.newLabel();
+
+ a.b(past_error);
+
+ a.bind(error);
+ {
+ comment("handle error");
+
+ /*
+ * ARG4 = packed error information
+ * ARG1 = optional bad size value; only valid if
+ * BSC_REASON_DEPENDS is set in ARG4
+ */
+ fragment_call(ga->get_bs_create_bin_error_shared());
+ last_error_offset = getOffset() & -8;
+ }
+
+ a.bind(past_error);
+ }
+
+ /* We count the total number of bits in an unsigned integer. To
+ * avoid having to check for overflow when adding to the counter,
+ * we ensure that the signed size of each segment fits in a
+ * word. */
+ if (sizeReg.isValid()) {
+ comment("calculate sizes");
+ mov_imm(sizeReg, num_bits);
+ }
+
+ /* Generate code for calculating the size of the binary to be
+ * created. */
+ for (auto seg : segments) {
+ if (seg.effectiveSize >= 0) {
+ continue;
+ }
+
+ if (seg.type == am_append || seg.type == am_private_append) {
+ continue;
+ }
+
+ if (seg.size.isImmed() && seg.size.getValue() == am_all &&
+ seg.type == am_binary) {
+ comment("size of an entire binary");
+ mov_arg(ARG1, seg.src);
+ fragment_call(ga->get_bs_bit_size_shared());
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.cond_mi().b(resolve_label(error, disp1MB));
+ a.add(sizeReg, sizeReg, ARG1);
+ } else if (seg.unit != 0) {
+ comment("size binary/integer/float/string");
+ mov_arg(ARG1, seg.size);
+ a.and_(TMP2, ARG1, imm(_TAG_IMMED1_MASK));
+ a.cmp(TMP2, imm(_TAG_IMMED1_SMALL));
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_DEPENDS,
+ BSC_INFO_DEPENDS));
+ }
+ a.cond_ne().b(resolve_label(error, disp1MB));
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info |
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_NEGATIVE));
+ }
+ a.tbnz(ARG1, 63, resolve_label(error, disp32K));
+ a.asr(TMP1, ARG1, imm(_TAG_IMMED1_SIZE));
+ if (seg.unit == 1) {
+ a.add(sizeReg, sizeReg, TMP1);
+ } else {
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info |
+ BSC_SET_REASON_INFO(BSC_REASON_SYSTEM_LIMIT,
+ BSC_INFO_SIZE));
+ }
+ a.tst(TMP1, imm(0xffful << 52));
+ a.cond_ne().b(resolve_label(error, disp1MB));
+ mov_imm(TMP2, seg.unit);
+ a.madd(sizeReg, TMP1, TMP2, sizeReg);
+ }
+ } else {
+ switch (seg.type) {
+ case am_utf8: {
+ comment("size utf8");
+ Label next = a.newLabel();
+ auto src_reg = load_source(seg.src, TMP1);
+
+ a.lsr(TMP1, src_reg.reg, imm(_TAG_IMMED1_SIZE));
+ mov_imm(TMP2, 1 * 8);
+ a.cmp(TMP1, imm(0x7F));
+ a.cond_ls().b(next);
+
+ mov_imm(TMP2, 2 * 8);
+ a.cmp(TMP1, imm(0x7FFUL));
+ a.cond_ls().b(next);
+
+ a.cmp(TMP1, imm(0x10000UL));
+ mov_imm(TMP2, 3 * 8);
+ mov_imm(TMP3, 4 * 8);
+ a.csel(TMP2, TMP2, TMP3, arm::Cond::kLO);
+
+ a.bind(next);
+ a.add(sizeReg, sizeReg, TMP2);
+ break;
+ }
+ case am_utf16: {
+ /* erts_bs_put_utf16 errors out whenever something's
+ * fishy, so we can return garbage (16 or 32) if our
+ * input is not a small. */
+ comment("size utf16");
+ auto src_reg = load_source(seg.src, TMP1);
+
+ a.asr(TMP1, src_reg.reg, imm(_TAG_IMMED1_SIZE));
+ a.cmp(TMP1, imm(0x10000UL));
+ mov_imm(TMP1, 2 * 8);
+ mov_imm(TMP2, 4 * 8);
+ a.csel(TMP1, TMP1, TMP2, arm::Cond::kLO);
+ a.add(sizeReg, sizeReg, TMP1);
+ break;
+ }
+ case am_utf32: {
+ Label next = a.newLabel();
+
+ comment("size utf32");
+ auto src_reg = load_source(seg.src, TMP1);
+
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info |
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+
+ a.add(sizeReg, sizeReg, imm(4 * 8));
+
+ a.and_(TMP2, src_reg.reg, imm(_TAG_IMMED1_MASK));
+ a.cmp(TMP2, imm(_TAG_IMMED1_SMALL));
+ a.cond_ne().b(error);
+
+ mov_imm(TMP2, make_small(0xD800UL));
+ a.cmp(src_reg.reg, TMP2);
+ a.cond_lo().b(next);
+
+ mov_imm(TMP2, make_small(0xDFFFUL));
+ a.cmp(src_reg.reg, TMP2);
+ a.cond_ls().b(error);
+
+ mov_imm(TMP2, make_small(0x10FFFFUL));
+ a.cmp(src_reg.reg, TMP2);
+ a.cond_hi().b(error);
+
+ a.bind(next);
+ break;
+ }
+ default:
+ ASSERT(0);
+ break;
+ }
+ }
+ }
+
+ /* Allocate the binary. */
+ if (segments[0].type == am_append) {
+ BscSegment seg = segments[0];
+ comment("append to binary");
+ mov_arg(ARG3, Live);
+ if (sizeReg.isValid()) {
+ a.mov(ARG4, sizeReg);
+ } else {
+ mov_imm(ARG4, num_bits);
+ }
+ mov_arg(ARG5, Alloc);
+ mov_imm(ARG6, seg.unit);
+ mov_arg(ArgVal(ArgVal::XReg, Live.getValue()), seg.src);
+ a.mov(ARG1, c_p);
+ load_x_reg_array(ARG2);
+
+ emit_enter_runtime<Update::eStack | Update::eHeap | Update::eXRegs |
+ Update::eReductions>(Live.getValue() + 1);
+ runtime_call<6>(erts_bs_append_checked);
+ emit_leave_runtime<Update::eStack | Update::eHeap | Update::eXRegs |
+ Update::eReductions>(Live.getValue() + 1);
+
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_FVALUE));
+ }
+ emit_branch_if_not_value(ARG1, resolve_label(error, dispUnknown));
+ } else if (segments[0].type == am_private_append) {
+ BscSegment seg = segments[0];
+ comment("private append to binary");
+ if (Alloc.getValue() != 0) {
+ emit_gc_test(ArgVal(ArgVal::Word, 0), Alloc, Live);
+ }
+ mov_arg(ARG2, seg.src);
+ if (sizeReg.isValid()) {
+ a.mov(ARG3, sizeReg);
+ } else {
+ mov_imm(ARG3, num_bits);
+ }
+ a.mov(ARG4, seg.unit);
+ a.mov(ARG1, c_p);
+ emit_enter_runtime(Live.getValue());
+ runtime_call<4>(erts_bs_private_append_checked);
+ emit_leave_runtime(Live.getValue());
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_FREASON,
+ BSC_INFO_FVALUE));
+ }
+ emit_branch_if_not_value(ARG1, resolve_label(error, dispUnknown));
+ } else {
+ comment("allocate binary");
+ mov_arg(ARG5, Alloc);
+ mov_arg(ARG6, Live);
+ load_erl_bits_state(ARG3);
+ load_x_reg_array(ARG2);
+ a.mov(ARG1, c_p);
+ emit_enter_runtime<Update::eReductions | Update::eStack |
+ Update::eHeap | Update::eXRegs>(Live.getValue());
+ if (sizeReg.isValid()) {
+ comment("(size in bits)");
+ a.mov(ARG4, sizeReg);
+ runtime_call<6>(beam_jit_bs_init_bits);
+ } else if (num_bits % 8 == 0) {
+ comment("(size in bytes)");
+ mov_imm(ARG4, num_bits / 8);
+ runtime_call<6>(beam_jit_bs_init);
+ } else {
+ mov_imm(ARG4, num_bits);
+ runtime_call<6>(beam_jit_bs_init_bits);
+ }
+ emit_leave_runtime<Update::eReductions | Update::eStack |
+ Update::eHeap | Update::eXRegs>(Live.getValue());
+ }
+ a.str(ARG1, TMP_MEM1q);
+
+ /* Build each segment of the binary. */
+ for (auto seg : segments) {
+ switch (seg.type) {
+ case am_append:
+ case am_private_append:
+ break;
+ case am_binary: {
+ Uint error_info;
+
+ comment("construct a binary segment");
+ emit_enter_runtime<Update::eReductions>(Live.getValue());
+ if (seg.effectiveSize >= 0) {
+ /* The segment has a literal size. */
+ mov_imm(ARG3, seg.effectiveSize);
+ mov_arg(ARG2, seg.src);
+ a.mov(ARG1, c_p);
+ runtime_call<3>(erts_new_bs_put_binary);
+ error_info =
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG, BSC_INFO_FVALUE);
+ } else if (seg.size.isImmed() && seg.size.getValue() == am_all) {
+ /* Include the entire binary/bitstring in the
+ * resulting binary. */
+ a.mov(ARG3, seg.unit);
+ mov_arg(ARG2, seg.src);
+ a.mov(ARG1, c_p);
+ runtime_call<3>(erts_new_bs_put_binary_all);
+ error_info =
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG, BSC_INFO_UNIT);
+ } else {
+ /* The size is a variable. We have verified that
+ * the value is a non-negative small in the
+ * appropriate range. Multiply the size with the
+ * unit. */
+ mov_arg(ARG3, seg.size);
+ a.asr(ARG3, ARG3, imm(_TAG_IMMED1_SIZE));
+ if (seg.unit != 1) {
+ mov_imm(TMP1, seg.unit);
+ a.mul(ARG3, ARG3, TMP1);
+ }
+ mov_arg(ARG2, seg.src);
+ a.mov(ARG1, c_p);
+ runtime_call<3>(erts_new_bs_put_binary);
+ error_info =
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG, BSC_INFO_FVALUE);
+ }
+ emit_leave_runtime<Update::eReductions>(Live.getValue());
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4, seg.error_info | error_info);
+ }
+ a.cbz(ARG1, resolve_label(error, disp1MB));
+ break;
+ }
+ case am_float:
+ comment("construct float segment");
+ if (seg.effectiveSize >= 0) {
+ mov_imm(ARG3, seg.effectiveSize);
+ } else {
+ mov_arg(ARG3, seg.size);
+ a.asr(ARG3, ARG3, imm(_TAG_IMMED1_SIZE));
+ if (seg.unit != 1) {
+ mov_imm(TMP1, seg.unit);
+ a.mul(ARG3, ARG3, TMP1);
+ }
+ }
+ mov_arg(ARG2, seg.src);
+ mov_imm(ARG4, seg.flags);
+ a.mov(ARG1, c_p);
+
+ emit_enter_runtime(Live.getValue());
+ runtime_call<4>(erts_new_bs_put_float);
+ emit_leave_runtime(Live.getValue());
+
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_FVALUE));
+ }
+ a.cbz(ARG1, resolve_label(error, disp1MB));
+ break;
+ case am_integer:
+ comment("construct integer segment");
+ if (seg.effectiveSize >= 0) {
+ mov_imm(ARG3, seg.effectiveSize);
+ } else {
+ mov_arg(ARG3, seg.size);
+ a.asr(ARG3, ARG3, imm(_TAG_IMMED1_SIZE));
+ if (seg.unit != 1) {
+ mov_imm(TMP1, seg.unit);
+ a.mul(ARG3, ARG3, TMP1);
+ }
+ }
+ mov_arg(ARG2, seg.src);
+ mov_imm(ARG4, seg.flags);
+ load_erl_bits_state(ARG1);
+
+ emit_enter_runtime(Live.getValue());
+ runtime_call<4>(erts_new_bs_put_integer);
+ emit_leave_runtime(Live.getValue());
+
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.cbz(ARG1, resolve_label(error, disp1MB));
+ break;
+ case am_string: {
+ ArgVal string_ptr = ArgVal(ArgVal::BytePtr, seg.src.getValue());
+ comment("insert string");
+ ASSERT(seg.effectiveSize >= 0);
+ mov_imm(ARG3, seg.effectiveSize / 8);
+ mov_arg(ARG2, string_ptr);
+ load_erl_bits_state(ARG1);
+
+ emit_enter_runtime(Live.getValue());
+ runtime_call<3>(erts_new_bs_put_string);
+ emit_leave_runtime(Live.getValue());
+ break;
+ }
+ case am_utf8:
+ comment("construct utf8 segment");
+ mov_arg(ARG2, seg.src);
+ load_erl_bits_state(ARG1);
+
+ emit_enter_runtime(Live.getValue());
+ runtime_call<2>(erts_bs_put_utf8);
+
+ emit_leave_runtime(Live.getValue());
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.cbz(ARG1, resolve_label(error, disp1MB));
+ break;
+ case am_utf16:
+ comment("construct utf8 segment");
+ mov_arg(ARG2, seg.src);
+ a.mov(ARG3, seg.flags);
+ load_erl_bits_state(ARG1);
+
+ emit_enter_runtime(Live.getValue());
+ runtime_call<3>(erts_bs_put_utf16);
+ emit_leave_runtime(Live.getValue());
+
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.cbz(ARG1, resolve_label(error, disp1MB));
+ break;
+ case am_utf32:
+ mov_arg(ARG2, seg.src);
+ mov_imm(ARG3, 4 * 8);
+ a.mov(ARG4, seg.flags);
+ load_erl_bits_state(ARG1);
+
+ emit_enter_runtime(Live.getValue());
+ runtime_call<4>(erts_new_bs_put_integer);
+ emit_leave_runtime(Live.getValue());
+
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.cbz(ARG1, resolve_label(error, disp1MB));
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ }
+
+ comment("done");
+ mov_arg(Dst, TMP_MEM1q);
+}
diff --git a/erts/emulator/beam/jit/arm/ops.tab b/erts/emulator/beam/jit/arm/ops.tab
index d6f0c283f6..845549f256 100644
--- a/erts/emulator/beam/jit/arm/ops.tab
+++ b/erts/emulator/beam/jit/arm/ops.tab
@@ -893,9 +893,19 @@ bs_skip_utf32 Fail=f Ms=xy Live=u Flags=u => \
i_bs_validate_unicode_retract j s S
%hot
-#
-# Constructing binaries
-#
+# ================================================================
+# New binary construction (OTP 25).
+# ================================================================
+
+bs_create_bin Fail=j Alloc=u Live=u Unit=u Dst=xy N=u Segments=* => \
+ create_bin(Fail, Alloc, Live, Unit, Dst, N, Segments)
+
+i_bs_create_bin j I t d *
+
+# ================================================================
+# Old instruction for constructing binaries (up to OTP 24).
+# ================================================================
+
%warm
bs_init2 Fail Sz Words Regs Flags Dst | binary_too_big(Sz) => system_limit Fail
diff --git a/erts/emulator/beam/jit/beam_jit_common.cpp b/erts/emulator/beam/jit/beam_jit_common.cpp
index b1a43206d8..d03ca7fcfd 100644
--- a/erts/emulator/beam/jit/beam_jit_common.cpp
+++ b/erts/emulator/beam/jit/beam_jit_common.cpp
@@ -664,11 +664,7 @@ Eterm beam_jit_bs_init_bits(Process *c_p,
Binary *bptr;
ProcBin *pb;
- test_bin_vheap(c_p,
- reg,
- num_bytes / sizeof(Eterm),
- alloc,
- Live);
+ test_bin_vheap(c_p, reg, num_bytes / sizeof(Eterm), alloc, Live);
/*
* Allocate the binary struct itself.
@@ -742,6 +738,111 @@ Eterm beam_jit_bs_get_integer(Process *c_p,
return erts_bs_get_integer_2(c_p, size, flags, mb);
}
+void beam_jit_bs_construct_fail_info(Process *c_p,
+ Uint packed_error_info,
+ Eterm bad_value) {
+ Eterm *hp = HAlloc(c_p, Sint(MAP3_SZ + 3 + 1));
+ Eterm cause_tuple;
+ Eterm error_info;
+ Uint segment = BSC_GET_SEGMENT(packed_error_info);
+ Uint op = BSC_GET_OP(packed_error_info);
+ Uint info = BSC_GET_INFO(packed_error_info);
+ Uint reason = BSC_GET_REASON(packed_error_info);
+ Eterm Op = am_none;
+ Eterm Info = am_none;
+
+ switch (op) {
+ case BSC_OP_BINARY:
+ Op = am_binary;
+ break;
+ case BSC_OP_FLOAT:
+ Op = am_float;
+ break;
+ case BSC_OP_INTEGER:
+ Op = am_integer;
+ break;
+ case BSC_OP_UTF8:
+ Op = am_utf8;
+ break;
+ case BSC_OP_UTF16:
+ Op = am_utf16;
+ break;
+ case BSC_OP_UTF32:
+ Op = am_utf32;
+ break;
+ }
+
+ switch (reason) {
+ case BSC_REASON_FREASON:
+ reason = c_p->freason;
+ break;
+ case BSC_REASON_BADARG:
+ reason = BADARG;
+ break;
+ case BSC_REASON_SYSTEM_LIMIT:
+ reason = SYSTEM_LIMIT;
+ break;
+ case BSC_REASON_DEPENDS:
+ if ((is_small(bad_value) && signed_val(bad_value) >= 0) ||
+ (is_big(bad_value) && !big_sign(bad_value))) {
+ reason = SYSTEM_LIMIT;
+ } else {
+ reason = BADARG;
+ }
+ break;
+ }
+
+ switch (info) {
+ case BSC_INFO_FVALUE:
+ Info = c_p->fvalue;
+ break;
+ case BSC_INFO_TYPE:
+ Info = am_type;
+ break;
+ case BSC_INFO_SIZE:
+ Info = am_size;
+ break;
+ case BSC_INFO_NEGATIVE:
+ Info = am_negative_size;
+ break;
+ case BSC_INFO_UNIT:
+ Info = am_unit;
+ break;
+ case BSC_INFO_DEPENDS:
+ if (reason == SYSTEM_LIMIT) {
+ Info = am_size;
+ } else if (is_small(bad_value) || is_big(bad_value)) {
+ Info = am_negative_size;
+ } else {
+ Info = am_size;
+ }
+ break;
+ }
+
+ cause_tuple = TUPLE3(hp, make_small(segment), Op, Info);
+ hp += 4;
+ error_info = MAP3(hp,
+ am_cause,
+ cause_tuple,
+ am_function,
+ am_format_bs_fail,
+ am_module,
+ am_erl_erts_errors);
+ c_p->fvalue = error_info;
+ c_p->freason = reason | EXF_HAS_EXT_INFO;
+}
+
+Sint beam_jit_bs_bit_size(Eterm term) {
+ if (is_binary(term)) {
+ ASSERT(sizeof(Uint) == 8); /* Only support 64-bit machines. */
+ Uint byte_size = binary_size(term);
+ return (Sint)((byte_size << 3) + binary_bitsize(term));
+ }
+
+ /* Signal error */
+ return (Sint)-1;
+}
+
ErtsMessage *beam_jit_decode_dist(Process *c_p, ErtsMessage *msgp) {
if (!erts_proc_sig_decode_dist(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) {
/*
diff --git a/erts/emulator/beam/jit/beam_jit_common.hpp b/erts/emulator/beam/jit/beam_jit_common.hpp
index 8db8c95eac..7d32f71dcd 100644
--- a/erts/emulator/beam/jit/beam_jit_common.hpp
+++ b/erts/emulator/beam/jit/beam_jit_common.hpp
@@ -235,6 +235,33 @@ public:
}
};
+static const int BSC_OP_BINARY = 0;
+static const int BSC_OP_FLOAT = 1;
+static const int BSC_OP_INTEGER = 2;
+static const int BSC_OP_UTF8 = 3;
+static const int BSC_OP_UTF16 = 4;
+static const int BSC_OP_UTF32 = 5;
+
+static const int BSC_INFO_FVALUE = 0;
+static const int BSC_INFO_TYPE = 1;
+static const int BSC_INFO_SIZE = 2;
+static const int BSC_INFO_NEGATIVE = 3;
+static const int BSC_INFO_UNIT = 4;
+static const int BSC_INFO_DEPENDS = 5;
+
+static const int BSC_REASON_FREASON = 0;
+static const int BSC_REASON_BADARG = 1;
+static const int BSC_REASON_SYSTEM_LIMIT = 2;
+static const int BSC_REASON_DEPENDS = 3;
+
+#define BSC_SET_SEGMENT_OP(Seg, Op) (((Seg) << 8) | ((Op) << 5))
+#define BSC_SET_REASON_INFO(Reason, Info) (((Info) << 2) | Reason)
+
+#define BSC_GET_SEGMENT(PackedInfo) ((PackedInfo) >> 8)
+#define BSC_GET_OP(PackedInfo) (((PackedInfo) >> 5) & 0x007)
+#define BSC_GET_INFO(PackedInfo) (((PackedInfo) >> 2) & 0x07);
+#define BSC_GET_REASON(PackedInfo) ((PackedInfo)&0x03);
+
/* ** */
#if defined(DEBUG) && defined(JIT_HARD_DEBUG)
@@ -292,6 +319,11 @@ Sint beam_jit_remove_message(Process *c_p,
Eterm *E,
Uint32 active_code_ix);
+void beam_jit_bs_construct_fail_info(Process *c_p,
+ Uint packed_error_info,
+ Eterm value);
+Sint beam_jit_bs_bit_size(Eterm term);
+
void beam_jit_take_receive_lock(Process *c_p);
void beam_jit_wait_locked(Process *c_p, ErtsCodePtr cp);
void beam_jit_wait_unlocked(Process *c_p, ErtsCodePtr cp);
diff --git a/erts/emulator/beam/jit/x86/beam_asm.hpp b/erts/emulator/beam/jit/x86/beam_asm.hpp
index 5e83c7e83c..44a41ca6d5 100644
--- a/erts/emulator/beam/jit/x86/beam_asm.hpp
+++ b/erts/emulator/beam/jit/x86/beam_asm.hpp
@@ -874,6 +874,7 @@ class BeamGlobalAssembler : public BeamAssembler {
_(bif_element_shared) \
_(bif_export_trap) \
_(bs_add_shared) \
+ _(bs_create_bin_error_shared) \
_(bs_size_check_shared) \
_(bs_fixed_integer_shared) \
_(bs_get_tail_shared) \
diff --git a/erts/emulator/beam/jit/x86/generators.tab b/erts/emulator/beam/jit/x86/generators.tab
index 8763e34a47..85384ad5bc 100644
--- a/erts/emulator/beam/jit/x86/generators.tab
+++ b/erts/emulator/beam/jit/x86/generators.tab
@@ -563,3 +563,59 @@ gen.func_end(Func_Label, Entry_Label) {
return op;
}
+
+gen.create_bin(Fail, Alloc, Live, Unit, Dst, N, Segments) {
+ BeamOp* op;
+ int fixed_args;
+ int i;
+
+ $NewBeamOp(S, op);
+ $BeamOpNameArity(op, i_bs_create_bin, 4);
+ fixed_args = op->arity;
+ $BeamOpArity(op, (N.val + fixed_args));
+
+ op->a[0] = Fail;
+ op->a[1] = Alloc;
+ op->a[2] = Live;
+ op->a[3] = Dst;
+
+ for (i = 0; i < N.val; i += 6) {
+ BeamOpArg Flags;
+ Uint flags = 0;
+
+ /* Copy all but flags. */
+ op->a[i+fixed_args+0] = Segments[i+0];
+ op->a[i+fixed_args+1] = Segments[i+1];
+ op->a[i+fixed_args+2] = Segments[i+2];
+ op->a[i+fixed_args+4] = Segments[i+4];
+ op->a[i+fixed_args+5] = Segments[i+5];
+
+ /* Translate flags. */
+ Flags = Segments[i+3]; /* Flags */
+ if (Flags.type != TAG_n) {
+ if (Flags.type == TAG_q) {
+ Eterm term = beamfile_get_literal(&S->beam, Flags.val);
+ while (is_list(term)) {
+ Eterm* consp = list_val(term);
+ Eterm elem = CAR(consp);
+ switch (elem) {
+ case am_little:
+ flags |= BSF_LITTLE;
+ break;
+ case am_native:
+ flags |= BSF_NATIVE;
+ break;
+ }
+ term = CDR(consp);
+ }
+ ASSERT(is_nil(term));
+ }
+ }
+ Flags.type = TAG_u;
+ Flags.val = flags;
+ $NativeEndian(Flags);
+ op->a[i+fixed_args+3] = Flags;
+ }
+
+ return op;
+}
diff --git a/erts/emulator/beam/jit/x86/instr_bs.cpp b/erts/emulator/beam/jit/x86/instr_bs.cpp
index 2d68db3aae..845e92fd2d 100644
--- a/erts/emulator/beam/jit/x86/instr_bs.cpp
+++ b/erts/emulator/beam/jit/x86/instr_bs.cpp
@@ -1559,3 +1559,537 @@ void BeamModuleAssembler::emit_bs_init_writable() {
emit_leave_runtime<Update::eReductions | Update::eStack | Update::eHeap>();
}
+
+void BeamGlobalAssembler::emit_bs_create_bin_error_shared() {
+ emit_enter_runtime<Update::eStack | Update::eHeap>();
+
+ a.mov(ARG3, ARG1);
+ a.mov(ARG2, ARG4);
+ a.mov(ARG1, c_p);
+ runtime_call<3>(beam_jit_bs_construct_fail_info);
+
+ emit_leave_runtime<Update::eStack | Update::eHeap>();
+
+ /* We must align the return address to make it a proper tagged CP, in case
+ * we were called with `safe_fragment_call`. This is safe because we will
+ * never actually return to the return address. */
+ a.pop(ARG2);
+ a.and_(ARG2, imm(-8));
+
+#ifdef NATIVE_ERLANG_STACK
+ a.push(ARG2);
+
+ if (erts_frame_layout == ERTS_FRAME_LAYOUT_FP_RA) {
+# ifdef ERLANG_FRAME_POINTERS
+ a.push(frame_pointer);
+# endif
+ } else {
+ ASSERT(erts_frame_layout == ERTS_FRAME_LAYOUT_RA);
+ }
+#endif
+
+ mov_imm(ARG4, nullptr);
+ a.jmp(labels[raise_exception_shared]);
+}
+
+struct BscSegment {
+ BscSegment()
+ : src(ArgVal(ArgVal::Immediate, NIL)),
+ size(ArgVal(ArgVal::Immediate, NIL)), effectiveSize(-1){};
+
+ Eterm type;
+ Uint unit;
+ Uint flags;
+ ArgVal src;
+ ArgVal size;
+
+ Uint error_info;
+ Sint effectiveSize;
+};
+
+void BeamModuleAssembler::emit_i_bs_create_bin(const ArgVal &Fail,
+ const ArgVal &Alloc,
+ const ArgVal &Live0,
+ const ArgVal &Dst,
+ const Span<ArgVal> &args) {
+ Uint num_bits = 0;
+ std::size_t n = args.size();
+ std::vector<BscSegment> segments;
+ Label error = a.newLabel();
+ Label past_error = a.newLabel();
+ ArgVal Live = Live0;
+ x86::Gp sizeReg;
+
+ /*
+ * Collect information about each segment and calculate sizes of
+ * fixed segments.
+ */
+ for (std::size_t i = 0; i < n; i += 6) {
+ BscSegment seg;
+ Uint bsc_op;
+ Uint bsc_segment;
+
+ ASSERT(args[i].isImmed());
+ ASSERT(args[i + 1].getType() == TAG_u);
+ ASSERT(args[i + 2].getType() == TAG_u);
+ ASSERT(args[i + 3].getType() == TAG_u);
+ seg.type = args[i].getValue();
+ bsc_segment = args[i + 1].getValue();
+ seg.unit = args[i + 2].getValue();
+ seg.flags = args[i + 3].getValue();
+ seg.src = args[i + 4];
+ seg.size = args[i + 5];
+
+ switch (seg.type) {
+ case am_float:
+ bsc_op = BSC_OP_FLOAT;
+ break;
+ case am_integer:
+ bsc_op = BSC_OP_INTEGER;
+ break;
+ case am_utf8:
+ bsc_op = BSC_OP_UTF8;
+ break;
+ case am_utf16:
+ bsc_op = BSC_OP_UTF16;
+ break;
+ case am_utf32:
+ bsc_op = BSC_OP_UTF32;
+ break;
+ default:
+ bsc_op = BSC_OP_BINARY;
+ break;
+ }
+
+ /*
+ * Save segment number and operation for use in extended
+ * error information.
+ */
+ seg.error_info = BSC_SET_SEGMENT_OP(bsc_segment, bsc_op);
+
+ /*
+ * As soon as we have entered runtime mode, Y registers can no
+ * longer be accessed in the usual way. Therefore, if the source
+ * and/or size are in Y register, copy them to X registers.
+ */
+ if (seg.src.getType() == TAG_y) {
+ ArgVal reg = ArgVal(TAG_x, Live.getValue());
+ mov_arg(reg, seg.src);
+ Live.val = Live.getValue() + 1;
+ seg.src = reg;
+ }
+
+ if (seg.size.getType() == TAG_y) {
+ ArgVal reg = ArgVal(TAG_x, Live.getValue());
+ mov_arg(reg, seg.size);
+ Live.val = Live.getValue() + 1;
+ seg.size = reg;
+ }
+
+ if (seg.size.isImmed() && seg.unit != 0) {
+ Eterm size = seg.size.getValue();
+ if (is_small(size)) {
+ Uint unsigned_size = unsigned_val(size);
+ if ((unsigned_size >> (sizeof(Eterm) - 1) * 8) == 0) {
+ /* This multiplication cannot overflow. */
+ Uint seg_size = seg.unit * unsigned_size;
+ seg.effectiveSize = seg_size;
+ num_bits += seg_size;
+ }
+ }
+ }
+
+ if (seg.effectiveSize < 0 && seg.type != am_append &&
+ seg.type != am_private_append) {
+ sizeReg = FCALLS;
+ }
+
+ segments.insert(segments.end(), seg);
+ }
+
+ emit_enter_runtime<Update::eReductions | Update::eStack | Update::eHeap>();
+
+ a.short_().jmp(past_error);
+ a.bind(error);
+ {
+ /*
+ * ARG4 = packed error information
+ * ARG1 = optional bad size value; only valid if BSC_REASON_DEPENDS is
+ * set in ARG4
+ */
+ comment("handle error");
+ emit_leave_runtime<Update::eReductions | Update::eStack |
+ Update::eHeap>();
+ if (Fail.getValue() != 0) {
+ a.jmp(labels[Fail.getValue()]);
+ } else {
+ safe_fragment_call(ga->get_bs_create_bin_error_shared());
+ }
+ }
+
+ a.bind(past_error);
+
+ /* We count the total number of bits in an unsigned integer. To
+ * avoid having to check for overflow when adding to the counter,
+ * we ensure that the signed size of each segment fits in a
+ * word. */
+ if (sizeReg.isValid()) {
+ comment("calculate sizes");
+ mov_imm(sizeReg, num_bits);
+ }
+
+ /* Generate code for calculating the size of the binary to be
+ * created. */
+ for (auto seg : segments) {
+ if (seg.effectiveSize >= 0) {
+ continue;
+ }
+
+ if (seg.type == am_append || seg.type == am_private_append) {
+ continue;
+ }
+
+ if (seg.size.isImmed() && seg.size.getValue() == am_all &&
+ seg.type == am_binary) {
+ comment("size of an entire binary");
+ mov_arg(ARG1, seg.src);
+ runtime_call<1>(beam_jit_bs_bit_size);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.test(RET, RET);
+ a.js(error);
+ a.add(sizeReg, RET);
+ } else if (seg.unit != 0) {
+ comment("size binary/integer/float/string");
+ mov_arg(ARG1, seg.size);
+ a.mov(RETd, ARG1d);
+ a.and_(RETb, imm(_TAG_IMMED1_MASK));
+ a.cmp(RETb, imm(_TAG_IMMED1_SMALL));
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_DEPENDS,
+ BSC_INFO_DEPENDS));
+ }
+ a.jne(error);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info |
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_NEGATIVE));
+ }
+ a.sar(ARG1, imm(_TAG_IMMED1_SIZE));
+ a.js(error);
+ if (seg.unit == 1) {
+ a.mov(RET, ARG1);
+ } else {
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info |
+ BSC_SET_REASON_INFO(BSC_REASON_SYSTEM_LIMIT,
+ BSC_INFO_SIZE));
+ }
+ a.imul(RET, ARG1, imm(seg.unit));
+ a.jo(error);
+ }
+ a.add(sizeReg, RET);
+ } else {
+ switch (seg.type) {
+ case am_utf8: {
+ Label next = a.newLabel();
+
+ comment("size utf8");
+ mov_arg(ARG1, seg.src);
+
+ mov_imm(RET, 0);
+ a.mov(RETb, imm(1 * 8));
+ a.cmp(ARG1, imm(make_small(0x80UL)));
+ a.short_().jl(next);
+
+ a.mov(RETb, imm(2 * 8));
+ a.cmp(ARG1, imm(make_small(0x800UL)));
+ a.short_().jl(next);
+
+ a.mov(RETb, imm(3 * 8));
+ a.cmp(ARG1, imm(make_small(0x10000UL)));
+ a.short_().jl(next);
+
+ a.mov(RETb, imm(4 * 8));
+
+ a.bind(next);
+ a.add(sizeReg, RET);
+ break;
+ }
+ case am_utf16: {
+ mov_arg(ARG1, seg.src);
+ mov_imm(RET, 2 * 8);
+ mov_imm(ARG2, 4 * 8);
+ a.cmp(ARG1, imm(make_small(0x10000UL)));
+ a.cmovae(RET, ARG2);
+ a.add(sizeReg, RET);
+ break;
+ }
+ case am_utf32: {
+ Label next = a.newLabel();
+
+ mov_arg(ARG1, seg.src);
+
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info |
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+
+ a.add(sizeReg, imm(4 * 8));
+
+ a.mov(RETd, ARG1d);
+ a.and_(RETb, imm(_TAG_IMMED1_MASK));
+ a.cmp(RETb, imm(_TAG_IMMED1_SMALL));
+ a.jne(error);
+
+ a.cmp(ARG1, imm(make_small(0xD800UL)));
+ a.short_().jb(next);
+ a.cmp(ARG1, imm(make_small(0xDFFFUL)));
+ a.jbe(error);
+ a.cmp(ARG1, imm(make_small(0x10FFFFUL)));
+ a.ja(error);
+
+ a.bind(next);
+ break;
+ }
+ default:
+ ASSERT(0);
+ }
+ }
+ }
+
+ /* Allocate the binary. */
+ if (segments[0].type == am_append) {
+ BscSegment seg = segments[0];
+ comment("append to binary");
+ mov_arg(ARG3, Live);
+ if (sizeReg.isValid()) {
+ a.mov(ARG4, sizeReg);
+ } else {
+ mov_imm(ARG4, num_bits);
+ }
+ mov_arg(ARG5, Alloc);
+ mov_imm(ARG6, seg.unit);
+ mov_arg(ArgVal(ArgVal::XReg, Live.getValue()), seg.src);
+ a.mov(ARG1, c_p);
+ load_x_reg_array(ARG2);
+ runtime_call<6>(erts_bs_append_checked);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_FVALUE));
+ }
+ emit_test_the_non_value(RET);
+ a.je(error);
+ } else if (segments[0].type == am_private_append) {
+ BscSegment seg = segments[0];
+ comment("private append to binary");
+ if (Alloc.getValue() != 0) {
+ emit_gc_test(ArgVal(ArgVal::Word, 0), Alloc, Live);
+ }
+ mov_arg(ARG2, seg.src);
+ if (sizeReg.isValid()) {
+ a.mov(ARG3, sizeReg);
+ } else {
+ mov_imm(ARG3, num_bits);
+ }
+ a.mov(ARG4, seg.unit);
+ a.mov(ARG1, c_p);
+ runtime_call<4>(erts_bs_private_append_checked);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_FREASON,
+ BSC_INFO_FVALUE));
+ }
+ emit_test_the_non_value(RET);
+ a.je(error);
+ } else {
+ comment("allocate binary");
+ mov_arg(ARG5, Alloc);
+ mov_arg(ARG6, Live);
+ load_erl_bits_state(ARG3);
+ load_x_reg_array(ARG2);
+ a.mov(ARG1, c_p);
+ if (sizeReg.isValid()) {
+ comment("(size in bits)");
+ a.mov(ARG4, sizeReg);
+ runtime_call<6>(beam_jit_bs_init_bits);
+ } else if (num_bits % 8 == 0) {
+ comment("(size in bytes)");
+ mov_imm(ARG4, num_bits / 8);
+ runtime_call<6>(beam_jit_bs_init);
+ } else {
+ mov_imm(ARG4, num_bits);
+ runtime_call<6>(beam_jit_bs_init_bits);
+ }
+ }
+ a.mov(TMP_MEM1q, RET);
+
+ /* Build each segment of the binary. */
+ for (auto seg : segments) {
+ switch (seg.type) {
+ case am_append:
+ case am_private_append:
+ break;
+ case am_binary: {
+ Uint error_info;
+
+ comment("construct a binary segment");
+ if (seg.effectiveSize >= 0) {
+ /* The segment has a literal size. */
+ mov_imm(ARG3, seg.effectiveSize);
+ mov_arg(ARG2, seg.src);
+ a.mov(ARG1, c_p);
+ runtime_call<3>(erts_new_bs_put_binary);
+ error_info =
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG, BSC_INFO_FVALUE);
+ } else if (seg.size.isImmed() && seg.size.getValue() == am_all) {
+ /* Include the entire binary/bitstring in the
+ * resulting binary. */
+ a.mov(ARG3, seg.unit);
+ mov_arg(ARG2, seg.src);
+ a.mov(ARG1, c_p);
+ runtime_call<3>(erts_new_bs_put_binary_all);
+ error_info =
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG, BSC_INFO_UNIT);
+ } else {
+ /* The size is a variable. We have verified that
+ * the value is a non-negative small in the
+ * appropriate range. Multiply the size with the
+ * unit. */
+ mov_arg(ARG3, seg.size);
+ a.sar(ARG3, imm(_TAG_IMMED1_SIZE));
+ if (seg.unit != 1) {
+ mov_imm(RET, seg.unit);
+ a.mul(ARG3); /* CLOBBERS RDX = ARG3! */
+ a.mov(ARG3, RET);
+ }
+ mov_arg(ARG2, seg.src);
+ a.mov(ARG1, c_p);
+ runtime_call<3>(erts_new_bs_put_binary);
+ error_info =
+ BSC_SET_REASON_INFO(BSC_REASON_BADARG, BSC_INFO_FVALUE);
+ }
+ a.test(RET, RET);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4, seg.error_info | error_info);
+ }
+ a.je(error);
+ break;
+ }
+ case am_float:
+ comment("construct float segment");
+ if (seg.effectiveSize >= 0) {
+ mov_imm(ARG3, seg.effectiveSize);
+ } else {
+ mov_arg(ARG3, seg.size);
+ a.sar(ARG3, imm(_TAG_IMMED1_SIZE));
+ if (seg.unit != 1) {
+ mov_imm(RET, seg.unit);
+ a.mul(ARG3); /* CLOBBERS RDX = ARG3! */
+ a.mov(ARG3, RET);
+ }
+ }
+ mov_arg(ARG2, seg.src);
+ mov_imm(ARG4, seg.flags);
+ a.mov(ARG1, c_p);
+ runtime_call<4>(erts_new_bs_put_float);
+ a.test(RET, RET);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_FVALUE));
+ }
+ a.je(error);
+ break;
+ case am_integer:
+ comment("construct integer segment");
+ if (seg.effectiveSize >= 0) {
+ mov_imm(ARG3, seg.effectiveSize);
+ } else {
+ mov_arg(ARG3, seg.size);
+ a.sar(ARG3, imm(_TAG_IMMED1_SIZE));
+ if (seg.unit != 1) {
+ mov_imm(RET, seg.unit);
+ a.mul(ARG3); /* CLOBBERS RDX = ARG3! */
+ a.mov(ARG3, RET);
+ }
+ }
+ mov_arg(ARG2, seg.src);
+ mov_imm(ARG4, seg.flags);
+ load_erl_bits_state(ARG1);
+ runtime_call<4>(erts_new_bs_put_integer);
+ a.test(RET, RET);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.je(error);
+ break;
+ case am_string:
+ comment("insert string");
+ ASSERT(seg.effectiveSize >= 0);
+ mov_imm(ARG3, seg.effectiveSize / 8);
+ make_move_patch(ARG2, strings, seg.src.getValue());
+ load_erl_bits_state(ARG1);
+ runtime_call<3>(erts_new_bs_put_string);
+ break;
+ case am_utf8:
+ mov_arg(ARG2, seg.src);
+ load_erl_bits_state(ARG1);
+ runtime_call<2>(erts_bs_put_utf8);
+ a.test(RET, RET);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.je(error);
+ break;
+ case am_utf16:
+ mov_arg(ARG2, seg.src);
+ a.mov(ARG3, seg.flags);
+ load_erl_bits_state(ARG1);
+ runtime_call<3>(erts_bs_put_utf16);
+ a.test(RET, RET);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.je(error);
+ break;
+ case am_utf32:
+ mov_arg(ARG2, seg.src);
+ mov_imm(ARG3, 4 * 8);
+ a.mov(ARG4, seg.flags);
+ load_erl_bits_state(ARG1);
+ runtime_call<4>(erts_new_bs_put_integer);
+ a.test(RET, RET);
+ if (Fail.getValue() == 0) {
+ mov_imm(ARG4,
+ seg.error_info | BSC_SET_REASON_INFO(BSC_REASON_BADARG,
+ BSC_INFO_TYPE));
+ }
+ a.je(error);
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ }
+
+ comment("done");
+ emit_leave_runtime<Update::eReductions | Update::eStack | Update::eHeap>();
+ a.mov(RET, TMP_MEM1q);
+ mov_arg(Dst, RET);
+}
diff --git a/erts/emulator/beam/jit/x86/ops.tab b/erts/emulator/beam/jit/x86/ops.tab
index 8109821a1b..00f56856ab 100644
--- a/erts/emulator/beam/jit/x86/ops.tab
+++ b/erts/emulator/beam/jit/x86/ops.tab
@@ -878,9 +878,19 @@ bs_skip_utf32 Fail=f Ms=xy Live=u Flags=u => \
i_bs_validate_unicode_retract j s S
%hot
-#
-# Constructing binaries
-#
+# ================================================================
+# New binary construction (OTP 25).
+# ================================================================
+
+bs_create_bin Fail=j Alloc=u Live=u Unit=u Dst=xy N=u Segments=* => \
+ create_bin(Fail, Alloc, Live, Unit, Dst, N, Segments)
+
+i_bs_create_bin j I t d *
+
+# ================================================================
+# Old instruction for constructing binaries (up to OTP 24).
+# ================================================================
+
%warm
bs_init2 Fail Sz Words Regs Flags Dst | binary_too_big(Sz) => system_limit Fail
diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index 81107c5644..5f3cea1bcb 100644
--- a/erts/emulator/test/bs_construct_SUITE.erl
+++ b/erts/emulator/test/bs_construct_SUITE.erl
@@ -28,7 +28,7 @@
huge_float_field/1, system_limit/1, badarg/1,
copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1,
otp_7422/1, zero_width/1, bad_append/1, bs_append_overflow/1,
- reductions/1, fp16/1]).
+ reductions/1, fp16/1, error_info/1]).
-include_lib("common_test/include/ct.hrl").
@@ -41,7 +41,7 @@ all() ->
in_guard, mem_leak, coerce_to_float, bjorn, append_empty_is_same,
huge_float_field, system_limit, badarg,
copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width,
- bad_append, bs_append_overflow, reductions, fp16].
+ bad_append, bs_append_overflow, reductions, fp16, error_info].
init_per_suite(Config) ->
Config.
@@ -935,9 +935,6 @@ reds_at_least(N, Fun) ->
Diff ->
ct:fail({expected,N,got,Diff})
end.
-
-id(I) -> I.
-
memsize() ->
application:ensure_all_started(os_mon),
{Tot,_Used,_} = memsup:get_memory_data(),
@@ -984,3 +981,111 @@ fp16(_Config) ->
?FP16(16#4000, 2),
?FP16(16#4000, 2.0),
ok.
+
+-define(ERROR_INFO(Expr),
+ fun() ->
+ try Expr of
+ _ ->
+ error(should_fail)
+ catch
+ error:Reason:Stk ->
+ error_info_verify(Reason, Stk, ??Expr)
+ end
+ end()).
+
+error_info(_Config) ->
+ Atom = id(atom),
+ NegativeSize = id(-1),
+ HugeNegativeSize = id(-1 bsl 64),
+ Binary = id(<<"abc">>),
+ HugeBig = id(1 bsl 1500),
+
+ {badarg, {1,binary,type}} = ?ERROR_INFO(<<Atom/binary, Binary/binary>>),
+ {badarg, {2,binary,type}} = ?ERROR_INFO(<<Binary/binary, Atom/binary>>),
+ {badarg, {3,binary,type}} = ?ERROR_INFO(<<1:32, Binary/binary, Atom/binary>>),
+ {badarg, {4,binary,type}} = ?ERROR_INFO(<<1:32, "xyz", Binary/binary, Atom/binary>>),
+
+ {badarg, {1,integer,type}} = ?ERROR_INFO(<<Atom:32>>),
+ {badarg, {1,integer,size}} = ?ERROR_INFO(<<42:Atom>>),
+ {badarg, {1,integer,negative_size}} = ?ERROR_INFO(<<42:NegativeSize>>),
+ {badarg, {1,integer,negative_size}} = ?ERROR_INFO(<<42:HugeNegativeSize>>),
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<42:(1 bsl 58)/unit:255>>),
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<42:(1 bsl 60)/unit:8>>),
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<42:(1 bsl 64)>>),
+
+ {badarg, {1,binary,type}} = ?ERROR_INFO(<<Atom:10/binary>>),
+ {badarg, {1,binary,size}} = ?ERROR_INFO(<<Binary:Atom/binary>>),
+ {badarg, {1,binary,negative_size}} = ?ERROR_INFO(<<Binary:NegativeSize/binary>>),
+ {badarg, {1,binary,negative_size}} = ?ERROR_INFO(<<Binary:HugeNegativeSize/binary>>),
+ {system_limit, {1,binary,size}} = ?ERROR_INFO(<<Binary:(1 bsl 64)/binary>>),
+ {badarg, {1,binary,short}} = ?ERROR_INFO(<<Binary:10/binary>>),
+ {badarg, {1,binary,type}} = ?ERROR_INFO(<<Atom/binary>>),
+
+ {badarg, {1,float,type}} = ?ERROR_INFO(<<Atom:64/float>>),
+ {badarg, {1,float,size}} = ?ERROR_INFO(<<Atom:Atom/float>>),
+ {badarg, {1,float,negative_size}} = ?ERROR_INFO(<<42.0:NegativeSize/float>>),
+ {badarg, {1,float,negative_size}} = ?ERROR_INFO(<<42.0:HugeNegativeSize/float>>),
+ {badarg, {1,float,invalid}} = ?ERROR_INFO(<<42.0:(id(1))/float>>),
+ {badarg, {1,float,no_float}} = ?ERROR_INFO(<<HugeBig:(id(64))/float>>),
+ {badarg, {1,float,no_float}} = ?ERROR_INFO(<<HugeBig:64/float>>),
+ {system_limit, {1,float,size}} = ?ERROR_INFO(<<42.0:(id(1 bsl 64))/float>>),
+
+ {badarg, {1,utf8,type}} = ?ERROR_INFO(<<Atom/utf8>>),
+ {badarg, {1,utf16,type}} = ?ERROR_INFO(<<Atom/utf16>>),
+ {badarg, {1,utf32,type}} = ?ERROR_INFO(<<Atom/utf32>>),
+
+ Bin = id(<<>>),
+ Float = id(42.0),
+
+ %% Attempt constructing a binary with total size 1^64 + 32.
+
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<0:((1 bsl 59)-1)/unit:32,0:64>>),
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<0:((1 bsl 59)-1)/unit:32,(id(0)):64>>),
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<0:(id((1 bsl 59)-1))/unit:32,0:64>>),
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<0:(id((1 bsl 59)-1))/unit:32,(id(0)):64>>),
+
+ {system_limit, {1,binary,size}} = ?ERROR_INFO(<<Bin:((1 bsl 59)-1)/binary-unit:32,0:64>>),
+ {system_limit, {1,binary,size}} = ?ERROR_INFO(<<Bin:((1 bsl 59)-1)/binary-unit:32,(id(0)):64>>),
+ {system_limit, {1,binary,size}} = ?ERROR_INFO(<<Bin:(id((1 bsl 59)-1))/binary-unit:32,0:64>>),
+ {system_limit, {1,binary,size}} = ?ERROR_INFO(<<Bin:(id((1 bsl 59)-1))/binary-unit:32,(id(0)):64>>),
+
+ {system_limit, {1,float,size}} = ?ERROR_INFO(<<Float:((1 bsl 59)-1)/float-unit:32,0:64>>),
+ {system_limit, {1,float,size}} = ?ERROR_INFO(<<Float:((1 bsl 59)-1)/float-unit:32,(id(0)):64>>),
+ {system_limit, {1,float,size}} = ?ERROR_INFO(<<Float:(id((1 bsl 59)-1))/float-unit:32,0:64>>),
+ {system_limit, {1,float,size}} = ?ERROR_INFO(<<Float:(id((1 bsl 59)-1))/float-unit:32,(id(0)):64>>),
+
+ %% Test a size exceeding 1^64, where the sign bit (bit 63) is not set.
+ 0 = ((((1 bsl 59)-1) * 33) bsr 63) band 1, %Assertion: The sign bit is not set.
+
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<0:((1 bsl 59)-1)/unit:33>>),
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<0:((1 bsl 59)-1)/unit:33>>),
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<0:(id((1 bsl 59)-1))/unit:33>>),
+ {system_limit, {1,integer,size}} = ?ERROR_INFO(<<0:(id((1 bsl 59)-1))/unit:33>>),
+
+ {system_limit, {1,binary,size}} = ?ERROR_INFO(<<Bin:((1 bsl 59)-1)/binary-unit:33>>),
+ {system_limit, {1,binary,size}} = ?ERROR_INFO(<<Bin:((1 bsl 59)-1)/binary-unit:33>>),
+ {system_limit, {1,binary,size}} = ?ERROR_INFO(<<Bin:(id((1 bsl 59)-1))/binary-unit:33>>),
+ {system_limit, {1,binary,size}} = ?ERROR_INFO(<<Bin:(id((1 bsl 59)-1))/binary-unit:33>>),
+
+ {system_limit, {1,float,size}} = ?ERROR_INFO(<<Float:((1 bsl 59)-1)/float-unit:33>>),
+ {system_limit, {1,float,size}} = ?ERROR_INFO(<<Float:((1 bsl 59)-1)/float-unit:33>>),
+ {system_limit, {1,float,size}} = ?ERROR_INFO(<<Float:(id((1 bsl 59)-1))/float-unit:33>>),
+ {system_limit, {1,float,size}} = ?ERROR_INFO(<<Float:(id((1 bsl 59)-1))/float-unit:33>>),
+
+ ok.
+
+error_info_verify(Reason, Stk, Expr) ->
+ [{?MODULE, _, _, Info}|_] = Stk,
+ {error_info, ErrorInfo} = lists:keyfind(error_info, 1, Info),
+ #{cause := Cause, module := Module, function := Function} = ErrorInfo,
+ Result = Module:Function(Reason, Stk),
+ #{general := String} = Result,
+ true = is_binary(String),
+ io:format("~ts: ~ts\n", [Expr,String]),
+ {Reason, Cause}.
+
+%%%
+%%% Common utilities.
+%%%
+
+id(I) -> I.
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index 6744ee895b..89f34b0a47 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -1485,7 +1485,7 @@ line_numbers(Config) when is_list(Config) ->
<<0,0>> = build_binary1(16),
{'EXIT',{badarg,
[{?MODULE,build_binary1,1,
- [{file,"bit_syntax.erl"},{line,72503}]},
+ [{file,"bit_syntax.erl"},{line,72503},{error_info,_}]},
{?MODULE,line_numbers,1,
[{file,ModFile},{line,_}]}|_]}} =
(catch build_binary1(bad_size)),
@@ -1493,21 +1493,20 @@ line_numbers(Config) when is_list(Config) ->
<<7,1,2,3>> = build_binary2(8, <<1,2,3>>),
{'EXIT',{badarg,
[{?MODULE,build_binary2,2,
- [{file,"bit_syntax.erl"},{line,72507}]},
+ [{file,"bit_syntax.erl"},{line,72507},{error_info,_}]},
{?MODULE,line_numbers,1,
[{file,ModFile},{line,_}]}|_]}} =
(catch build_binary2(bad_size, <<>>)),
{'EXIT',{badarg,
- [{erlang,bit_size,[bad_binary],[{error_info,_}]},
- {?MODULE,build_binary2,2,
- [{file,"bit_syntax.erl"},{line,72507}]},
+ [{?MODULE,build_binary2,2,
+ [{file,"bit_syntax.erl"},{line,72507},{error_info,_}]},
{?MODULE,line_numbers,1,
[{file,ModFile},{line,_}]}|_]}} =
(catch build_binary2(8, bad_binary)),
<<"abc",357:16>> = build_binary3(<<"abc">>),
{'EXIT',{badarg,[{?MODULE,build_binary3,1,
- [{file,"bit_syntax.erl"},{line,72511}]},
+ [{file,"bit_syntax.erl"},{line,72511},{error_info,_}]},
{?MODULE,line_numbers,1,
[{file,ModFile},{line,_}]}|_]}} =
(catch build_binary3(no_binary)),
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 7d2992044d..3f98aba6aa 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -1042,6 +1042,7 @@ sub emulator_output {
print '#include "erl_term.h"', "\n";
print '#include "erl_map.h"', "\n";
print '#include "big.h"', "\n";
+ print '#include "erl_bits.h"', "\n";
print '#include "beam_transform_helpers.h"', "\n";
print "\n";
gen_tr_code('pred.');
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index 7d1cf90c8e..c5d26146e8 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -391,14 +391,9 @@ make_op({Name,Arg1,Arg2,Arg3}, Dict) ->
encode_op(Name, [Arg1,Arg2,Arg3], Dict);
make_op({Name,Arg1,Arg2,Arg3,Arg4}, Dict) ->
encode_op(Name, [Arg1,Arg2,Arg3,Arg4], Dict);
-make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5}, Dict) ->
- encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5], Dict);
-make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6}, Dict) ->
- encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6], Dict);
-%% make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7}, Dict) ->
-%% encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7], Dict);
-make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8}, Dict) ->
- encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8], Dict);
+make_op(Instr, Dict) when tuple_size(Instr) >= 6 ->
+ [Name|Args] = tuple_to_list(Instr),
+ encode_op(Name, Args, Dict);
make_op(Op, Dict) when is_atom(Op) ->
encode_op(Op, [], Dict).
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index 30d6670bf6..fb36f2375f 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -186,9 +186,7 @@ remove_lines_block([]) -> [].
%%%
fix_bs_create_bin(Fs, Opts) ->
- %% Force translation to old instructions before we have actually
- %% implemented the `bs_create_bin` instruction.
- case true orelse proplists:get_bool(no_bs_create_bin, Opts) of
+ case proplists:get_bool(no_bs_create_bin, Opts) of
false -> Fs;
true -> fold_functions(fun fix_bs_create_bin/1, Fs)
end.
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 40359c1c54..d9675713ae 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -379,6 +379,8 @@ disasm_instr(B, Bs, Atoms, Literals) ->
disasm_make_fun3(Bs, Atoms, Literals);
init_yregs ->
disasm_init_yregs(Bs, Atoms, Literals);
+ bs_create_bin ->
+ disasm_bs_create_bin(Bs, Atoms, Literals);
_ ->
try decode_n_args(Arity, Bs, Atoms, Literals) of
{Args, RestBs} ->
@@ -443,6 +445,18 @@ disasm_init_yregs(Bs1, Atoms, Literals) ->
{List, RestBs} = decode_n_args(Len, Bs3, Atoms, Literals),
{{init_yregs, [{Z,U,List}]}, RestBs}.
+disasm_bs_create_bin(Bs0, Atoms, Literals) ->
+ {A1, Bs1} = decode_arg(Bs0, Atoms, Literals),
+ {A2, Bs2} = decode_arg(Bs1, Atoms, Literals),
+ {A3, Bs3} = decode_arg(Bs2, Atoms, Literals),
+ {A4, Bs4} = decode_arg(Bs3, Atoms, Literals),
+ {A5, Bs5} = decode_arg(Bs4, Atoms, Literals),
+ {Z, Bs6} = decode_arg(Bs5, Atoms, Literals),
+ {U, Bs7} = decode_arg(Bs6, Atoms, Literals),
+ {u, Len} = U,
+ {List, RestBs} = decode_n_args(Len, Bs7, Atoms, Literals),
+ {{bs_create_bin, [{A1,A2,A3,A4,A5,Z,U,List}]}, RestBs}.
+
%%-----------------------------------------------------------------------
%% decode_arg([Byte]) -> {Arg, [Byte]}
%%
@@ -1173,6 +1187,13 @@ resolve_inst({recv_marker_reserve,[Reg]},_,_,_) ->
resolve_inst({recv_marker_use,[Reg]},_,_,_) ->
{recv_marker_use,Reg};
+%%
+%% OTP 25.
+%%
+
+resolve_inst({bs_create_bin,Args},_,_,_) ->
+ io:format("~p\n", [Args]),
+ {bs_create_bin,Args};
%%
%% Catches instructions that are not yet handled.
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index 9c0fb13c84..a4b10acff2 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -647,3 +647,9 @@ BEAM_FORMAT_NUMBER=0
## @doc Sets the current receive cursor to the marker associated with
## the given Reference.
176: recv_marker_use/1
+
+# OTP 25
+
+## @spec bs_create_bin Fail Alloc Live Unit Dst OpList
+## @doc Builda a new binary using the binary syntax.
+177: bs_create_bin/6
diff --git a/lib/kernel/src/erl_erts_errors.erl b/lib/kernel/src/erl_erts_errors.erl
index 8b0768ac80..66fcd1bb0b 100644
--- a/lib/kernel/src/erl_erts_errors.erl
+++ b/lib/kernel/src/erl_erts_errors.erl
@@ -19,7 +19,7 @@
%%
-module(erl_erts_errors).
--export([format_error/2]).
+-export([format_error/2, format_bs_fail/2]).
-spec format_error(Reason, StackTrace) -> ErrorMap when
Reason :: term(),
@@ -43,6 +43,24 @@ format_error(Reason, [{M,F,As,Info}|_]) ->
end,
format_error_map(Res, 1, #{}).
+-spec format_bs_fail(Reason, StackTrace) -> ErrorMap when
+ Reason :: term(),
+ StackTrace :: erlang:stacktrace(),
+ ErrorMap :: #{'general' => unicode:chardata()}.
+
+format_bs_fail(Reason, [{_,_,_,Info}|_]) ->
+ ErrorInfoMap = proplists:get_value(error_info, Info, #{}),
+ case ErrorInfoMap of
+ #{cause := {Segment,Type,Error}} ->
+ Str0 = do_format_bs_fail( Reason, Type, Error),
+ Str1 = io_lib:format("failed constructing binary: segment ~p, type '~ts': ~ts",
+ [Segment,Type,Str0]),
+ Str = iolist_to_binary(Str1),
+ #{general => Str};
+ #{} ->
+ #{}
+ end.
+
format_atomics_error(new, [Size,Options], Reason, Cause) ->
case Reason of
system_limit ->
@@ -1027,6 +1045,40 @@ format_erlang_error(whereis, [_], _) ->
format_erlang_error(_, _, _) ->
[].
+do_format_bs_fail(system_limit, _Type, size) ->
+ <<"the size is too large">>;
+do_format_bs_fail(badarg, Type, Info) ->
+ do_format_bs_fail(Type, Info).
+
+do_format_bs_fail(float, invalid) ->
+ <<"the size is not one of the supported sizes (16, 32, or 64)">>;
+do_format_bs_fail(float, no_float) ->
+ <<"the integer is outside the range expressible as a float of the given size">>;
+do_format_bs_fail(binary, unit) ->
+ <<"the size of the given binary/bitstring is not a multiple of the unit for the segment">>;
+do_format_bs_fail(_Type, short) ->
+ <<"the given binary/bitstring is shorter than the size of the segment">>;
+do_format_bs_fail(_Type, negative_size) ->
+ <<"the size is negative">>;
+do_format_bs_fail(_Type, size) ->
+ <<"the size is not an integer">>;
+do_format_bs_fail(Type, type) ->
+ <<"not a",
+ (case Type of
+ binary ->
+ <<" binary">>;
+ float ->
+ <<" float or an integer">>;
+ integer ->
+ <<"n integer">>;
+ _ ->
+ <<"n integer encodable as ", (atom_to_binary(Type))/binary>>
+ end)/binary>>.
+
+%%%
+%%% Utility functions.
+%%%
+
list_to_something(List, Error) ->
try length(List) of
_ ->