Discussion:
question about attribute aligned for functions
Martin Sebor
2018-11-23 19:31:37 UTC
Permalink
GCC currently accepts the declaration of f0 below but ignores
the attribute. On aarch64 (and I presume on other targets with
a default function alignment greater than 1), GCC rejects f1
with an error, even though it accepts -falign-functions=1
without as much as a warning.

Clang, on the other hand, rejects f0 with a hard error because
the alignment is not a power of two, but accepts f1 and appears
to honor the attribute. It also accepts -falign-functions=1.

I think diagnosing f0 with a warning is helpful because an explicit
zero alignment is most likely a mistake (especially when it comes
from a macro or some computation).

But I don't see a good reason to reject a program that specifies
a smaller alignment for a function when the default (or minimum)
alignment is greater. A smaller alignment is trivially satisfied
by a greater alignment so either accepting it or dropping it seems
preferable to failing with an error (either could be with or without
a warning).

__attribute__ ((aligned (0))) void f0 (void); // accepted, ignored
__attribute__ ((aligned (1))) void f1 (void); // aarch64 error
__attribute__ ((aligned (4))) void f4 (void); // okay

Does anyone know why GCC rejects the program, or can anyone think
of a reason why GCC should not behave as suggested above?

Thanks
Martin
Jeff Law
2018-11-26 22:37:50 UTC
Permalink
Post by Martin Sebor
GCC currently accepts the declaration of f0 below but ignores
the attribute.  On aarch64 (and I presume on other targets with
a default function alignment greater than 1), GCC rejects f1
with an error, even though it accepts -falign-functions=1
without as much as a warning.
Clang, on the other hand, rejects f0 with a hard error because
the alignment is not a power of two, but accepts f1 and appears
to honor the attribute.  It also accepts -falign-functions=1.
I think diagnosing f0 with a warning is helpful because an explicit
zero alignment is most likely a mistake (especially when it comes
from a macro or some computation).
But I don't see a good reason to reject a program that specifies
a smaller alignment for a function when the default (or minimum)
alignment is greater.  A smaller alignment is trivially satisfied
by a greater alignment so either accepting it or dropping it seems
preferable to failing with an error (either could be with or without
a warning).
  __attribute__ ((aligned (0))) void f0 (void);   // accepted, ignored
  __attribute__ ((aligned (1))) void f1 (void);   // aarch64 error
  __attribute__ ((aligned (4))) void f4 (void);   // okay
Does anyone know why GCC rejects the program, or can anyone think
of a reason why GCC should not behave as suggested above?
Note we have targets that support single byte opcodes and do not have
any requirements for functions starting on any boundary. mn103 comes to
mind.

However, the attribute can't be used to decrease a function's alignment,
so values of 0 or 1 are in practice totally uninteresting and one could
make an argument to warn for them.

Whether or not to warn in general if the alignment attribute is smaller
than the default may be subject to debate. I guess it depends on the
general intent that we'd find in real world codes.

jeff
Martin Sebor
2018-11-27 18:57:10 UTC
Permalink
Post by Jeff Law
Post by Martin Sebor
GCC currently accepts the declaration of f0 below but ignores
the attribute.  On aarch64 (and I presume on other targets with
a default function alignment greater than 1), GCC rejects f1
with an error, even though it accepts -falign-functions=1
without as much as a warning.
Clang, on the other hand, rejects f0 with a hard error because
the alignment is not a power of two, but accepts f1 and appears
to honor the attribute.  It also accepts -falign-functions=1.
I think diagnosing f0 with a warning is helpful because an explicit
zero alignment is most likely a mistake (especially when it comes
from a macro or some computation).
But I don't see a good reason to reject a program that specifies
a smaller alignment for a function when the default (or minimum)
alignment is greater.  A smaller alignment is trivially satisfied
by a greater alignment so either accepting it or dropping it seems
preferable to failing with an error (either could be with or without
a warning).
  __attribute__ ((aligned (0))) void f0 (void);   // accepted, ignored
  __attribute__ ((aligned (1))) void f1 (void);   // aarch64 error
  __attribute__ ((aligned (4))) void f4 (void);   // okay
Does anyone know why GCC rejects the program, or can anyone think
of a reason why GCC should not behave as suggested above?
Note we have targets that support single byte opcodes and do not have
any requirements for functions starting on any boundary. mn103 comes to
mind.
However, the attribute can't be used to decrease a function's alignment,
so values of 0 or 1 are in practice totally uninteresting and one could
make an argument to warn for them.
The attribute does reduce the default alignment at least on some
targets. For instance, on x86_64 it lowers it from the default
16 to as little as 2, but it silently ignores 1.

At the same time, the following passes on x86_64:

__attribute__ ((aligned (1))) void f1 (void) { }
_Static_assert (__alignof__ (f1) == 1); // wrong alignof result

__attribute__ ((aligned)) void f0 (void) { }
_Static_assert (__alignof__ (f0) == 16);

__attribute__ ((aligned (2))) void f2 (void) { }
_Static_assert (__alignof__ (f2) == 2);

but the assembly shows no .align directive for f1, .align 16 for
f0, and .align 2 for f2, and the object file shows f1 first with
padding up to 16-bytes, followed by f0 padded to a 2 byte
boundary, followed by f2. The picture stays the same when f1()
is declared without the attribute (i.e., it's 16-byte aligned
by default). So __alignof__ reports the wrong alignment for
the f1 declared aligned (1).
Post by Jeff Law
Whether or not to warn in general if the alignment attribute is smaller
than the default may be subject to debate. I guess it depends on the
general intent that we'd find in real world codes.
I would expect real world code to care about achieving at least
the specified alignment.

A less restrictive alignment requirement is satisfied by providing
a more restrictive one, and (the above notwithstanding) the manual
documents that

You cannot use this attribute to decrease the alignment of
a function, only to increase it.

So I wouldn't expect real code to be relying on decreasing
the alignment.

That said, since GCC does make it possible to decrease the default
alignment of functions, I can think of no reason for it not to
continue when it's possible. I.e., on targets with underaligned
instruction reads to be honor requests for underaligned functions.
On strictly aligned targets I think the safe thing to do is to
quietly ignore requests for smaller alignments by default, and
perhaps provide a new option to request a warning (with the warning
being disabled by default).

Do you see a problem with this approach?

Martin
Martin Sebor
2018-11-27 22:35:10 UTC
Permalink
Post by Martin Sebor
Post by Jeff Law
Post by Martin Sebor
GCC currently accepts the declaration of f0 below but ignores
the attribute.  On aarch64 (and I presume on other targets with
a default function alignment greater than 1), GCC rejects f1
with an error, even though it accepts -falign-functions=1
without as much as a warning.
Clang, on the other hand, rejects f0 with a hard error because
the alignment is not a power of two, but accepts f1 and appears
to honor the attribute.  It also accepts -falign-functions=1.
I think diagnosing f0 with a warning is helpful because an explicit
zero alignment is most likely a mistake (especially when it comes
from a macro or some computation).
But I don't see a good reason to reject a program that specifies
a smaller alignment for a function when the default (or minimum)
alignment is greater.  A smaller alignment is trivially satisfied
by a greater alignment so either accepting it or dropping it seems
preferable to failing with an error (either could be with or without
a warning).
   __attribute__ ((aligned (0))) void f0 (void);   // accepted, ignored
   __attribute__ ((aligned (1))) void f1 (void);   // aarch64 error
   __attribute__ ((aligned (4))) void f4 (void);   // okay
Does anyone know why GCC rejects the program, or can anyone think
of a reason why GCC should not behave as suggested above?
Note we have targets that support single byte opcodes and do not have
any requirements for functions starting on any boundary.  mn103 comes to
mind.
However, the attribute can't be used to decrease a function's alignment,
so values of 0 or 1 are in practice totally uninteresting and one could
make an argument to warn for them.
The attribute does reduce the default alignment at least on some
targets.  For instance, on x86_64 it lowers it from the default
16 to as little as 2, but it silently ignores 1.
  __attribute__ ((aligned (1))) void f1 (void) { }
  _Static_assert (__alignof__ (f1) == 1);   // wrong alignof result
  __attribute__ ((aligned)) void f0 (void) { }
  _Static_assert (__alignof__ (f0) == 16);
  __attribute__ ((aligned (2))) void f2 (void) { }
  _Static_assert (__alignof__ (f2) == 2);
but the assembly shows no .align directive for f1, .align 16 for
f0, and .align 2 for f2, and the object file shows f1 first with
padding up to 16-bytes, followed by f0 padded to a 2 byte
boundary, followed by f2.  The picture stays the same when f1()
is declared without the attribute (i.e., it's 16-byte aligned
by default).  So __alignof__ reports the wrong alignment for
the f1 declared aligned (1).
Actually, after some thought and experimenting I don't believe
the alignment __alignof__ reports is wrong. It's the best it
can do. The actual alignment of the function in the object file
could be greater (e.g., depending on what other functions are
before or after it), and that should be fine.

GCC should also be able to optimize the size of the emitted code
by rearranging functions based on both their size and alignment
requirements, perhaps by increasing both for some functions if
that reduces the size overall. This optimization can only be
done after __alignof__ expressions have been evaluated.

So with that, I think the argument to attribute aligned must
inevitably be viewed as the lower bound on a function's (or even
variable's) alignment, and specifying a value that's smaller than
what's ultimately provided should be accepted without a warning.
I think that should hold for strictly aligned targets like sparc
with a minimum alignment greater than 1 as well as for relaxed
ones like i386 with a minimum alignment of 1.

I will make this change to the aligned attribute handler to avoid
the failures in the builtin-has-attribute-*.c tests on strictly
aligned targets.
Post by Martin Sebor
Post by Jeff Law
Whether or not to warn in general if the alignment attribute is smaller
than the default may be subject to debate.  I guess it depends on the
general intent that we'd find in real world codes.
I would expect real world code to care about achieving at least
the specified alignment.
A less restrictive alignment requirement is satisfied by providing
a more restrictive one, and (the above notwithstanding) the manual
documents that
  You cannot use this attribute to decrease the alignment of
  a function, only to increase it.
So I wouldn't expect real code to be relying on decreasing
the alignment.
That said, since GCC does make it possible to decrease the default
alignment of functions, I can think of no reason for it not to
continue when it's possible.  I.e., on targets with underaligned
instruction reads to be honor requests for underaligned functions.
On strictly aligned targets I think the safe thing to do is to
quietly ignore requests for smaller alignments by default, and
perhaps provide a new option to request a warning (with the warning
being disabled by default).
Do you see a problem with this approach?
Martin
Florian Weimer
2018-11-28 13:04:28 UTC
Permalink
Post by Martin Sebor
__attribute__ ((aligned (1))) void f1 (void) { }
_Static_assert (__alignof__ (f1) == 1); // wrong alignof result
__attribute__ ((aligned)) void f0 (void) { }
_Static_assert (__alignof__ (f0) == 16);
__attribute__ ((aligned (2))) void f2 (void) { }
_Static_assert (__alignof__ (f2) == 2);
Is there any value in implementing alignof on functions?

sizeof (f1) is defined to be 1, allegedly to support function pointer
arithmetic (which is why sizeof (void) is 1 as well). In practice, this
is confusing to the programmer because the size of a function is at
least known to the linker.

Thanks,
Florian
Martin Sebor
2018-11-28 15:59:28 UTC
Permalink
Post by Florian Weimer
Post by Martin Sebor
__attribute__ ((aligned (1))) void f1 (void) { }
_Static_assert (__alignof__ (f1) == 1); // wrong alignof result
__attribute__ ((aligned)) void f0 (void) { }
_Static_assert (__alignof__ (f0) == 16);
__attribute__ ((aligned (2))) void f2 (void) { }
_Static_assert (__alignof__ (f2) == 2);
Is there any value in implementing alignof on functions?
sizeof (f1) is defined to be 1, allegedly to support function pointer
arithmetic (which is why sizeof (void) is 1 as well). In practice, this
is confusing to the programmer because the size of a function is at
least known to the linker.
I also don't see any utility in applying sizeof to functions, at
least not with the existing semantics of evaluating to 1. alignof
seems somewhat more useful, but not tremendously so, for similar
reasons (at least it gives the lower bound). But both have been
implemented for ages so they couldn't be removed without some risk.
I don't know if the benefits of the removal would justify the risks.

Martin
Jeff Law
2018-11-29 04:07:12 UTC
Permalink
Post by Martin Sebor
Post by Jeff Law
Post by Martin Sebor
GCC currently accepts the declaration of f0 below but ignores
the attribute.  On aarch64 (and I presume on other targets with
a default function alignment greater than 1), GCC rejects f1
with an error, even though it accepts -falign-functions=1
without as much as a warning.
Clang, on the other hand, rejects f0 with a hard error because
the alignment is not a power of two, but accepts f1 and appears
to honor the attribute.  It also accepts -falign-functions=1.
I think diagnosing f0 with a warning is helpful because an explicit
zero alignment is most likely a mistake (especially when it comes
from a macro or some computation).
But I don't see a good reason to reject a program that specifies
a smaller alignment for a function when the default (or minimum)
alignment is greater.  A smaller alignment is trivially satisfied
by a greater alignment so either accepting it or dropping it seems
preferable to failing with an error (either could be with or without
a warning).
   __attribute__ ((aligned (0))) void f0 (void);   // accepted, ignored
   __attribute__ ((aligned (1))) void f1 (void);   // aarch64 error
   __attribute__ ((aligned (4))) void f4 (void);   // okay
Does anyone know why GCC rejects the program, or can anyone think
of a reason why GCC should not behave as suggested above?
Note we have targets that support single byte opcodes and do not have
any requirements for functions starting on any boundary.  mn103 comes to
mind.
However, the attribute can't be used to decrease a function's alignment,
so values of 0 or 1 are in practice totally uninteresting and one could
make an argument to warn for them.
The attribute does reduce the default alignment at least on some
targets.  For instance, on x86_64 it lowers it from the default
16 to as little as 2, but it silently ignores 1.
[ ... ]
You cannot use this attribute to decrease the alignment of a function,
only to increase it. However, when you explicitly specify a function
alignment this overrides the effect of the
@option{-falign-functions} (@pxref{Optimize Options}) option for this
function.
[ ... ]

My reading of that would be that I would get an error/warning if I even
specified an alignment attribute which decreased the alignment.

If it instead said something like

You can not rely on this attribute to decrease ...

Then current behavior (where apparently you can decrease the alignment
on some ports) makes much more sense.

I guess it ultimately depends on how one interprets that tidbit from the
docs.
Post by Martin Sebor
Post by Jeff Law
Whether or not to warn in general if the alignment attribute is smaller
than the default may be subject to debate.  I guess it depends on the
general intent that we'd find in real world codes.
I would expect real world code to care about achieving at least
the specified alignment.
If we only allow increasing, yes. At which point what do the values 0
or 1 realistically mean?

If we allow decreasing, then the user may be asking for a smaller
alignment to achieve better code density.
Post by Martin Sebor
A less restrictive alignment requirement is satisfied by providing
a more restrictive one, and (the above notwithstanding) the manual
documents that
  You cannot use this attribute to decrease the alignment of
  a function, only to increase it.
So I wouldn't expect real code to be relying on decreasing
the alignment.
That said, since GCC does make it possible to decrease the default
alignment of functions, I can think of no reason for it not to
continue when it's possible.  I.e., on targets with underaligned
instruction reads to be honor requests for underaligned functions.
On strictly aligned targets I think the safe thing to do is to
quietly ignore requests for smaller alignments by default, and
perhaps provide a new option to request a warning (with the warning
being disabled by default).
Do you see a problem with this approach?
ISTM we need to figure out whether or not we consider an attempt to
lower the alignment as invalid vs we'll try, but no guarantees.

jeff
Martin Sebor
2018-11-29 15:34:46 UTC
Permalink
Post by Jeff Law
Post by Martin Sebor
Post by Jeff Law
Post by Martin Sebor
GCC currently accepts the declaration of f0 below but ignores
the attribute.  On aarch64 (and I presume on other targets with
a default function alignment greater than 1), GCC rejects f1
with an error, even though it accepts -falign-functions=1
without as much as a warning.
Clang, on the other hand, rejects f0 with a hard error because
the alignment is not a power of two, but accepts f1 and appears
to honor the attribute.  It also accepts -falign-functions=1.
I think diagnosing f0 with a warning is helpful because an explicit
zero alignment is most likely a mistake (especially when it comes
from a macro or some computation).
But I don't see a good reason to reject a program that specifies
a smaller alignment for a function when the default (or minimum)
alignment is greater.  A smaller alignment is trivially satisfied
by a greater alignment so either accepting it or dropping it seems
preferable to failing with an error (either could be with or without
a warning).
   __attribute__ ((aligned (0))) void f0 (void);   // accepted, ignored
   __attribute__ ((aligned (1))) void f1 (void);   // aarch64 error
   __attribute__ ((aligned (4))) void f4 (void);   // okay
Does anyone know why GCC rejects the program, or can anyone think
of a reason why GCC should not behave as suggested above?
Note we have targets that support single byte opcodes and do not have
any requirements for functions starting on any boundary.  mn103 comes to
mind.
However, the attribute can't be used to decrease a function's alignment,
so values of 0 or 1 are in practice totally uninteresting and one could
make an argument to warn for them.
The attribute does reduce the default alignment at least on some
targets.  For instance, on x86_64 it lowers it from the default
16 to as little as 2, but it silently ignores 1.
[ ... ]
You cannot use this attribute to decrease the alignment of a function,
only to increase it. However, when you explicitly specify a function
alignment this overrides the effect of the
@option{-falign-functions} (@pxref{Optimize Options}) option for this
function.
[ ... ]
My reading of that would be that I would get an error/warning if I even
specified an alignment attribute which decreased the alignment.
If it instead said something like
You can not rely on this attribute to decrease ...
Then current behavior (where apparently you can decrease the alignment
on some ports) makes much more sense.
I guess it ultimately depends on how one interprets that tidbit from the
docs.
GCC does disallow decreasing the function alignment -- but only
for functions that were already declared with a more restrictive
one. Like this:

