Forums

16-bit integer ops on 32-bit processor(PowerPC)

Started by joshc February 14, 2005
I'm wondering about the assembly generated by my compiler for a 32-bit
PowerPC processor. I have some code(in C) that deals with 16 bit
unsigned integer types(unsigned short int on my compiler) and I have
some questions about the assembly that is generated for some of the
code.

If I have a function such as the following:

unsigned short int foo(unsigned short int x, unsigned short int y)
{
  return x >> y;
}

I compile this without optimizations and I get assembly such as the
following(x is passed in r31, y in r30):

rlwinm r12, r31, 0, 16, 31
srw r29, r12, r30

What I want to understand is why 'x' is effectively cast to an unsigned
short int but 'y' is not. Is this because the ANSI C standard states
that shifting a 16-bit value by more than 15 results in undefined
behavior?

I am basically trying to get a grasp of the overhead involved with
dealing with 16-bit integers on a 32-bit machine. BTW, I tried posting
this to comp.compilers but apparently it might take a few days for the
moderator to accept my message so I posted here due to urgency.

Thanks!

joshc <josh.curtz@gmail.com> wrote:

[...]
> What I want to understand is why 'x' is effectively cast to an unsigned > short int but 'y' is not. Is this because the ANSI C standard states > that shifting a 16-bit value by more than 15 results in undefined > behavior?
It may not be the actual reason (nobody outside your compiler vendor's shop may actually ever get to know that one), but at the very least, it's a perfectly valid possible reason. There's no way any legal value of y could be large enough to make a cast from 32-bit to 16-bit integer have any effect. For the rest, you'll have to consult ABI documentation to learn how 16-bit are supposed to passed to functions, on that platform. Pay special attention to the question of whether a 16-bit value passed to a function in a 32-bit register is supposed to be "masked clean" or not, and if so, by whom: the caller or the called function. -- Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de) Even if all the snow were burnt, ashes would remain.
Hans, thanks for the reply. I'll check the documentation for EABI.
Let's suppose that the callee is supposed to do the cleaning of the
most significant 16 bits. This cleaning is happening to achieve typing,
correct? I mean to say it is so if you pass for example a 32-bit
integer in as an argument to a function that takes a 16-bit integer as
a parameter, the 32-bit integer according to the C standard gets cast
to a 16-bit integer and loses its most significant 16 bits.

Thanks.

joshc <josh.curtz@gmail.com> wrote:
> Hans, thanks for the reply. I'll check the documentation for EABI. > Let's suppose that the callee is supposed to do the cleaning of the > most significant 16 bits. This cleaning is happening to achieve typing, > correct?
You could put it that way. The language dictates that the code must behave *as if* the extra upper 16 bits weren't there at all --- but only to the extent that a correct C program can ever notice the difference. Once the user's source code has trespassed into "undefined behaviour" country, the implementation can do whatever it pleases. It's up to the implementor to decide when and how to achieve the "as if" result. The ABI is one of the documents that document or structure such decisions. -- Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de) Even if all the snow were burnt, ashes would remain.
joshc wrote:
> > Hans, thanks for the reply. I'll check the documentation for EABI. > Let's suppose that the callee is supposed to do the cleaning of the > most significant 16 bits. This cleaning is happening to achieve > typing, correct? I mean to say it is so if you pass for example a > 32-bit integer in as an argument to a function that takes a 16-bit > integer as a parameter, the 32-bit integer according to the C > standard gets cast to a 16-bit integer and loses its most > significant 16 bits.
No, if the value doesn't fit you have an undefined behavior condition. So you simply document the range of argument the callee will accept. Unfortunately, with C lacking sub-ranges and range checking, you can't enforce this automatically. -- "If you want to post a followup via groups.google.com, don't use the broken "Reply" link at the bottom of the article. Click on "show options" at the top of the article, then click on the "Reply" at the bottom of the article headers." - Keith Thompson
joshc wrote:
> ... > > unsigned short int foo(unsigned short int x, unsigned short int y) > { > return x >> y; > } > > I compile this without optimizations and I get assembly such as the > following(x is passed in r31, y in r30): > > rlwinm r12, r31, 0, 16, 31 > srw r29, r12, r30 > > What I want to understand is why 'x' is effectively cast to an
unsigned
> short int but 'y' is not. ... > > Thanks!
There are two things about C that are often overlooked in cases like this. First, according to C standards since K&R, shorts are ordinarily promoted to ints for arithmetic. This can be optimised into narrower operations by a compiler but only if the same result can be guaranteed (as if the operation were evaluated on promoted ints). Second: unlike other operators, shift amounts (RHS of << >>) are not really "typed" in the same sense as, for instance, arithmetic operands. The bit count, shift amount, whatever you call it, is entirely abstract. For this reason the concept of "promoting" y doesn't have any real meaning here - despite the blanket implication of the rules. Semantically the meaning of y is independent of its integral type; so the specified type of y cannot influence the generated code (unless there is a compiler bug). --Toby