Hello,

I think I came across a possible bug in gcc when using -O2 optimisation on using
the following program .

int global_var;

int switch_test(void) {
   int        sw = 4;
   int dmy    = (sw == 77) ? 11 : 0x101;
   global_var = dmy * ((sw >= 5) +1);

   switch (sw) {
     case 4:  return(0);   // okay
     default: return(99);  //error
   }
 }

int main(void) {
  printf("result=%d\n", switch_test()); return(0);
}               
 
When executed, the function returns "99", i.e. at the time the switch(sw) is
calculated, the wrong case is taken. 

The following assembly code is generated :

 * 13                            .global switch_test
 * 14                            .type    switch_test,function
 * 15                    switch_test:
 * 16                            @ args = 0, pretend = 0, frame = 0
 * 17                            @ frame_needed = 1, current_function_anonymous_args = 0
 * 18 0000 0DC0A0E1              mov     ip, sp
 * 19 0004 00D82DE9              stmfd   sp!, {fp, ip, lr, pc}
 * 20 0008 04B04CE2              sub     fp, ip, #4
 * 21 000c 0B30A0E3              mov     r3, #11      ; dmy = constant 11
 * 22 0010 08209FE5              ldr     r2, .L40     ; load adrs of global
 * 23 0014 6300A0E3              mov     r0, #99      ; load result = 99 (wrong !!!)
 * 24 0018 003082E5              str     r3, [r2, #0] ; store dmy = 257 into global
 * 25 001c 00A81BE9              ldmea   fp, {fp, sp, pc}
 * 27                    .L41:
 * 28                            .align  2
 * 29                    .L40:
 * 30 0024 00000000              .word   global

Obviously, gcc optimizes away all the constant expressions and produces optimum
code, but then decides to use the wrong case.

I have tried three different compilers, and all showed the problem :
 
> gcc version 2.95.2 20000220 (Debian GNU/Linux)                  (native onSA1110)   
> gcc version 2.95.3 20010125 (prerelease)                       (native on 
>skiffclusters2.handhelds.org)  
> gcc version egcs-2.91.60 19990113/philb (egcs-1.1.1 release)   (cross-compiler 
>hosted on x86)

Does anybody have any hint what I might be doing wrong (missing compiler
options, loose C-Syntax ?), or is this possibly a known issue that i could fix
with a newer compiler version ?

I think that this problem is somehow specific to the arm-gcc, because the error
depends on whether some constants are valid arm-immediatates (<8-bit> << n*2)
or not, even though they may eventually not be used in the code after
optimization !?   

 Thank you very much,
Regards klaus


In case that might help, here is a more verbose version of the program that
might provide some additional hints:


#include <stdio.h>
#include <stdlib.h>

int global = 3;

int switch_test(void)
{

#define INITIAL_SW 0x04      /* use any positive immediate arm-constant for INITIAL_SW
                              *  (tested: 0, 1, 2, 3, 4,  10, 99, 0x0039c000) and the 
error occurs
                              * larger constants (0x101, 1000000 and negative numbers 
do not produce errors */

#define MUST_BE_SW_PLUS_1 (INITIAL_SW + 1)  /* this must be INITIAL_SW + 1 */


#define ONLY_1_CAUSES_ERR 1
#define ANY_INTEGER  77
#define ANY_INTEGER1 11
#define NO_ARM_IMMEDIATE 0x101 /* 257, a constant that can not be representated as an 
arm immediate value ( b << n*2, where b is 8 bit)*/



#define ANY_OPERATOR_EXCEPT_LOG_OR &         /* also fails for <, ==, >=, &, |, &&, 
but not || */

#define ANY_OF_dmy_sw_global_result sw /* use either dmy, sw, global, local */

        int dmy;
        int result;

        int sw=INITIAL_SW; /* assign any integer to sw and never overwrite it later */
       
// I could not reproduce the error without using any of the following three
statements A, B, C 


// In statement A, a comparison using any binary operator (except "||" ) seems to be 
required. 
// This might mean that the problem does not occur if the second operand is not
evaluated ?
    

/* A */ dmy  = (ANY_OF_dmy_sw_global_result   ANY_OPERATOR_EXCEPT_LOG_OR    
ANY_INTEGER) 
                  ? ANY_INTEGER1       /* either ANY_INTEGER1 or NO_ARM_IMMEDIATE must 
not be a valid */
                  : NO_ARM_IMMEDIATE;  /* ARM constant (8 bit << n*2), otherwise no 
fault occurs */

/* In statement B, to produce the fault, I need 
 *   - ">=" Operator
 *   - compare <n> with <n+1> and then add one 
 *   - multiply ("dmy * ..", "dmy + .." will not do)
 *   - the result must be assigned to a global variable to avoid these statements 
 *     from being optimized away
 */

/* B */ global = dmy * ((sw >= MUST_BE_SW_PLUS_1) + ONLY_1_CAUSES_ERR); /* assigning 
to global ensures result of mult is used */ 

/* In statement C, sw is falsely evaluated only by the "switch"-operator.
 * Just assigning result = sw works correctly.
 * The error is not affected by extra case-statements or changing what the 
case-statements do. 
 */   

/* C */ switch( sw ) { /* only a switch causes errors, merely assigning b gives 
expected result */
        case INITIAL_SW: result=0;  break;  /* 0 = expected result, sw should still be 
= INITIAL_SW */
        default: result=99;             /* 99 = error       */
        }
 
        return (result);  /* 0 = no error, 99 = error (values do not matter) */
}

int main(void)
{
    int res = switch_test();

    if (res == 0) {
        printf ("okay, no Error\n");
    } else {
        printf ("####### Error: result=%d #########\n", res);
    }
    return(0);
}





 


                        

 --
Mobotix AG
Klaus Borchers
Luxemburgerstr. 6
D-67657 Kaiserslautern
Germany

Tel: +49 (631) 3033141
Fax: +49 (631) 3033190
E-Mail: [EMAIL PROTECTED]

_______________________________________________
http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm
Please visit the above address for information on this list.

Reply via email to