> "The aligned attribute can only increase the alignment; but you can decrease it by specifying packed as well. See below."
but gcc-6.2 documentation adds:
> "When used as part of a typedef, the aligned attribute can both increase and decrease alignment, and specifying the packed attribute generates a warning."
FWIW, Clang has supported reducing alignment in this fashion for a few years now.
[1] it's possible (likely, even) that it has been working for a while but the documentation was only recently brought up to date; I haven't investigated too carefully.
#define __packed2__ __attribute__((packed, aligned(2)))
/*
* SystemV FS comes in two variants:
* sysv2: System V Release 2 (e.g. Microport), structure elements aligned(2).
* sysv4: System V Release 4 (e.g. Consensys), structure elements aligned(4).
*/
struct sysv2_super_block {
__fs16 s_isize; /* index of first data zone */
__fs32 s_fsize __packed2__; /* total number of zones of this fs */
http://lxr.free-electrons.com/source/include/linux/sysv_fs.h...If the strictest (largest) alignas on a declaration is weaker than the alignment it would have without any alignas specifiers (that is, weaker than its natural alignment or weaker than alignas on another declaration of the same object or type), the program is ill-formed.
Also, using alignas on pointer variable will probably specify alignment of the pointer itself, not the pointed object. I suppose you can create a wrapper class around int with alignas(32) and specify the pointer as pointing to that, but this is going to be nasty given that you can't derive from int and have to write all those operators by hand.
[repr(C)]
struct Something
{
pub foo: f32,
pub _alignment: [EightBytes, 0]
}
where "EightBytes" is a data type of size 8, to align the whole struct on 8 bytes. struct One {
foo: u8,
}
struct Two {
bar: u16,
}
struct Three {
foo: u8,
bar: u16,
}
struct Four {
foo: u16,
bar: u16,
}
fn main() {
assert_eq!(1, std::mem::size_of::<One>());
assert_eq!(2, std::mem::size_of::<Two>());
assert_eq!(4, std::mem::size_of::<Three>());
assert_eq!(4, std::mem::size_of::<Four>());
}It's not optimal but you can always use libc::posix_memalign()
[repr(C)]
struct StructA
{
pub foo: f32,
_alignment: [SixteenBytes, 0]
}
[repr(C)]
struct StructB
{
pub foo: f32,
_alignment: [ThirtytwoBytes, 0]
}
Are both just padded with 16-4 and 32-4 bytes respectively, so they are equivalent to making a padding like this in the first case? [repr(C)]
struct StructAPadded
{
pub foo: f32,
_padding : TwelveBytes;
}It seems that at present, if you want to generate an unaligned SSE2 load/store, you have to rely on modern memcpy (spelled copy_nonoverlapping in Rust) optimization. See the first reply to https://internals.rust-lang.org/t/unaligned-simd-sse2-in-par...
#include <stdlib.h>
#include <stdint.h>
uint64_t sum (char *p, size_t nwords)
{
uint64_t res = 0;
size_t i;
for (i = 0; i < nwords; i += 8) {
uint64_t tmp;
memcpy(&tmp, &p[i], sizeof(tmp));
res += tmp;
}
return res;
}Deal breaker: your memcpy invocation requires a sufficiently smart compiler to convert into normal unaligned load on x86 and seems to prevent GCC autovectorization. In this case OP actually didn't want vectorization, but in general it happens that such workarounds confuse compilers and produce worse code.
Vectorization is in general not applicable here since it usually requires aligned memory... not all implementations do, but most. In any case, benchmarking is more appropriate than armchair optimizing.
I prefer to just add alignment specification and move on, assuming I don't care about portability. If portability matters, reread my original post ;)
I'd call compiler specific alignment attributes more arcane, convoluted, and susceptible to future bugs.
Vectorization isn't a panacea. You need to benchmark to be sure, lacking that I expect GCC to be better at optimizing code than you. If you disagree, please manually write a vectorized one that handles non-aligned addition and post your results :)
Data written as int may be read as char, but going the other way is usually a standards violation. (an exception being if you had used char to implement a memcpy-like function, but in that case you should expect compiler bugs to bite you)
And yes, as for other casts, I'm well aware that they cause problems.
Unless __aligned__ or __packed__ is documented as also relaxing the strict aliasing rules - if such documentation exists, I haven't found it - there's still undefined behavior from the strict aliasing violation by type punning size_t <-> uint32_t.
Bad alignment is not the only possible "bad" optimization compilers can apply that relies on this being undefined behavior. For example, they may mistakenly assume a size_t value in memory can be cached in a register, and not reloaded from memory when it calls code that uses only uint32_t* pointers, because in a standards compliant program these cannot be used to modify size_t values.
You need __may_alias__ as well.
struct S { short f[3]; } __attribute__ ((aligned (8)));
where it actually is used outside of a struct so that this whole 6B thing is aligned to 8B instead of the default 2B. .L13:
movdqa (%r8), %xmm2
...At least that would be a failure at compile-time, rather than run-time.
And yes, it's a compile-time failure, which is great. My comment should not be read as criticism at all (though I would likely use `uint16_t`, as the OP says the code is intended to work with [presumably aligned] 16-bit words).
And I'm not sure how exactly this code will fail on architectures which don't support unaligned uint32_t.