x86_64: Fix compares with NaNs.
Comparisons with unordered doubles was broken, NaNs always compare unequal (and unordered) to everything, including to itself.
This commit is contained in:
		
							parent
							
								
									0394caf784
								
							
						
					
					
						commit
						2daae0dc99
					
				
					 2 changed files with 97 additions and 2 deletions
				
			
		| 
						 | 
					@ -87,6 +87,7 @@ void builtin_test(void);
 | 
				
			||||||
void weak_test(void);
 | 
					void weak_test(void);
 | 
				
			||||||
void global_data_test(void);
 | 
					void global_data_test(void);
 | 
				
			||||||
void cmp_comparison_test(void);
 | 
					void cmp_comparison_test(void);
 | 
				
			||||||
 | 
					void math_cmp_test(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int fib(int n);
 | 
					int fib(int n);
 | 
				
			||||||
void num(int n);
 | 
					void num(int n);
 | 
				
			||||||
| 
						 | 
					@ -594,6 +595,7 @@ int main(int argc, char **argv)
 | 
				
			||||||
    weak_test();
 | 
					    weak_test();
 | 
				
			||||||
    global_data_test();
 | 
					    global_data_test();
 | 
				
			||||||
    cmp_comparison_test();
 | 
					    cmp_comparison_test();
 | 
				
			||||||
 | 
					    math_cmp_test();
 | 
				
			||||||
    return 0; 
 | 
					    return 0; 
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2592,3 +2594,67 @@ void cmp_comparison_test(void)
 | 
				
			||||||
  compare_comparisons (&s);
 | 
					  compare_comparisons (&s);
 | 
				
			||||||
  return 0;
 | 
					  return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int fcompare (double a, double b, int code)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  switch (code) {
 | 
				
			||||||
 | 
					    case 0: return a == b;
 | 
				
			||||||
 | 
					    case 1: return a != b;
 | 
				
			||||||
 | 
					    case 2: return a < b;
 | 
				
			||||||
 | 
					    case 3: return a >= b;
 | 
				
			||||||
 | 
					    case 4: return a > b;
 | 
				
			||||||
 | 
					    case 5: return a <= b;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void math_cmp_test(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  double nan = 0.0/0.0;
 | 
				
			||||||
 | 
					  double one = 1.0;
 | 
				
			||||||
 | 
					  double two = 2.0;
 | 
				
			||||||
 | 
					  int comp = 0;
 | 
				
			||||||
 | 
					#define bug(a,b,op,iop,part) printf("Test broken: %s %s %s %s %d\n", #a, #b, #op, #iop, part)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* This asserts that "a op b" is _not_ true, but "a iop b" is true.
 | 
				
			||||||
 | 
					     And it does this in various ways so that all code generation paths
 | 
				
			||||||
 | 
					     are checked (generating inverted tests, or non-inverted tests, or
 | 
				
			||||||
 | 
					     producing a 0/1 value without jumps (that's done in the fcompare
 | 
				
			||||||
 | 
					     function).  */
 | 
				
			||||||
 | 
					#define FCMP(a,b,op,iop,code) \
 | 
				
			||||||
 | 
					  if (fcompare (a,b,code))    \
 | 
				
			||||||
 | 
					    bug (a,b,op,iop,1); \
 | 
				
			||||||
 | 
					  if (a op b) \
 | 
				
			||||||
 | 
					    bug (a,b,op,iop,2); \
 | 
				
			||||||
 | 
					  if (a iop b) \
 | 
				
			||||||
 | 
					    ; \
 | 
				
			||||||
 | 
					  else \
 | 
				
			||||||
 | 
					    bug (a,b,op,iop,3); \
 | 
				
			||||||
 | 
					  if ((a op b) || comp) \
 | 
				
			||||||
 | 
					    bug (a,b,op,iop,4); \
 | 
				
			||||||
 | 
					  if ((a iop b) || comp) \
 | 
				
			||||||
 | 
					    ; \
 | 
				
			||||||
 | 
					  else \
 | 
				
			||||||
 | 
					    bug (a,b,op,iop,5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Equality tests.  */
 | 
				
			||||||
 | 
					  FCMP(nan, nan, ==, !=, 0);
 | 
				
			||||||
 | 
					  FCMP(one, two, ==, !=, 0);
 | 
				
			||||||
 | 
					  FCMP(one, one, !=, ==, 1);
 | 
				
			||||||
 | 
					  /* Non-equality is a bit special.  */
 | 
				
			||||||
 | 
					  if (!fcompare (nan, nan, 1))
 | 
				
			||||||
 | 
					    bug (nan, nan, !=, ==, 6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Relational tests on numbers.  */
 | 
				
			||||||
 | 
					  FCMP(two, one, <, >=, 2);
 | 
				
			||||||
 | 
					  FCMP(one, two, >=, <, 3);
 | 
				
			||||||
 | 
					  FCMP(one, two, >, <=, 4);
 | 
				
			||||||
 | 
					  FCMP(two, one, <=, >, 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Relational tests on NaNs.  Note that the inverse op here is
 | 
				
			||||||
 | 
					     always !=, there's no operator in C that is equivalent to !(a < b),
 | 
				
			||||||
 | 
					     when NaNs are involved, same for the other relational ops.  */
 | 
				
			||||||
 | 
					  FCMP(nan, nan, <, !=, 2);
 | 
				
			||||||
 | 
					  FCMP(nan, nan, >=, !=, 3);
 | 
				
			||||||
 | 
					  FCMP(nan, nan, >, !=, 4);
 | 
				
			||||||
 | 
					  FCMP(nan, nan, <=, !=, 5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										33
									
								
								x86_64-gen.c
									
										
									
									
									
								
							
							
						
						
									
										33
									
								
								x86_64-gen.c
									
										
									
									
									
								
							| 
						 | 
					@ -429,7 +429,18 @@ void load(int r, SValue *sv)
 | 
				
			||||||
            gen_modrm(r, VT_LOCAL, sv->sym, fc);
 | 
					            gen_modrm(r, VT_LOCAL, sv->sym, fc);
 | 
				
			||||||
        } else if (v == VT_CMP) {
 | 
					        } else if (v == VT_CMP) {
 | 
				
			||||||
            orex(0,r,0,0);
 | 
					            orex(0,r,0,0);
 | 
				
			||||||
            oad(0xb8 + REG_VALUE(r), 0); /* mov $0, r */
 | 
						    if ((fc & ~0x100) != TOK_NE)
 | 
				
			||||||
 | 
					              oad(0xb8 + REG_VALUE(r), 0); /* mov $0, r */
 | 
				
			||||||
 | 
						    else
 | 
				
			||||||
 | 
					              oad(0xb8 + REG_VALUE(r), 1); /* mov $1, r */
 | 
				
			||||||
 | 
						    if (fc & 0x100)
 | 
				
			||||||
 | 
						      {
 | 
				
			||||||
 | 
						        /* This was a float compare.  If the parity bit is
 | 
				
			||||||
 | 
							   set the result was unordered, meaning false for everything
 | 
				
			||||||
 | 
							   except TOK_NE, and true for TOK_NE.  */
 | 
				
			||||||
 | 
							fc &= ~0x100;
 | 
				
			||||||
 | 
							o(0x037a + (REX_BASE(r) << 8));
 | 
				
			||||||
 | 
						      }
 | 
				
			||||||
            orex(0,r,0, 0x0f); /* setxx %br */
 | 
					            orex(0,r,0, 0x0f); /* setxx %br */
 | 
				
			||||||
            o(fc);
 | 
					            o(fc);
 | 
				
			||||||
            o(0xc0 + REG_VALUE(r));
 | 
					            o(0xc0 + REG_VALUE(r));
 | 
				
			||||||
| 
						 | 
					@ -1161,6 +1172,24 @@ int gtst(int inv, int t)
 | 
				
			||||||
    v = vtop->r & VT_VALMASK;
 | 
					    v = vtop->r & VT_VALMASK;
 | 
				
			||||||
    if (v == VT_CMP) {
 | 
					    if (v == VT_CMP) {
 | 
				
			||||||
        /* fast case : can jump directly since flags are set */
 | 
					        /* fast case : can jump directly since flags are set */
 | 
				
			||||||
 | 
						if (vtop->c.i & 0x100)
 | 
				
			||||||
 | 
						  {
 | 
				
			||||||
 | 
						    /* This was a float compare.  If the parity flag is set
 | 
				
			||||||
 | 
						       the result was unordered.  For anything except != this
 | 
				
			||||||
 | 
						       means false and we don't jump (anding both conditions).
 | 
				
			||||||
 | 
						       For != this means true (oring both).
 | 
				
			||||||
 | 
						       Take care about inverting the test.  We need to jump
 | 
				
			||||||
 | 
						       to our target if the result was unordered and test wasn't NE,
 | 
				
			||||||
 | 
						       otherwise if unordered we don't want to jump.  */
 | 
				
			||||||
 | 
						    vtop->c.i &= ~0x100;
 | 
				
			||||||
 | 
						    if (!inv == (vtop->c.i != TOK_NE))
 | 
				
			||||||
 | 
						      o(0x067a);  /* jp +6 */
 | 
				
			||||||
 | 
						    else
 | 
				
			||||||
 | 
						      {
 | 
				
			||||||
 | 
						        g(0x0f);
 | 
				
			||||||
 | 
							t = psym(0x8a, t); /* jp t */
 | 
				
			||||||
 | 
						      }
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
        g(0x0f);
 | 
					        g(0x0f);
 | 
				
			||||||
        t = psym((vtop->c.i - 16) ^ inv, t);
 | 
					        t = psym((vtop->c.i - 16) ^ inv, t);
 | 
				
			||||||
    } else if (v == VT_JMP || v == VT_JMPI) {
 | 
					    } else if (v == VT_JMP || v == VT_JMPI) {
 | 
				
			||||||
| 
						 | 
					@ -1469,7 +1498,7 @@ void gen_opf(int op)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            vtop--;
 | 
					            vtop--;
 | 
				
			||||||
            vtop->r = VT_CMP;
 | 
					            vtop->r = VT_CMP;
 | 
				
			||||||
            vtop->c.i = op;
 | 
					            vtop->c.i = op | 0x100;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            /* no memory reference possible for long double operations */
 | 
					            /* no memory reference possible for long double operations */
 | 
				
			||||||
            if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
 | 
					            if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue