-
Notifications
You must be signed in to change notification settings - Fork 281
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
Add AlignType and use it for buffer allocations #141
Conversation
@@ -572,7 +574,15 @@ impl<C: Context> Secp256k1<C> { | |||
|
|||
/// Returns the required memory for a preallocated context buffer in a generic manner(sign/verify/all) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add comment that this returns the size in AlignType units
(also for the specific methods Secp256k1::preallocate_size_XX()
, otherwise someone might think they return bytes!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good Point!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think maybe they should return bytes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
93b3a49
to
cca6642
Compare
(I might split the commit into code, and tests+doc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think 8 may hurt us, libsecp256k1 uses uint128_t on gcc, which has alignment 16 on x86_64 for example:
https://godbolt.org/z/6-TVaY
edit: Oh I should have opened IRC earlier:
<wumpus> secp256k1 never stores 128 bit integers though
<wumpus> (it uses it for multiplication in one place, but only on the stack)
That's true, nevermind.
/// ```rust | ||
/// # use secp256k1::*; | ||
/// let buf_size = Secp256k1::preallocate_size(); | ||
/// let mut buf = vec![0; buf_size]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0 as AlignType
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rust automatically assign the right type because it's then passed to Secp256k1::preallocated_new
which accepts [AlignType]
Ha, I don't like the fact that gcc's |
8477d9f
to
07460fd
Compare
I think that's expected. I guess |
src/context.rs
Outdated
/// Uses the ffi `secp256k1_context_preallocated_size` to check the memory size needed for the context | ||
/// Returns the required memory for a preallocated context buffer in a generic manner(sign/verify/all) | ||
/// | ||
/// Notice that the memory returned is in [AlignedType](type.AlignType.html) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/AlignedType/AlignType
To be honest, I don't like that name too much anyway. But I don't have better suggestions. PreallocatedMemory
? Maybe better but not great either.
Ops, I meant to do https://godbolt.org/z/hGjfLQ |
07460fd
to
e8a1513
Compare
#142 Is a blocker to making the tests here pass. |
e8a1513
to
07b7e74
Compare
Now that #142 is solved the tests pass here :) |
Due to an LVLM bug, i128/u128 in Rust are currently not FFI-safe. So, you cannot use these types when doing Rust <-> C FFI. See rust-lang/rust#54341 for further details. The "FFI safety" lint should thus warn about i128/u128 on FFI boundaries, does it not? |
src/context.rs
Outdated
/// Trying to match what `malloc` does in C, this should be aligned enough to contain pointers too. | ||
/// This type can have different size/alignment depending on the architecture. | ||
#[cfg(any(target_pointer_width = "32", target_pointer_width = "16", target_pointer_width = "8"))] | ||
pub type AlignType = u64; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh, based on what documentation are you assuming that this is a type with "the biggest alignment we can get"? I don't think such a type can even exist, since we can do things like
#[repr(align(256))]
struct A(u8);
This type is 256-aligned.
I am not sure what you are trying to do here, but it seems extremely fishy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll start with the purpose and then with "the biggest".
The purpose is creating a pointer that is valid for a C function contract that requires a pointer from malloc
:
The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).
Now malloc
doesn't define what is this. and currently pointers in 64bit architecture are u64
so we know that we need at least this much alignment because the C library does write pointers into that memory.
The only thing currently that can align more than that is __uint128_t
which is 16 bytes alignment.
Now to why this value was chosen.
we support rust 1.22.0 here (last point: https://github.com/rust-bitcoin/rust-secp256k1#contributing) so we can't use #[repr(align(N))]
, and even if we could i'm not sure what would be the right value to put there.
but currently as we don't have support for that, and we know that we need at most 8 bytes alignment we use u64
for alignment.
I agree that's not a good solution, but because rust will never have max_align_t
and we don't have access to C's max alignment (as it's not defined AFAIK), so currently the only thing we can do is maintain an alignment as big as the biggest primitive's alignment that we can check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, so what you really want is C's alignof(max_align_t)
? The Rust libstd has a table for this that I copied into Miri at some point. I don't think it agrees with the value you have been using here, though. In particular, on 64bit x86, max_align_t
is 16-byte aligned.
However, how far does this "requires a pointer from malloc" go? Does the pointer have to be freeable with free
? If yes, you have to call malloc
; the pointer you get from Rust's alloactor might or might not come from malloc
. Even if no, it might be easiest to just call malloc
?
@gnzlbg does libc
expose max_align_t
, or is there some other way to get that information though libc
?
I am still confused why you need this as a type though, as opposed to just having to know the alignment and then using that (together with the size, which I assume is supplied separately) when allocating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gnzlbg does libc expose max_align_t, or is there some other way to get that information though libc?
Not yet but this seems a reasonable thing to implement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am still confused why you need this as a type though, as opposed to just having to know the alignment and then using that (together with the size, which I assume is supplied separately) when allocating.
I suppose that people wanted to be able to use C's alignof(<type>)
to compute the maximing alignment, so instead of creating a MAX_ALIGN
constant, they decided to over engineer it and make it a type.
If we expose this from libc, in Rust, you can just do const MAX_ALIGN: usize = align_of::<libc::max_align_t>();
and use that. In jemalloc-sys
we use a hardcoded MAX_ALIGN
constant for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RalfJung the exact definition from upstream is:
The caller must provide a pointer to a rewritable contiguous block of memory of size at least secp256k1_context_preallocated_size(flags) bytes, suitably aligned to hold an object of any type.
So being able to pass it to free()
isn't a requirement, being aligned as max is.
https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1_preallocated.h#L41
is it possible to achieve alignment 16 without repr(align(N))
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible to achieve alignment 16 without repr(align(N))?
core::arch::...::__m128
have alignment of 16.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the exact definition from upstream is
I see. "object of any type" is certainly not true even in C with compiler extensions, as C also has a way to increase alignment similar to #[repr(align)]
in Rust. So probably upstream means max_align_t
but it might be a good idea to ask them to clarify their docs.
So, why do you need a type that has that alignment, as opposed to just using alloc::alloc
? Is Rust 1.28 (more than a year old at this point!) too "new"?
EDIT: Ah yes, you mentioned Rust 1.22 above (released in 2017, wow). Well I'm afraid if you are restricting yourself to support ancient unsupported versions of Rust, things might become messy. I think it's a mistake to not use a better version when it exists, this is holding back the ecosystem... but that's up to you. I just don't have a lot of motivation to review code that is awful for entirely self-inflicted reasons.
EDIT2: Sorry for venting, you probably didn't even come up with this policy. It's just frustrating to see how policies I consider misguided hold back language development.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"object of any type" is certainly not true even in C with compiler extensions,
That's because that's incorrect. The requirement is that the allocation must be suited for any type with fundamental alignment, where "fundamental" means that you did not use anything like repr(align())
anywhere on it, among other things.
If you use repr(align())
on it in such a way that the alignment of the type is larger than the largest alignment of such a "fundamental" type, then you can't use malloc
to allocate its memory, and need to use an API that accepts an alignment instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right when it comes to modern C.
But upstream uses C89, so there's no max_align_t
and no _Alignas
, and AFAIK there's no sense of "fundamental type".
But it still may be confusing so i'll ask upstream what people think. Thanks.
07b7e74
to
6e399ce
Compare
6e399ce
to
40737a6
Compare
40737a6
to
43b4906
Compare
43b4906
to
12be8fa
Compare
I think we should postpone this until we bump MSRV and then replace the |
Now that our MSRV is 1.29 I think we can do this. |
With 1.29, I think all of this can be done much simpler using https://doc.rust-lang.org/std/alloc/fn.alloc.html and https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.from_size_align. Then we're responsible for deallocation but we don't need to create artifical types to abuse |
Yep. I already started working on a few alternatives, the main "problem" is the |
Uff, I wasn't aware that we offer these functions. Aren't these terribly broken even now if you pass an unaliged buffer there? The docs don't even say anything about alignment. |
succeeded by #233 |
Making sure everything is aligned correctly. Succeeder of #141
This addresses #138
In rust 1.22, we cannot get any type with alignment bigger than 8.
so we take
u64
for any platform that is lower than 64bit andusize
for any platform that is u64 and up.I'm not entirely sure if the assertions are needed, I mostly use them as sanity checks and I hope that they're optimized out by the compiler(they can be easily checked at compile time), but if anyone feel differently I can remove some/all of them.