Pointer subtraction, 32-bit ARM, negative distance reported as postive - c

When performing subtraction of pointers and the first pointer is less than the second, I'm getting an underflow error with the ARM processor.
Example code:
#include <stdint.h>
#include <stdbool.h>
uint8_t * p_formatted_data_end;
uint8_t formatted_text_buffer[10240];
static _Bool
Flush_Buffer_No_Checksum(void)
{
_Bool system_failure_occurred = false;
p_formatted_data_end = 0; // For demonstration puposes.
const signed int length =
p_formatted_data_end - &formatted_text_buffer[0];
if (length < 0)
{
system_failure_occurred = true;
}
//...
return true;
}
The assembly code generated by the IAR compiler is:
807 static _Bool
808 Flush_Buffer_No_Checksum(void)
809 {
\ Flush_Buffer_No_Checksum:
\ 00000000 0xE92D4070 PUSH {R4-R6,LR}
\ 00000004 0xE24DD008 SUB SP,SP,#+8
810 _Bool system_failure_occurred = false;
\ 00000008 0xE3A04000 MOV R4,#+0
811 p_formatted_data_end = 0; // For demonstration purposes.
\ 0000000C 0xE3A00000 MOV R0,#+0
\ 00000010 0x........ LDR R1,??DataTable3_7
\ 00000014 0xE5810000 STR R0,[R1, #+0]
812 const signed int length =
813 p_formatted_data_end - &formatted_text_buffer[0];
\ 00000018 0x........ LDR R0,??DataTable3_7
\ 0000001C 0xE5900000 LDR R0,[R0, #+0]
\ 00000020 0x........ LDR R1,??DataTable7_7
\ 00000024 0xE0505001 SUBS R5,R0,R1
814 if (length < 0)
\ 00000028 0xE3550000 CMP R5,#+0
\ 0000002C 0x5A000009 BPL ??Flush_Buffer_No_Checksum_0
815 {
816 system_failure_occurred = true;
\ 00000030 0xE3A00001 MOV R0,#+1
\ 00000034 0xE1B04000 MOVS R4,R0
The subtraction instruction SUBS R5,R0,R1 is equivalent to:
R5 = R0 - R1
The N bit in the CPSR register will be set if the result is negative.
Ref: Section A4.1.106 SUB of ARM Architecture Reference Manual
Let:
R0 == 0x00000000
R1 == 0x802AC6A5
Register R5 will have the value 0x7FD5395C.
The N bit of the CPSR register is 0, indicating the result is not negative.
The Windows 7 Calculator application is reporting negative, but only when expressed as 64-bits: FFFFFFFF7FD5395C.
As an experiment, I used the ptrdiff_t type for the length, and the same assembly language was generated.
Questions:
Is this valid behavior, to have the result of pointer subtraction to
underflow?
What is the recommended data type to view the distance as negative?
Platform:
Target Processor: ARM Cortex A8 (TI AM3358)
Compiler: IAR 7.40
Development platform: Windows 7.

Is this valid behavior, to have the result of pointer subtraction to underflow?
Yes, because the behavior in your case is undefined. Any behavior is valid there. As was observed in comments, the difference between two pointers is defined only for pointers that point to elements of the same array object, or one past the last element of the array object (C2011, 6.5.6/9).
What is the recommended data type to view the distance as negative?
Where it is defined, the result of subtracting two pointers is specified to be of type ptrdiff_t, a signed integer type of implementation-defined size. If you evaluate p1 - p2, where p1 points to an array element and p2 points to a later element of the same array, then the result will be a negative number representable as a ptrdiff_t.

Although this is UB as stated in the other answer, most C implementations will simply subtract these pointers anyway ptrdiff_t size (or possibly using appropriate arithmetic for their word size which might also be different if both operands are near/far/huge pointers). The result should fit inside ptrdiff_t, which is usually a typedef-ed int on ARM:
typedef int ptrdiff_t;
So the issue with your code in this particular case will simply be that you are treating an unsigned int value as signed, and it doesn't fit. As specified in your question, the address of formatted_text_buffer is 0x802AC6A5, which fits inside unsigned int, but (int)0x802AC6A5 in two's complement form is actually a negative number (-0x7FD5395B). So subtracting a negative number from 0 will return a positive int as expected.
Signed 32-bit integer subtraction will work correctly if both operands are less than 0x7FFFFFFF apart, and it's reasonable to expect your arrays to be smaller than that:
// this will work
const int length = &formatted_text_buffer[0] - &formatted_text_buffer[100];
Or, if you really need to do subtract pointers which don't fit into signed 32-bit ints, use long long instead:
// ...but I doubt you really want this
const long long length = (long long)p_formatted_data_end -
(long long)&formatted_text_buffer[0];

Related

Odd behaviour using float pointer vs. creating a float variable on ARM

I have created an uint8_t array which has data packed into a dense structure which the microcontroller parses to extract variables. I have found a workaround for now, but I am curious as to why my previous code might have failed.
The following function is what I was trying to use:
/*
* Input Configuration Buffer:
* Byte 0:
* Bit 0: Input Joystick Left (0) or Input Joystick Right (1)
* Bit 1: Invert X-Axis (1)
* Bit 2: Invert Y-Axis (1)
* Bit 3: Output Joystick Left (0) or Output Joystick Right (1)
* Bits 4-7: Don't Care
* Byte 1: X-Deadzone 4th-Byte (float)
* Byte 2: X-Deadzone 3rd-Byte (float)
* Byte 3: X-Deadzone 2nd-Byte (float)
* Byte 4: X-Deadzone 1st-Byte (float)
* Byte 5: Y-Deadzone 4th-Byte (float)
* Byte 6: Y-Deadzone 3rd-Byte (float)
* Byte 7: Y-Deadzone 2nd-Byte (float)
* Byte 8: Y-Deadzone 1st-Byte (float)
*/
void Controller_Config_MapInputJoystickAsJoystick(Controller_HandleTypeDef *c, uint8_t *ic_buffer){
uint8_t js_in = GET_BIT(ic_buffer[0], 0);
uint8_t invert_x = GET_BIT(ic_buffer[0], 1);
uint8_t invert_y = GET_BIT(ic_buffer[0], 2);
uint8_t js_out = GET_BIT(ic_buffer[0], 3);
float *deadzone_x = (float *)(&ic_buffer[1]);
float *deadzone_y = (float *)(&ic_buffer[5]);
float val_x = invert_x ? -joysticks[js_in].x.val : joysticks[js_in].x.val;
float val_y = invert_y ? -joysticks[js_in].y.val : joysticks[js_in].y.val;
if((val_x > *deadzone_x) || (val_x < -*deadzone_x)){
c->joysticks._bits[js_out*2 + 0] += (int16_t)(val_x * -INT16_MIN);
}
if((val_y > *deadzone_y) || (val_y < -*deadzone_y)){
c->joysticks._bits[js_out*2 + 1] += (int16_t)(val_y * -INT16_MIN);
}
}
When this code runs, it seems to run fine until it tries to compare (val_x > *deadzone_x) || (val_x < -*deadzone_x). At this point, the microcontroller enters a HardFault condition.
This code is compiled as (asm):
ldr r3, [r7, #24]
vldr s15, [r3]
vldr s14, [r7, #16]
vcmpe.f32 s14, s15
vmrs APSR_nzcv, fpscr
bgt.n 0x80022c6 <Controller_Config_MapInputJoystickAsJoystick+230>
ldr r3, [r7, #24]
vldr s15, [r3]
vneg.f32 s15, s15
vldr s14, [r7, #16]
vcmpe.f32 s14, s15
vmrs APSR_nzcv, fpscr
bpl.n 0x8002302 <Controller_Config_MapInputJoystickAsJoystick+290>
In the assembly above, the microcontroller HardFaults on the instr, vldr s15, [r3] at which point, the s15 register is 1.00000238 and the r3 register is 0x802007d (This is the address of the float *deadzone_x).
I am concerned as to why this might be failing? I would love to use this function using a float * without casting a new float variable of a pointer. The instruction difference is considerable and would save a lot of processing time.
I don't know whether this is a general C programming misconception or perhaps something to do with the compiled ARM assembly? Any help would be appreciated.
I can provide you with any data, views or whatever might help. Thanks.
Edit 1:
This is workaround I am using, not much difference. I just dereference the pointer. You hate to C it.
/*
* Input Configuration Buffer:
* Byte 0:
* Bit 0: Input Joystick Left (0) or Input Joystick Right (1)
* Bit 1: Invert X-Axis (1)
* Bit 2: Invert Y-Axis (1)
* Bit 3: Output Joystick Left (0) or Output Joystick Right (1)
* Bits 4-7: Don't Care
* Byte 1: X-Deadzone 4th-Byte (float)
* Byte 2: X-Deadzone 3rd-Byte (float)
* Byte 3: X-Deadzone 2nd-Byte (float)
* Byte 4: X-Deadzone 1st-Byte (float)
* Byte 5: Y-Deadzone 4th-Byte (float)
* Byte 6: Y-Deadzone 3rd-Byte (float)
* Byte 7: Y-Deadzone 2nd-Byte (float)
* Byte 8: Y-Deadzone 1st-Byte (float)
*/
void Controller_Config_MapInputJoystickAsJoystick(Controller_HandleTypeDef *c, uint8_t *ic_buffer){
uint8_t js_in = GET_BIT(ic_buffer[0], 0);
uint8_t invert_x = GET_BIT(ic_buffer[0], 1);
uint8_t invert_y = GET_BIT(ic_buffer[0], 2);
uint8_t js_out = GET_BIT(ic_buffer[0], 3);
float deadzone_x = (float)(*(float *)(&ic_buffer[1]));
float deadzone_y = (float)(*(float *)(&ic_buffer[5]));
float val_x = invert_x ? -joysticks[js_in].x.val : joysticks[js_in].x.val;
float val_y = invert_y ? -joysticks[js_in].y.val : joysticks[js_in].y.val;
if((val_x > deadzone_x) || (val_x < -deadzone_x)){
c->joysticks._bits[js_out*2 + 0] += (int16_t)(val_x * -INT16_MIN);
}
if((val_y > deadzone_y) || (val_y < -deadzone_y)){
c->joysticks._bits[js_out*2 + 1] += (int16_t)(val_y * -INT16_MIN);
}
}
Debugger Output of Working Code
Debugger Output of Non-Working Code
Edit 2:
Ill-Alignment
I am concerned as to why this might be failing?
Very likely, #Gerhardh has it right in comments: your particular conversions of uint8_t * to type float * result in pointers that are not correctly aligned for type float (alignment requirements are type- and architecture-specific). C has this to say about that:
A pointer to an object type may be converted to a pointer to a
different object type. If the resulting pointer is not correctly
aligned for the referenced type, the behavior is undefined.
(C17, 6.3.2.3/7)
Your debugging demonstrates that the address in question is odd, whereas it is entirely plausible that your hardware requires float accesses to be aligned on 4-byte boundaries.
Moreover, accessing a slice of your byte array as if it were a float also violates C's strict aliasing rule (C17 6.5/7):
An object shall have its stored value accessed only by an lvalue
expression that has one of the following types:
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a
subaggregate or contained union), or
a character type.
Undefined behavior results on account of this violation as well. In a sense, this is the more fundamental issue, because if you were not violating the SAR then there would be no opportunity for a pointer conversion with a misaligned result.
I would love to use this function using a float * without casting a new float variable of a pointer. The instruction difference is considerable and would save a lot of processing time.
The difference between the code that breaks and the one that doesn't is not casting, but the fact that you immediately dereference the converted pointer, as opposed to storing it in a variable and dereferencing it later. The (float) casts are redundant. The immediate dereferencing does not make your program's behavior defined, but apparently it does induce the compiler to produce code that works correctly.
The difference in instruction count between machine code that does not work and machine code that does work is absolutely irrelevant. You do not save processing time in any useful sense by executing code that causes a hard fault. Furthermore, greater instruction count does not necessarily mean slower code.
In any case, the correct thing to do here as far as the C language is concerned is to copy the data out to your float byte by byte. If you have the C library available then memcpy() would be the conventional way to do this, and many compilers will recognize that usage and optimize the function call away:
float deadzone_x;
memcpy(&deadzone_x, ic_buffer + 1, sizeof(deadzone_x));
If you are building for a freestanding implementation that does not provide memcpy(), then I would write my own for this purpose:
inline void my_memcpy(void *dest, const void *src, size_t size) {
char *d = dest;
const char *s = src;
for (size_t i = 0; i < size; i++) {
*d++ = *s++;
}
}
Probably the compiler will indeed inline that for you (not guaranteed by the inline keyword), but if not then you could inline it manually.
Of course, if you were writing to the packed buffer then you would need to perform a similar copy in the other direction.
On a somewhat larger scale, it is possible that you would benefit from unpacking the array into a standard C structure, once, and then passing around and working with that instead of working directly on the packed buffer. This is of course very situation dependent.

Dividing a number represented by two words by a number represented by one? [duplicate]

This question already has answers here:
how to calculate (a times b) divided by c only using 32-bit integer types even if a times b would not fit such a type
(7 answers)
Closed 6 years ago.
I have two numbers, X and Y.
Y is a single unsigned integer primitive, e.g. long unsigned int. (In this case, there is no larger primitive to upcast to before performing the operation.)
X is represented by two primitives: X0 is the same type as Y and represents the low bits of X, and X1 is the same type and represents the high bits of X.
X / Y will always be representable using the same type as Y, i.e. the operation can be assumed not to overflow. (Because X is incidentally the product of two values of the same type as Y, one of which is less than or equal to Y.)
What is an efficient way to determine the result of this division?
You haven't specified the platform, which is crucial for the answer.
X / Y will always be representable using the same type as Y, i.e. the operation can be assumed not to overflow. (Because X is incidentally the product of two values of the same type
as Y, one of which is less than or equal to Y.)
On the x86-64 architecture, you could take advantage of that fact, by dividing RDX:RAX pair, so it's actually the same as you would have one "glued" 128 bit register for the dividend. Beware, though, that if above invariant doesn't always hold, then you will get division exception from CPU.
That said, one implementation is to use inline assembly, e.g.:
/* divides x1:x0 pair by y, assumes that quotient <= UINT64_MAX */
uint64_t udiv128_64_unsafe(uint64_t x0, uint64_t x1, uint64_t y)
{
__asm__ (
"divq\t%3"
: "=a" (x0)
: "0" (x0), "d" (x1), "rm" (y)
);
return x0;
}
which GCC 6.3.0 translates nicely (at -O1):
udiv128_64_unsafe:
mov rcx, rdx ; place the y (divisor) in RCX
mov rax, rdi ; low part of the dividend (x0)
mov rdx, rsi ; high part of the divided (x1)
divq rcx ; RAX = RDX:RAX / RCX
ret ; RAX is return value
For instance, for X = 65454567423355465643444545, Y = 86439334393432232:
#include <stdio.h>
#include <inttypes.h>
uint64_t udiv128_64_unsafe(uint64_t x0, uint64_t x1, uint64_t y) { ... }
int main(void)
{
printf("%" PRIu64 "\n", udiv128_64_unsafe(0x35c0ecb3fea1c941ULL, 0x36248bULL,
86439334393432232ULL));
return 0;
}
the given test driver program yields:
757231275
gcc has __int128 and unsigned __int128 for x86 architectures. I have successfully use it in the past to perform this kind of operations you describe. I am sure all major compilers have equivalents.
The "divide a 2 digit number by 1 digit, giving 1 digit quotient and remainder" is the basic primitive you need to synthesize larger divisions. If you don't have it (with digit == unsigned long int) available in your hardware, you need to use smaller digits.
In your case, split Y into 2 half-sized integers and X into 4 half-sized integers, and do the division that way.

Optimize a bit decoding operation in C

I have an unsigned 32 bit integer encoded in the following way:
the first 6 bits define the opcode
next 8 bits define a register
next 18 bits are a two's complement signed integer value.
I am currently decoding this number (uint32_t inst) using:
const uint32_t opcode = ((inst >> 26) & 0x3F);
const uint32_t r1 = (inst >> 18) & 0xFF;
const int32_t value = ((inst >> 17) & 0x01) ? -(131072 - (inst & 0x1FFFF)) : (inst & 0x1FFFF);
I can measure a significant overhead while decoding the value and I am quite sure it is due to the ternary operator (essentially an if statment) used to compare the sign an perform the negative operation.
Is there a way to perform value decoding in a faster way?
Your expression is more complicated than it needs to be, especially in needlessly involving the ternary operator. The following expression computes the same results for all inputs without involving the ternary operator.* It is a good candidate for a replacement, but as with any optimization problem, it is essential to test:
const int32_t value = (int32_t)(inst & 0x1FFFF) - (int32_t)(inst & 0x20000);
Or this variation on #doynax's suggestion along similar lines might be more optimizer-friendly:
const int32_t value = (int32_t)(inst & 0x3FFFF ^ 0x20000) - (int32_t)0x20000;
In each case, the casts avoid implementation-defined behavior; on many architectures they would be no-ops as far as the machine code is concerned. On those architectures, these expressions involve fewer operations in all cases than does yours, not to mention being unconditional.
Competitive alternatives involving shifting may also optimize well, but all such alternatives necessarily rely on implementation-defined behavior because of integer overflow of a left shift, a negative integer being the left-hand operand of a right shift, and / or converting an out-of-range value to a signed integer type. You will have to determine for yourself whether constitutes a problem.
* as compiled by GCC 4.4.7 for x86_64. The original expression invokes implementation-defined behavior for some inputs, so on other implementations the two expressions might compute different values for those inputs.
A standard (even though non-portable) practice is a left-shift followed by an arithmetic right-shift:
const int32_t temp = inst << 14; // "shift out" the 14 unneeded bits
const int32_t value = temp >> 14; // shift the number back; sign-extend
This involves a conversion from uint32_t to int32_t and a right-shift of a possibly negative int32_t; both operations are implementation-defined, i.e. unportable (work on 2's complement systems; pretty much guaranteed to work on any architecture). If you want to gain the best performance and willing to rely on implementation-defined behavior, you can use this code.
As a single expression:
const int32_t value = (int32_t)(inst << 14) >> 14;
Note: the following looks cleaner, will also typically work, but involves undefined behavior (signed integer overflow):
const int32_t value = (int32_t)inst << 14 >> 14;
Don't use it! (even though you probably won't receive any warning or error about it).
For ideal compiler output with no implementation-defined or undefined behaviour, use #doynax's 2's complement decoding expression:
value = (int32_t)((inst & 0x3FFFF) ^ 0x20000) - (int32_t)0x20000;
The casts make sure we're doing signed subtraction, rather than unsigned with wraparound and then assigning that bit-pattern to a signed integer.
This compiles to optimal asm on ARM, where gcc uses sbfx r1, r1, #0, #18 (signed bitfield-extract) to sign-extend bits [17:0] into a full int32_t register. On x86, it uses shl by 14 and sar by 14 (arithmetic shift) to do the same thing. This is a clear sign that gcc recognizes the 2's complement pattern and uses whatever is most optimal on the target machine to sign-extend the bitfield.
There isn't a portable way to make sure bitfields are ordered the way you want them. gcc appears to order bitfields from LSB to MSB for little-endian targets, but MSB to LSB for big-endian targets. You can use an #if to get identical asm output for ARM with/without -mbig-endian, just like the other methods, but there's no guarantee that other compilers work the same.
If gcc/clang didn't see through the xor and sub, it would be worth considering the <<14 / >>14 implementation which hand-holds the compiler towards doing it that way. Or considering the signed/unsigned bitfield approach with an #if.
But since we can get ideal asm from gcc/clang with fully safe and portable code, we should just do that.
See the code on the Godbolt Compiler Explorer, for versions from most of the answers. You can look at asm output for x86, ARM, ARM64, or PowerPC.
// have to put the results somewhere, so the function doesn't optimize away
struct decode {
//unsigned char opcode, r1;
unsigned int opcode, r1;
int32_t value;
};
// in real code you might return the struct by value, but there's less ABI variation when looking at the ASM this way (some would pack the struct into registers)
void decode_two_comp_doynax(struct decode *result, uint32_t inst) {
result->opcode = ((inst >> 26) & 0x3F);
result->r1 = (inst >> 18) & 0xFF;
result->value = ((inst & 0x3FFFF) ^ 0x20000) - 0x20000;
}
# clang 3.7.1 -O3 -march=haswell (enables BMI1 bextr)
mov eax, esi
shr eax, 26 # grab the top 6 bits with a shift
mov dword ptr [rdi], eax
mov eax, 2066 # (0x812)# only AMD provides bextr r32, r32, imm. Intel has to set up the constant separately
bextr eax, esi, eax # extract the middle bitfield
mov dword ptr [rdi + 4], eax
shl esi, 14 # <<14
sar esi, 14 # >>14 (arithmetic shift)
mov dword ptr [rdi + 8], esi
ret
You may consider using bit-fields to simplify your code.
typedef struct inst_type {
#ifdef MY_MACHINE_NEEDS_THIS
uint32_t opcode : 6;
uint32_t r1 : 8;
int32_t value : 18;
#else
int32_t value : 18;
uint32_t r1 : 8;
uint32_t opcode : 6;
#endif
} inst_type;
const uint32_t opcode = inst.opcode;
const uint32_t r1 = inst.r1;
const int32_t value = inst.value;
Direct bit manipulation often performs better, but not always. Using John Bollinger's answer as a baseline, the above structure results in one fewer instruction to extract the three values of interest on GCC (but fewer instructions does not necessarily mean faster).
const uint32_t opcode = ((inst >> 26) & 0x3F);
const uint32_t r1 = (inst >> 18) & 0xFF;
const uint32_t negative = ((inst >> 17) & 0x01);
const int32_t value = -(negative * 131072 - (inst & 0x1FFFF));
when negative is 1 -(131072 - (inst & 0x1FFFF)) and for 0: -(0 - (inst & 0x1FFFF)) which is equal to inst & 0x1FFFF.

Unexpected results while bit-shifting in C

I just hit some unexpected behavior while porting some code. I've boiled it down to this example:
#include <stdint.h>
#include <stdio.h>
uint32_t swap_16_p(uint8_t *value)
{
return (*(uint16_t*)value << 8 | *(uint16_t*)value >> 8);
}
int main()
{
uint8_t start[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0xEF };
printf("0x%08x\n", swap_16_p(start));
return 0;
}
On a Little Endian system like x86-64 I would expect this to print 0x0000dead but instead it prints 0x00addead. Looking at the assembly output makes the issue more clear:
uint32_t swap_16_p(uint8_t *value)
{
400506: 55 push %rbp
400507: 48 89 e5 mov %rsp,%rbp
40050a: 48 89 7d f8 mov %rdi,-0x8(%rbp)
return (*(uint16_t*)value << 8 | *(uint16_t*)value >> 8);
40050e: 48 8b 45 f8 mov -0x8(%rbp),%rax
400512: 0f b7 00 movzwl (%rax),%eax
400515: 0f b7 c0 movzwl %ax,%eax
400518: c1 e0 08 shl $0x8,%eax
40051b: 89 c2 mov %eax,%edx
40051d: 48 8b 45 f8 mov -0x8(%rbp),%rax
400521: 0f b7 00 movzwl (%rax),%eax
400524: 66 c1 e8 08 shr $0x8,%ax
400528: 0f b7 c0 movzwl %ax,%eax
40052b: 09 d0 or %edx,%eax
}
40052d: 5d pop %rbp
40052e: c3 retq
By using eax as the scratch area for doing the computation, the extra byte gets shifted past the 16-bit boundary with shl $0x8,%eax. I wouldn't have expected the computation to be treated as a 32-bit value until just before the return (as it would need to promote it to a uint32_t); similar behavior is seen when storing the value in a temporary uint32_t and the printing that instead.
Have I gone against (or improperly interpreted) the C spec, or is this a compiler bug (seems unlikely since this happens in both clang and GCC)?
The integer promotions are done at the "read side", therefore while the expression is evaluated. This means that after reading an integer value that has a smaller size than int resp. unsigned it is immediately converted:
The following may be used in an expression wherever an int or unsigned int may be used:
— An object or expression with an integer type whose integer conversion rank is less than or equal to the rank of int and unsigned int.
— A bit-field of type _Bool, int, signed int, or unsigned int.
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. 48)
48) The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses.
ISO/IEC 9899:TC3 6.3.1.1-2
Therefore
*(uint16_t*)value
is immediately converted to int and then shifted.
On a little endian system you are reading a unit16_t memory location that contains value 0xADDE. Before performing shifts, the value is promoted to int type, which is probably 32-bit wide on your platform, producing 0x0000ADDE. Shifts produce 0x00ADDE00 and 0x000000AD respectively. Bitwise OR produces 0x00ADDEAD.
Everything is as expected.
C language does not perform any arithmetic operations within types smaller than int (or unsigned int). Any smaller type is always promoted to int (or unsigned int) before performing the operation. This is what happens with your shifts. Your shifts are int shifts. C does not have "narrower" shifts. C does not have "narrower" additions and multiplications. C does not have "narrower" anything.
If you want a "narrower" shift (or any other operation) you have to simulate it by meticulously manually truncating the intermediate results in order to force them into a smaller type
(uint16_t) (*(uint16_t*) value << 8) | (uint16_t) (*(uint16_t*) value >> 8);
They will constantly spring back to int and you have to constantly beat them back into uint16_t.
This is what the compiler does:
uint32_t swap_16_p(uint8_t *value)
{
uint16_t v1 = *(uint16_t*)value; // -> 0x0000ADDE
int v2 = v1 << 8; // -> 0x00ADDE00
int v3 = v1 >> 8; // -> 0x000000AD
uint32_t v4 = v2 | v3; // -> 0x00ADDEAD
return v4;
}
So the result is well-justified.
Please note that v2 and v3 are results of integral promotion.
Let's look at your logic:
return (*(uint16_t*)value << 8 | *(uint16_t*)value >> 8);
*(uint16_t*)value is 0xADDE since your system is little-endian. (Subject to some caveats which I will mention below).
0xADDE << 8 is 0xADDE00, assuming you have 32-bit (or larger) int. Remember that left-shifting is equivalent to multiplying by a power of 2.
0xADDE >> 8 is 0xAD.
0xADDE00 | 0xAD is 0xADDEAD which is what you observed.
If you expected to 0xDEAD then you are going about it completely the wrong way. Instead the following code would work (and be endian-agnostic):
return (value[0] << 8) | value[1];
although my personal preference, since we are doing arithmetic, is to write it as value[0] * 0x100u + value[1].
*(uint16_t *)value has other problems. Firstly it will cause undefined behaviour if your system has an alignment restriction on integers. Secondly, it violates the strict aliasing rule: objects of type uint8_t may not be read through an lvalue of type uint16_t, again causing undefined behaviour.
If you are porting code that uses aliasing casts like this, I'd suggest disabling type-based aliasing optimization in your compiler until you fully understand the issues. In gcc the flag is -fno-strict-aliasing.

Strange behavior of bit operation in C

I'm studying C programming language and its bit operators.
I've written codes like below and I expected that the result of the codes are same.
But the reality is not.
#include <stdio.h>
#define N 0
int main() {
int n = 0;
printf("%d\n", ~0x00 + (0x01 << (0x20 + (~n + 1))));
printf("%d\n", ~0x00 + (0x01 << (0x20 + (~N + 1))));
return 0;
}
I assumed that the machine represent numbers as 2's complement on 32-bit.
They both have to be -1 which is all bits are 1 but first one is 0 and second one is -1.
I think both are exactly same code except whether using variable or constant.
I used gcc with option -m32 on Mac of i5 CPU.
What's wrong with it?
Thanks.
The short answer
You're evaluating the same expression in two different ways—once at runtime on an x86, and once at compile time. (And I assume you've disabled optimizations when you compile, see below.)
The long answer
Looking at the disassembled executable I notice the following: the argument to the first printf() is computed at runtime:
movl $0x0,-0x10(%ebp)
mov -0x10(%ebp),%ecx ; ecx = 0 (int n)
mov $0x20,%edx ; edx = 32
sub %ecx,%edx ; edx = 32-0 = 32
mov %edx,%ecx ; ecx = 32
mov $0x1,%edx ; edx = 1
shl %cl,%edx ; edx = 1 << (32 & 31) = 1 << 0 = 1
add $0xffffffff,%edx ; edx = -1 + 1 = 0
The shift is performed by an x86 SHL instruction with %cl as its operator. As per Intel manual: "The destination operand can be a register or a memory location. The count operand can be an immediate value or register CL. The count is masked to five bits, which limits the count range to 0 to 31. A special opcode encoding is provided for a count of 1."
For the above code, that means that you're shifting by 0, thus leaving the 1 in place after the shift instruction.
In contrast, the argument to the second printf() is essentially a constant expression that is computed by the compiler, and the compiler does not mask the shift amount. It therefore performs a "correct" shift of a 32b value: 1<<32 = 0 It then adds -1 to that—and you see the 0+(-1) = -1 as a result.
This also explains why you see only one warning: left shift count >= width of type and not two, as the warning stems from the compiler evaluating the shift of a 32b value by 32 bits. The compiler did not issue any warning regarding the runtime shift.
Reduced test case
The following is a reduction of your example to its essentials:
#define N 0
int n = 0;
printf("%d %d\n", 1<<(32-N) /* compiler */, 1<<(32-n) /* runtime */);
which prints 0 1 demonstrating the different results of the shift.
A word of caution
Note that the above example works only in -O0 compiled code, where you don't have the compiler optimize (evaluate and fold) constant expressions at compile time. If you take the reduced test case and compile it with -O3 then you get the same and correct results 0 0 from this optimized code:
movl $0x0,0x8(%esp)
movl $0x0,0x4(%esp)
I would think that if you change the compiler options for your test, you will see the same changed behavior.
Note There seems to be a code-gen bug in gcc-4.2.1 (and others?) where the runtime result is just off 0 8027 due to a broken optimization.
A simplified example
unsigned n32 = 32;
printf("%d\n", (int) sizeof(int)); // 4
printf("%d\n", (0x01 << n32)); // 1
printf("%d\n", (0x01 << 32)); // 0
You get UB in (0x01 << n32) as the shift >= width of int. (Looks like only 5 lsbits of n32 participated in the shift. Hence a shift of 0.)
You get a UB in (0x01 << 32) as the shift >= width of int. (Looks like complier performed the math with more bits.) This UB could have been the same as above.

Resources