GCC changing signdess whole expression

If you have any questions on programming, this is the place to ask them, whether you're a newbie or an experienced programmer. Discussion on programming in general is also welcome. We will help you with programming homework, but we will not do your work for you! Any porting requests must be made in Developmental Ideas.
Post Reply
Sigeru
DCEmu Newbie
DCEmu Newbie
Posts: 4
https://www.artistsworkshop.eu/meble-kuchenne-na-wymiar-warszawa-gdzie-zamowic/
Joined: Mon Aug 16, 2021 12:40 pm
Has thanked: 0
Been thanked: 0

GCC changing signdess whole expression

Post by Sigeru »

I have the following code:

Code: Select all

unsigned int m_font_timer = 0;

int Getsum(int p_top_left_x)
{
	int l_sum = (((p_top_left_x + (m_font_timer >> 2)) & 0x7) - 4 ) >> 2;
	
	if (l_sum < 0) l_sum = -l_sum;
	return l_sum;
}
Unless I explicitly cast m_font_timer to (signed int) this part of the expression becomes unsigned:

Code: Select all

= ((p_top_left_x + (m_font_timer >> 2)) & 0x7) - 4 );
Even though there is a "-4" and p_top_left_x is signed.

I wasn't expecting that part to turn into unsigned. I have this code running on two other platforms also with GCC (higher version than the one I'm using for DC, currently 4.7.3) and one with MSCV and they all keep the aforementioned expression as signed.

I don't know much about compilers, is this undefined behavior?
Is there any way the compiler would warn me about this? I have -Wall -Wformat=0 -Wextra -Wsign-compare but doesn't rise any warnings in that function.

Thanks!
nymus
DC Developer
DC Developer
Posts: 968
Joined: Tue Feb 11, 2003 4:12 pm
Location: In a Dream
Has thanked: 5 times
Been thanked: 5 times

Re: GCC changing signdess whole expression

Post by nymus »

Sorry this is a guess, but ill confirm later.

It could be that:
- you are not performing any comparisons, so even -wsign-compare won't warn, which exposes the potential reason as...
- unsigned has a higher range of (positive) values than signed, so the intermediate ops based on a signed type would potentially fit.
- if such an op was "officially" undefined, then the compiler vendor might only warn if it detected a potential for incorrect results, I.e. the compiler might be smart enough to see that ((int + (uint / 2) & 7) will always fit in uint intermediate.
- the largest op on char type would be: 255/2=127; 127+127=254; 254&7=6; 6-4=2
- maybe the compiler can see the input from elsewhere and is checking this?
- I'm not sure about unsigned vs signed implicit conversion, but it seems logical to me...

There was a question asked recently about assembly optimization where someone explained, in a very nice way, just how smart compilers are these days.
Last edited by nymus on Sat Sep 04, 2021 2:48 pm, edited 2 times in total.
behold the mind
inspired by Dreamcast
nymus
DC Developer
DC Developer
Posts: 968
Joined: Tue Feb 11, 2003 4:12 pm
Location: In a Dream
Has thanked: 5 times
Been thanked: 5 times

Re: GCC changing signdess whole expression

Post by nymus »

I can confirm that gcc will only warn when it suspects overflow will occur, depending on optimization and warning levels.

Using char as follows, gcc doesn't warn for '255' but does for '400'

Code: Select all

#include <stdio.h>

unsigned char m_font_timer = 255;

char Getsum(char p_top_left_x)
{
	char l_sum = (((p_top_left_x + (m_font_timer >> 2)) & 0x7) - 4 ) >> 2;
	
	if (l_sum < 0) l_sum = -l_sum;
	return l_sum;
}

int main(int argc, char *argv[])
{
	printf("getsum: %d\n", Getsum(255));
}
Here is the gcc warning documentation page.

https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

The relevant warning is -Wstrict-overflow=[1-5]. Level 1 is enabled by -Wall and catches the 400 value.

Here is a stack overflow answer pertaining to the unsigned/signed "usual arithmetic conversion"

https://stackoverflow.com/questions/178 ... d#17833338
behold the mind
inspired by Dreamcast
Post Reply