__attribute__ ((aligned (4))) void f (void);

__attribute__ ((aligned (2))) void f (void);

warning: ignoring attribute ‘aligned (2)’ because it conflicts with
attribute ‘aligned (4)’

It doesn't warn about going from the default (say 16 on i86)
to something smaller, and it honors that lower alignment up
to the supported minimum. It's probably worth clarifying in
the manual. Let me propose something.
Post by Jeff Law
Post by Martin Sebor
Post by Jeff Law
Whether or not to warn in general if the alignment attribute is smaller
than the default may be subject to debate.  I guess it depends on the
general intent that we'd find in real world codes.
I would expect real world code to care about achieving at least
the specified alignment.
If we only allow increasing, yes. At which point what do the values 0
or 1 realistically mean?
If we only allowed increasing then both 0 and 1 would be
meaningless.
Post by Jeff Law
If we allow decreasing, then the user may be asking for a smaller
alignment to achieve better code density.
Yes, that's definitely possible (I just opened pr88231 -
aligned functions laid down inefficiently, as I noticed this
doesn't work as well as it could and arguably should). But they
can't get it if the target doesn't support it. In which case I
think adding a new warning to point it out might be useful. At
the same time, users wanting maximum density across all their
targets shouldn't have to worry about what the minimum alignment
is on each of them and hardwire different constants into
the attribute. I think they should be able to specify 1 and
have GCC round it up as necessary, with no warning. So I'd make
the new warning off by default.
Post by Jeff Law
Post by Martin Sebor
A less restrictive alignment requirement is satisfied by providing
a more restrictive one, and (the above notwithstanding) the manual
documents that
  You cannot use this attribute to decrease the alignment of
  a function, only to increase it.
So I wouldn't expect real code to be relying on decreasing
the alignment.
That said, since GCC does make it possible to decrease the default
alignment of functions, I can think of no reason for it not to
continue when it's possible.  I.e., on targets with underaligned
instruction reads to be honor requests for underaligned functions.
On strictly aligned targets I think the safe thing to do is to
quietly ignore requests for smaller alignments by default, and
perhaps provide a new option to request a warning (with the warning
being disabled by default).
Do you see a problem with this approach?
ISTM we need to figure out whether or not we consider an attempt to
lower the alignment as invalid vs we'll try, but no guarantees.
Agreed. Let me know if the approach above makes sense to you.
I can look into adding the new warning either next or in stage
1 if it's too late now.

Martin
Jeff Law
2018-12-01 00:12:35 UTC
Permalink
Post by Martin Sebor
GCC does disallow decreasing the function alignment -- but only
for functions that were already declared with a more restrictive
  __attribute__ ((aligned (4))) void f (void);
  __attribute__ ((aligned (2))) void f (void);
  warning: ignoring attribute ‘aligned (2)’ because it conflicts with
attribute ‘aligned (4)’
It doesn't warn about going from the default (say 16 on i86)
to something smaller, and it honors that lower alignment up
to the supported minimum.  It's probably worth clarifying in
the manual.  Let me propose something.
Please do. Thanks.
Post by Martin Sebor
Post by Martin Sebor
Post by Jeff Law
Whether or not to warn in general if the alignment attribute is smaller
than the default may be subject to debate.  I guess it depends on the
general intent that we'd find in real world codes.
I would expect real world code to care about achieving at least
the specified alignment.
If we only allow increasing, yes.  At which point what do the values 0
or 1 realistically mean?
If we only allowed increasing then both 0 and 1 would be
meaningless.
If we allow decreasing, then the user may be asking for a smaller
alignment to achieve better code density.
Yes, that's definitely possible (I just opened pr88231 -
aligned functions laid down inefficiently, as I noticed this
doesn't work as well as it could and arguably should).  But they
can't get it if the target doesn't support it.  In which case I
think adding a new warning to point it out might be useful.  At
the same time, users wanting maximum density across all their
targets shouldn't have to worry about what the minimum alignment
is on each of them and hardwire different constants into
the attribute.  I think they should be able to specify 1 and
have GCC round it up as necessary, with no warning.  So I'd make
the new warning off by default.
That seems like a better UI than forcing the user to know what their
target actually supports.

So, yea I think I'm in agreement with where you're going.

jeff

Loading...