Page 2 of 2

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 16th, 2018, 11:46 pm
by LameGuy64
I've been experiencing some really annoying bugs with GCC 7.2.0 as of late, most notably when optimizations are specified. The most significant bug I've encountered is if you compile the following:

Code: Select all

void myfunc() {

        ival = 0;

        somefunc( ival );

        do {

                ival++;
                ival = ival * 2;

                somefunc( ival );
                somefunc( ival/2 );

        } while( 1 );

}
It comes out as:

Code: Select all

myfunc:
        sw      $0,%gp_rel(ival)($28)
$L4:
        b       $L4
        nop
Which is basically an infinite loop. I've also had occurrences where certain arithmetic won't work correctly (ie. i |= 25 will replace the value of i instead of OR'ing the specified value into i) and a while loop would sometimes come out as an infinite loop which can sometimes be fixed by adding dummy code in it.

The worst part about this is I cannot seem to find anything on the internet regarding this issue probably because people haven't used GCC to compile MIPS code for years or those who do don't use any optimizations so they didn't notice these issues. I suspect these optimizer problems cropped up after GCC 4.8.x as anything above it has large portions of the toolchain rewritten to make use of C++.

I've tried to compile GCC 4.8.2 but I haven't been successful at it either because newer versions of the toolchain don't play well with 4.8.2 source or configure scripts failing with a unable to calculate EOF error.

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 17th, 2018, 5:18 am
by Xavi92
After ~7 years of using different versions of mipsel-unknown-elf (initially GCC 4, now 8.2.0), I have never found any compiler bugs. What assembly does GCC generate if you declare ival as volatile? It could be related to that.

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 17th, 2018, 8:01 am
by gwald
I'm not the best C coder, but isn't do while(1); an infinite loop?

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 17th, 2018, 8:31 am
by Yagotzirck
gwald wrote: December 17th, 2018, 8:01 am I'm not the best C coder, but isn't do while(1); an infinite loop?
I think he means that the compiler discards the dummy multiplication/addition operations followed by the two calls to somefunc(), rather than doing a 1:1 assembly translation... It's hard to guess since he didn't post the code for somefunc(), but I guess somefunc() does nothing more than some more dummy operations, the compiler notices that and just optimizes away all the dummy code, leaving just an infinite loop... but yeah, optimizations are bugs apparently :roll:
Xavi92 wrote: December 17th, 2018, 5:18 am After ~7 years of using different versions of mipsel-unknown-elf (initially GCC 4, now 8.2.0), I have never found any compiler bugs. What assembly does GCC generate if you declare ival as volatile? It could be related to that.
That's what I already suggested him to do in this thread's first page, but he plain ignored it and went on with the compiler optimizations-turned-into-bugs conspiracy; I guess I'm unknowingly cosplaying as Bruce Willis in The 6th Sense and whatever I said went unnoticed.

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 17th, 2018, 12:06 pm
by LameGuy64
Oh, so they're actually part of the optimizations? I didn't know it is that clever omitting variables that are never used elsewhere and turning very small functions into in-line code :oops:.

somefunc() simply sets a global variable:

Code: Select all

void somefunc(int value) {
	
	global_var = value;
	
}
I decided to do another test but this time with a volatile variable set to a hardcoded address (ie. an I/O port):

Code: Select all

char global_array[256];
int global_var;
int ival;

volatile int *ioport = (int*)0x1f800000;

void somefunc(int value) {
	
	global_var = value;
	*ioport = value;
	
}

void myfunc() {
	
	ival = 0;
	
	somefunc( ival );
	
	do {
		
		ival++;
		ival = ival * 2;
		
		somefunc( ival );
		somefunc( ival/2 );
		
	} while( 1 );

}
And the assembler output looks like this:

Code: Select all

somefunc:
        .frame  $sp,0,$31               # v
        .mask   0x00000000,0
        .fmask  0x00000000,0
        .set    noreorder
        .set    nomacro
        lw      $2,%gp_rel(ioport)($28)
        sw      $4,%gp_rel(global_var)($28)
        sw      $4,0($2)
        jr      $31
        nop

        .set    macro
        .set    reorder
        .end    somefunc
        .size   somefunc, .-somefunc
        .align  2
        .globl  myfunc
        .set    nomips16
        .set    nomicromips
        .ent    myfunc
        .type   myfunc, @function
myfunc:
        .frame  $sp,0,$31               # v
        .mask   0x00000000,0
        .fmask  0x00000000,0
        .set    noreorder
        .set    nomacro
        lw      $4,%gp_rel(ioport)($28)
        sw      $0,%gp_rel(ival)($28)
        sw      $0,0($4)
$L4:
        lw      $2,%gp_rel(ival)($28)
        nop
        addiu   $2,$2,1
        sll     $2,$2,1
        sw      $2,%gp_rel(ival)($28)
        sw      $2,0($4)
        lw      $3,%gp_rel(ival)($28)
        nop
        srl     $2,$3,31
        addu    $2,$2,$3
        sra     $2,$2,1
        sw      $2,%gp_rel(global_var)($28)
        sw      $2,0($4)
        b       $L4
        nop
Looks like it reduces somefunc() into just a few instructions and it calls it as in-line. I really didn't know that GCC's optimizer is this sophisticated.

I still remember running into a few compiler bugs with GCC like this bit of code from PSXSDK's SsUpload() would produce an infinite loop which I had to fix by placing some dummy operations in it:

Code: Select all

while( ( SPU_STATUS2 & 0x7ff ) != spu_status );
And there was also an instance where performing an OR operation behaved like I was doing an = operation instead:

Code: Select all

// To avoid confusion, these are from homebrew libs of my own that follow Sony's syntax

typedef struct {
	unsigned int	tag;
	unsigned int	code[1];
} DR_TPAGE;

DR_TPAGE *tpri = (DR_TPAGE*)nextpri;

setTPagePri( tpri, 0, 0, tim.prect->x, tim.prect->y );	// Sets tag and code[0]
tpri->code[0] |= 0x200;					// OR 0x200 to code[0] (behaved like = at one point)

Strangely, that OR bug I ran into above is gone now.

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 17th, 2018, 5:04 pm
by Xavi92
In fact, many variables in stock PSXSDK 0.5.99 missed the "volatile" qualifier and because of this reason it would not work with compiler optimizations. That's one of the first changes that I made on my own fork of PSXSDK: https://github.com/XaviDCR92/psxsdk-20150729
As a rule of thumb, always declare variables as "volatile" if they could be modified outside normal code execution flow (e.g.: from an interrupt handler) or if you really want to ensure the compiler does not optimize them away for any particular reason. Obviously, hardware registers must be then always declared as "volatile" since they can be modified by the hardware itself (e.g.: GPU ready flag), and the compiler will not be aware of that unless clearly specified.

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 18th, 2018, 12:11 am
by Yagotzirck
And there was also an instance where performing an OR operation behaved like I was doing an = operation instead:

Code: Select all

// To avoid confusion, these are from homebrew libs of my own that follow Sony's syntax

typedef struct {
	unsigned int	tag;
	unsigned int	code[1];
} DR_TPAGE;

DR_TPAGE *tpri = (DR_TPAGE*)nextpri;

setTPagePri( tpri, 0, 0, tim.prect->x, tim.prect->y );	// Sets tag and code[0]
tpri->code[0] |= 0x200;					// OR 0x200 to code[0] (behaved like = at one point)

Strangely, that OR bug I ran into above is gone now.
How is the struct nextpri points to allocated/initialized? If it's initialized to 0 (e.g. with calloc()) and there aren't any other assignments to code[0] between initialization and the OR operation above, the compiler might again have noticed that, and since 0 | 0x200 = 0x200 it simply replaced the OR operation with an assignment; otherwise, it's a strange thing indeed.

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 18th, 2018, 12:59 am
by LameGuy64
nextpri points to a primitive buffer (in this case a fixed sized global array), usually to a spot where the next primitive should be written to. setTPagePri sets tpri->tag to 0x01000000 and tpri->code[0] to 0xe100xxxx. Reason I wanted to OR 0x200 in it is to set a bit that enables dithering but at one time the OR operation sets tpri->code[0] to 0x00000200 turning it into an invalid 'nop' primitive.

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 18th, 2018, 1:26 am
by Yagotzirck
How are tpri->code[0]'s lower 2 bytes undefined (Unless the "xxxx" part means something else)? I initially guessed that you used LUI instruction, but AFAIK it zeroes out the lower 2 bytes :shrug

Anyway, any clue about how it got fixed, or did a simple recompilation without changing anything fix it? In the latter case that would be rather weird.

Re: MinGW32 compiled GCC 7.2.0 Toolchain (mipsel-unknown-elf)

Posted: December 20th, 2018, 11:58 am
by LameGuy64
The undefined values in the lower 2 bytes of tpri->code[0] are texture page coordinates which varies depending on the arguments specified on setTpagePri and the 4th byte being 0xe1 is the primitive code for setting a texture page which is also set by setTpagePri. I suppose you're unfamiliar with GPU packets on the PS1.

Not sure why but when I went to uncomment tpri->code[0] |= 0x200; later on the bug mysteriously disappeared.