Skip to content

Add binary_part/3 to BIFs #1569

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 9, 2025
Merged

Conversation

jgonet
Copy link
Contributor

@jgonet jgonet commented Mar 13, 2025

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

@jgonet jgonet mentioned this pull request Mar 13, 2025
36 tasks
Copy link
Collaborator

@bettio bettio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the NIF function can be completely dropped.

@bettio
Copy link
Collaborator

bettio commented Mar 16, 2025

A number of CI fixes has been made in release-0.6. Let's rebase it on latest release-0.6.

@bettio bettio mentioned this pull request Mar 16, 2025
3 tasks
@jgonet jgonet force-pushed the jgonet/binary-part branch 2 times, most recently from cac7b78 to 0b91671 Compare March 31, 2025 17:27
Comment on lines 1615 to 1623
*return_value = gcbif->gcbif1_ptr(ctx, 0, 1, ctx->x[0]);
return true;
}
case 2: {
*return_value = gcbif->gcbif2_ptr(ctx, 0, 0, ctx->x[0], ctx->x[1]);
*return_value = gcbif->gcbif2_ptr(ctx, 0, 2, ctx->x[0], ctx->x[1]);
return true;
}
case 3: {
*return_value = gcbif->gcbif3_ptr(ctx, 0, 0, ctx->x[0], ctx->x[1], ctx->x[2]);
*return_value = gcbif->gcbif3_ptr(ctx, 0, 3, ctx->x[0], ctx->x[1], ctx->x[2]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll rebase after #1610 is merged.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this, see my other comments.

RAISE_ERROR_BIF(fail_label, OUT_OF_MEMORY_ATOM);
}

return term_maybe_create_sub_binary(ctx->x[0], slice.pos, slice.len, &ctx->heap, ctx->global);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be arg1, not ctx->x[0]

RAISE_ERROR_BIF(fail_label, BADARG_ATOM);
}

if (UNLIKELY(memory_ensure_free_with_roots(ctx, slice.heap_size, live, ctx->x, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to add arg1 to ctx->[live], and pass live+1 with a comment saying you can do that because x has size MAX_REGS+1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand. If live is always zero, we'd clobber x[1] in that case and preserve nothing since roots length is still 0.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the confusion.

I meant:

Suggested change
if (UNLIKELY(memory_ensure_free_with_roots(ctx, slice.heap_size, live, ctx->x, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
// We can always use ctx->x[live] as ctx->x has MAX_REGS+1 slots
ctx->x[live] = arg1;
if (UNLIKELY(memory_ensure_free_with_roots(ctx, slice.heap_size, live + 1, ctx->x, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {

If live is 0, you can clobber x[1]. This is what live being 0 means.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the implementation then. In maybe_call_native we hardcode live=0. It's used in tail-calls (which is fine) and in apply. Why it's fine to clobber registers in apply case?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it's fine to clobber x regs in any NIF or external calls. My understanding is that x regs are used for args and results for NIF or external calls, and they do not need to be preserved. It is as if live was equal to 0. Caller does not even ensure that they are valid beyond argc. On return, only x[0] is considered valid.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @pguyot, it works with both NIFs and BIFs.

Comment on lines 1615 to 1623
*return_value = gcbif->gcbif1_ptr(ctx, 0, 1, ctx->x[0]);
return true;
}
case 2: {
*return_value = gcbif->gcbif2_ptr(ctx, 0, 0, ctx->x[0], ctx->x[1]);
*return_value = gcbif->gcbif2_ptr(ctx, 0, 2, ctx->x[0], ctx->x[1]);
return true;
}
case 3: {
*return_value = gcbif->gcbif3_ptr(ctx, 0, 0, ctx->x[0], ctx->x[1], ctx->x[2]);
*return_value = gcbif->gcbif3_ptr(ctx, 0, 3, ctx->x[0], ctx->x[1], ctx->x[2]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this, see my other comments.

@@ -1612,15 +1612,15 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
const struct GCBif *gcbif = EXPORTED_FUNCTION_TO_GCBIF(exported_bif);
switch (arity) {
case 1: {
*return_value = gcbif->gcbif1_ptr(ctx, 0, 0, ctx->x[0]);
*return_value = gcbif->gcbif1_ptr(ctx, 0, 1, ctx->x[0]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, since we have another open discussion on this topic, and the issue we experienced here turned out to be something different (did I understood this right?), I suggest removing this change from here and making this PR focused on just binary_part.

@bettio
Copy link
Collaborator

bettio commented Apr 3, 2025

Let's move forward with binary_part, and let's have an additional (but separated) discussion about live parameter when doing the NIF trick on that specific PR.

@jgonet jgonet force-pushed the jgonet/binary-part branch from 0b91671 to ec4baea Compare April 3, 2025 17:58
Copy link
Collaborator

@bettio bettio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with this approach, it is a matter of polishing. Correct naming and placing functions in the right place is important to keep everything maintainable.

@@ -108,6 +108,48 @@ term bif_erlang_bit_size_1(Context *ctx, uint32_t fail_label, int live, term arg
return term_from_int32(len);
}

bool get_sub_binary_slice(term bin_term, term pos_term, term len_term, struct SubBinarySlice *slice)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name didn't suggest me the purpose of this function without looking at the actual implementation. What about maybe_get_sub_binary_slice or even sanitize_sub_binary_slice.
Furthermore,

  1. we keep prefixing "public" function names, so a "public" function declared in bif.h is named such as bif_sanitize_sub_binary_slice.
  2. I would keep this function in term.h, since it is a common helper between nifs and bifs.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be even a more reusable API if take indexes as avm_int_t, so we might use it internally in other situations.

@@ -39,11 +39,20 @@ extern "C" {

#define MAX_BIF_NAME_LEN 260

struct SubBinarySlice
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are using this struct only as an output of get_sub_binary_slice, so we might link better the 2 names when we rename the function. It might be called SlicedBinary or SanitizedSlice or whatever we'll use as new name.
Also let's not forget to do the typedef trick, so we don't have to use all around structSubBinarySlice foo_bar;

Signed-off-by: Jakub Gonet <[email protected]>
@jgonet jgonet force-pushed the jgonet/binary-part branch from ec4baea to 19395fa Compare April 7, 2025 14:54
@bettio bettio changed the base branch from main to release-0.6 April 8, 2025 22:35
@bettio bettio changed the title Add binary_part/3 to BIFs Add binary_part/3 to BIFs Apr 8, 2025
return pos_len.pos == -1 && pos_len.len == -1;
}

static inline BinaryPosLen term_invalid_binary_pos_len(void)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, last minute review: term_is_invalid_binary_pos_len and term_invalid_binary_pos_len are unused, let's remove them.
Also, for the future: it is not necessary having (void) for functions with no parameters. () is ok.

Copy link
Collaborator

@bettio bettio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, forget my last comment. I didn't notice this was required for #1621

@bettio bettio merged commit d024e6d into atomvm:release-0.6 Apr 9, 2025
146 of 149 checks passed
bettio added a commit that referenced this pull request Apr 9, 2025
Merge `binary_part/3` BIF (PR #1569), fix to `is_number` (#1624) and
migration to ubuntu-20.04 container (#1626) from release-0.6 branch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants