LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 33 - C Backend cannot emit NAN's as global initializers
Summary: C Backend cannot emit NAN's as global initializers
Status: RESOLVED FIXED
Alias: None
Product: libraries
Classification: Unclassified
Component: Backend: C (show other bugs)
Version: 1.0
Hardware: All All
: P normal
Assignee: Bill Wendling
URL:
Keywords: missing-feature
Depends on:
Blocks:
 
Reported: 2003-10-12 02:33 PDT by Chris Lattner
Modified: 2010-03-06 14:00 PST (History)
4 users (show)

See Also:
Fixed By Commit(s):


Attachments
Configuration regen for "isinf" check and new NaN and Inf handling (211.78 KB, patch)
2004-07-13 01:09 PDT, Bill Wendling
Details
"isinf" support (895 bytes, patch)
2004-07-13 01:10 PDT, Bill Wendling
Details
Configuration regen for "isinf" check and new NaN and Inf handling (Redux) (212.01 KB, patch)
2004-07-13 14:49 PDT, Bill Wendling
Details
Patches to the (non-generated) configuration files to support the "IsInf" function. (2.10 KB, patch)
2004-07-16 01:20 PDT, Bill Wendling
Details
Code patch to add support for proper NaN and Inf handling (4.35 KB, patch)
2004-07-16 01:22 PDT, Bill Wendling
Details
IsInf functions (895 bytes, text/plain)
2004-07-16 01:24 PDT, Bill Wendling
Details
Patches to the (non-generated) configuration files to support the "IsInf" function (1.55 KB, patch)
2004-07-16 01:31 PDT, Bill Wendling
Details
Fix for converting FP value to Hex (643 bytes, patch)
2004-07-21 16:21 PDT, Bill Wendling
Details
Fix to get the mantissa to determine if this is a quiet or signalling NaN. (4.53 KB, patch)
2004-07-22 00:01 PDT, Bill Wendling
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Lattner 2003-10-12 02:33:21 PDT
The C backend cannot emit NAN's and other non-number values (such as infinity)
as global variable initializers.

This is tested by: test/Regression/CBackend/2003-10-12-NANGlobalInits.ll
Comment 1 Bill Wendling 2003-11-12 21:17:14 PST
Here is a proposed patch for NaN and Inf cases:

Index: Writer.cpp
===================================================================
RCS file: /var/cvs/llvm/llvm/lib/CWriter/Writer.cpp,v
retrieving revision 1.143
diff -a -u -r1.143 Writer.cpp
--- Writer.cpp  11 Nov 2003 22:41:32 -0000      1.143
+++ Writer.cpp  13 Nov 2003 03:14:05 -0000
@@ -457,14 +457,25 @@
       Out << "(*(" << (FPC->getType() == Type::FloatTy ? "float" : "double")
           << "*)&FPConstant" << I->second << ")";
     } else {
+      std::string Num;
 #if HAVE_PRINTF_A
       // Print out the constant as a floating point number.
       char Buffer[100];
+
       sprintf(Buffer, "%a", FPC->getValue());
-      Out << Buffer << " /*" << FPC->getValue() << "*/ ";
+      Num = Buffer;
 #else
-      Out << ftostr(FPC->getValue());
+      Num = ftostr(FPC->getValue());
 #endif
+
+      if (Num == "nan")
+        // The value is NaN
+        Out << "LLVM_NAN /*nan*/ ";
+      else if (Num == "inf")
+        // The value is Inf
+        Out << "LLVM_INF /*inf*/ ";
+      else
+        Out << Num;
     }
     break;
   }
@@ -580,6 +591,15 @@
   // If we aren't being compiled with GCC, just drop these attributes.
   Out << "#ifndef __GNUC__  /* Can only support \"linkonce\" vars with GCC */\n"
       << "#define __attribute__(X)\n"
+      << "#endif\n\n";
+
+  // Define NaN and Inf as GCC builtins if using GCC, as 0 otherwise.
+  Out << "#ifdef __GNUC__\n"
+      << "#define LLVM_NAN  __builtin_nan(\"\")\n"
+      << "#define LLVM_INF  __builtin_inf()\n"
+      << "#else\n"
+      << "#define LLVM_NAN  0\n"
+      << "#define LLVM_INF  0\n"
       << "#endif\n";
 }
Comment 2 Chris Lattner 2003-11-12 21:19:39 PST
Looks like a great first start.  However, there are multiple different kinds of
nan's (hence the argument to the builtin) and there are both + and - infinities.
 Can we support these as well?  :)

-Chris
Comment 3 Bill Wendling 2003-11-12 21:27:32 PST
Sure. :-)

-Bill
Comment 4 Chris Lattner 2003-11-12 21:28:46 PST
This bug was present in 1.0, but hopefully Bill will fix it for 1.1 :)

-Chris
Comment 5 Bill Wendling 2003-11-18 17:22:15 PST
When you ask about the multiple different types of builtins, are you refering to
the nans and huge_val? If so, I'm not sure when would be the best situation to
use these. (huge_val doesn't produce a warning like inf does and nans produces a
"signaling" NaN, which I have no clue what that is...)
Comment 6 Bill Wendling 2003-11-18 17:26:17 PST
Suggested patch:

cvs server: Diffing .
Index: Writer.cpp
===================================================================
RCS file: /var/cvs/llvm/llvm/lib/CWriter/Writer.cpp,v
retrieving revision 1.144
diff -a -u -r1.144 Writer.cpp
--- Writer.cpp  16 Nov 2003 22:06:14 -0000      1.144
+++ Writer.cpp  18 Nov 2003 23:24:43 -0000
@@ -451,20 +451,45 @@
   case Type::DoubleTyID: {
     ConstantFP *FPC = cast<ConstantFP>(CPV);
     std::map<const ConstantFP*, unsigned>::iterator I = FPConstantMap.find(FPC);
+
     if (I != FPConstantMap.end()) {
       // Because of FP precision problems we must load from a stack allocated
       // value that holds the value in hex.
       Out << "(*(" << (FPC->getType() == Type::FloatTy ? "float" : "double")
           << "*)&FPConstant" << I->second << ")";
     } else {
+      std::string Num;
 #if HAVE_PRINTF_A
       // Print out the constant as a floating point number.
       char Buffer[100];
+
       sprintf(Buffer, "%a", FPC->getValue());
-      Out << Buffer << " /*" << FPC->getValue() << "*/ ";
+      Num = Buffer;
 #else
-      Out << ftostr(FPC->getValue());
+      Num = ftostr(FPC->getValue());
 #endif
+
+      if (Num == "nan") {
+        // The value is NaN
+        if (FPC->getType() == Type::FloatTy)
+          Out << "LLVM_NANF /*nan*/ ";
+        else
+          Out << "LLVM_NAN /*nan*/ ";
+      } else if (Num == "inf") {
+        // The value is +Inf
+        if (FPC->getType() == Type::FloatTy)
+          Out << "LLVM_INFF /*inf*/ ";
+        else
+          Out << "LLVM_INF /*inf*/ ";
+      } else if (Num == "-inf") {
+        // The value is -Inf
+        if (FPC->getType() == Type::FloatTy)
+          Out << "-LLVM_INFF /*inf*/ ";
+        else
+          Out << "-LLVM_INF /*inf*/ ";
+      } else {
+        Out << Num;
+      }
     }
     break;
   }
@@ -580,6 +605,19 @@
   // If we aren't being compiled with GCC, just drop these attributes.
   Out << "#ifndef __GNUC__  /* Can only support \"linkonce\" vars with GCC */\n"
       << "#define __attribute__(X)\n"
+      << "#endif\n\n";
+
+  // Define NaN and Inf as GCC builtins if using GCC, as 0 otherwise.
+  Out << "#ifdef __GNUC__\n"
+      << "#define LLVM_NAN  __builtin_nan(\"\")   /* Double */\n"
+      << "#define LLVM_NANF __builtin_nanf(\"\")  /* Float  */\n"
+      << "#define LLVM_INF  __builtin_inf()       /* Double */\n"
+      << "#define LLVM_INFF __builtin_inff()      /* Float  */\n"
+      << "#else\n"
+      << "#define LLVM_NAN  ((double)0.0)         /* Double */\n"
+      << "#define LLVM_NANF 0.0F                  /* Float  */\n"
+      << "#define LLVM_INF  ((double)0.0)         /* Double */\n"
+      << "#define LLVM_INFF 0.0F                  /* Float  */\n"
       << "#endif\n";
 }
 
Comment 7 Chris Lattner 2003-11-18 17:32:37 PST
The patch is definitely looking better...

> When you ask about the multiple different types of builtins, are you refering to
> the nans and huge_val? If so, I'm not sure when would be the best situation to
> use these. (huge_val doesn't produce a warning like inf does and nans produces a
> "signaling" NaN, which I have no clue what that is...)

The basic idea is that the C backend should be able to translate ANY IEEE
bitpattern exactly to the C compiler.  Some bit-patterns represent numbers which
can be printed exactly as FP numbers, such as 1.0.  Others are normal numbers,
but cannot be printed exactly in decimal.  We handle these with C99 "hex FP"
extensions.

The class if bit patterns this bug is talking about is those classified as NANs,
SNANs, and INFs.  There is a whole class of these values (not just 3 for float
and double) and we want to translate ALL of them bit-exactly to the C compiler.

The argument to builtin_nan specifies _which_ NAN to generate.  If my memory is
correct, NAN's all have an exponent of 128 or something, but the mantissa can be
anything.  Correctly telling the C compiler about this requires using the string
argument to builtin_nan.

Floating point is a wierd disturbing thing.  I'd recommend skimming the article
"what every computer scientist ought to know about floating point".

-Chris
Comment 8 Chris Lattner 2004-01-18 14:15:21 PST
I don't think that Bill is looking at this bug anymore.  If you are, please feel
free to take back ownership of the bug Bill.

-Chris
Comment 9 Bill Wendling 2004-01-18 23:50:19 PST
Sorry. I dropped the ball on this. With moving and not having internet
connections, etc. I hadn't had the time to look into it further. I'll still be
working on it a bit but won't revert the ownership to me until I get something
working on my end.

Bill
Comment 10 Chris Lattner 2004-02-26 15:29:50 PST
Changing all of these bugs who do not have people looking at them to be assigned
to "unassignedbugs", indicating that if someone is feeling ambitious, they can
take ownership of the bug.

If I stole your bug, and you still want it, feel free to take ownership back.

-Chris
Comment 11 Chris Lattner 2004-02-26 15:31:53 PST
Changing all of these bugs who do not have people looking at them to be assigned
to "unassignedbugs", indicating that if someone is feeling ambitious, they can
take ownership of the bug.

If I stole your bug, and you still want it, feel free to take ownership back.

-Chris
Comment 12 Bill Wendling 2004-07-12 23:30:41 PDT
Here's another shot at this. It allows for bit-precise NaNs by feeding the
actual hex value of the FP into the __builtin_nan(...) built-in.

One thing. What should be the default for non-GNUC compilers? Right now, it's
just 0.0, which can't be correct.

Index: Writer.cpp
===================================================================
RCS file: /var/cvs/llvm/llvm/lib/Target/CBackend/Writer.cpp,v
retrieving revision 1.188
diff -a -u -r1.188 Writer.cpp
--- Writer.cpp  12 Jul 2004 23:37:18 -0000      1.188
+++ Writer.cpp  13 Jul 2004 04:25:17 -0000
@@ -556,14 +556,37 @@
       Out << "(*(" << (FPC->getType() == Type::FloatTy ? "float" : "double")
           << "*)&FPConstant" << I->second << ")";
     } else {
+                       std::string Num;
 #if HAVE_PRINTF_A
       // Print out the constant as a floating point number.
       char Buffer[100];
       sprintf(Buffer, "%a", FPC->getValue());
-      Out << Buffer << " /*" << FPC->getValue() << "*/ ";
+                       Num = Buffer;
 #else
-      Out << ftostr(FPC->getValue());
+      Num = ftostr(FPC->getValue());
 #endif
+
+                       if (Num == "nan") {
+                               // The value is NaN
+                               if (FPC->getType() == Type::FloatTy)
+                                       Out << "LLVM_NANF(\"" << Num << "\")
/*nan*/ ";
+                               else
+                                       Out << "LLVM_NAN(\"" << Num << "\")
/*nan*/ ";
+                       } else if (Num == "inf") {
+                               // The value is +Inf
+                               if (FPC->getType() == Type::FloatTy)
+                                       Out << "LLVM_INFF /*inf*/ ";
+                               else
+                                       Out << "LLVM_INF /*inf*/ ";
+                       } else if (Num == "-inf") {
+                               // The value is -Inf
+                               if (FPC->getType() == Type::FloatTy)
+                                       Out << "-LLVM_INFF /*inf*/ ";
+                               else
+                                       Out << "-LLVM_INF /*inf*/ ";
+                       } else {
+                               Out << Num;
+                       }
     }
     break;
   }
@@ -699,6 +722,49 @@
       << "#else\n"
       << "#define __ATTRIBUTE_WEAK__\n"
       << "#endif\n\n";
+
+       // Define NaN and Inf as GCC builtins if using GCC, as 0 otherwise
+       // From the GCC documentation:
+       //
+       //   double __builtin_nan (const char *str)
+       //
+       // This is an implementation of the ISO C99 function nan.
+       //
+       // Since ISO C99 defines this function in terms of strtod, which we do
+       // not implement, a description of the parsing is in order. The string is
+       // parsed as by strtol; that is, the base is recognized by leading 0 or
+       // 0x prefixes. The number parsed is placed in the significand such that
+       // the least significant bit of the number is at the least significant
+       // bit of the significand. The number is truncated to fit the significand
+       // field provided. The significand is forced to be a quiet NaN.
+       //
+       // This function, if given a string literal, is evaluated early enough
+       // that it is considered a compile-time constant.
+       //
+       //   float __builtin_nanf (const char *str)
+       //
+       // Similar to __builtin_nan, except the return type is float.
+       //
+       //   double __builtin_inf (void)
+       //
+       // Similar to __builtin_huge_val, except a warning is generated if the
+       // target floating-point format does not support infinities. This
+       // function is suitable for implementing the ISO C99 macro INFINITY.
+       //
+       //   float __builtin_inff (void)
+       //
+       // Similar to __builtin_inf, except the return type is float.
+       Out << "#ifdef __GNUC__\n"
+                 << "#define LLVM_NAN(NanStr)  __builtin_nan(NanStr)   /*
Double */\n"
+                 << "#define LLVM_NANF(NanStr) __builtin_nan(NanStr)   /* Float
*/\n"
+      << "#define LLVM_INF          __builtin_inf()         /* Double */\n"
+                 << "#define LLVM_INFF         __builtin_inff()        /* Float
*/\n"
+                 << "#else\n"
+                 << "#define LLVM_NAN(NanStr)  ((double)0.0)           /*
Double */\n"
+                 << "#define LLVM_NANF(NanStr) 0.0F                    /* Float
*/\n"
+      << "#define LLVM_INF          ((double)0.0)           /* Double */\n"
+                 << "#define LLVM_INFF         0.0F                    /* Float
*/\n"
+                 << "#endif\n";
 }
  
 bool CWriter::doInitialization(Module &M) {
Comment 13 Chris Lattner 2004-07-12 23:47:49 PDT
We can try out your patch, but don't close the bug until it's fixed in CVS :)

-Chris
Comment 14 Bill Wendling 2004-07-12 23:50:23 PDT
Sorry. I jumped the gun on that. I didn't realize what the "WORKSFORME" option
did. :-)

-bw
Comment 15 Chris Lattner 2004-07-13 00:11:57 PDT
The patch looks basically good, however I don't like the text comparisons like
this: 'if (Num == "nan")'.  Instead could you use the IsNan function from
Support/IsNan.h, and perhaps come up with a new function for infinity checking?

-Chris
Comment 16 Bill Wendling 2004-07-13 01:09:37 PDT
Created attachment 154 [details]
Configuration regen for "isinf" check and new NaN and Inf handling

This is the "cvs diff" of my CVS tree after running AutoRegen.sh. I also need
to add the IsInf.cpp file...That's coming up.
Comment 17 Bill Wendling 2004-07-13 01:10:29 PDT
Created attachment 155 [details]
"isinf" support

Here's the implementation of the "isinf" support.
Comment 18 Bill Wendling 2004-07-13 14:49:56 PDT
Created attachment 156 [details]
Configuration regen for "isinf" check and new NaN and Inf handling (Redux)

The previous patch doesn't compile...*sigh*
Comment 19 Chris Lattner 2004-07-16 00:51:48 PDT
K, two things

 *  Please resend just the isinf autoconf changes (without regenerated files) as
one change.  I am not one that can do this, but John or Brian can approve and
apply that.

 *  Once that is in, the writer.cpp change can go in.  Please reattach as a
separate file, and make sure that tabs don't sneak in :)

Thanks Bill, I'm sorry I dropped the ball on this one!

-Chris
Comment 20 Bill Wendling 2004-07-16 01:20:59 PDT
Created attachment 157 [details]
Patches to the (non-generated) configuration files to support the "IsInf" function.

Once applied, you should regenerate the configure code like normal.
Comment 21 Bill Wendling 2004-07-16 01:22:52 PDT
Created attachment 158 [details]
Code patch to add support for proper NaN and Inf handling

Adds header information to the MathExtras.h file and the code to the
CodeGen/CBackend/Writer.cpp file.
Comment 22 Bill Wendling 2004-07-16 01:24:03 PDT
Created attachment 159 [details]
IsInf functions

Supplies the "IsInf" functions. Resides in the llvm/lib/Support directory.
Comment 23 Bill Wendling 2004-07-16 01:31:14 PDT
Created attachment 160 [details]
Patches to the (non-generated) configuration files to support the "IsInf" function

This doesn't have the "MathExtras.h" patc included, which is already included
in #158.
Comment 24 Brian R. Gaeke 2004-07-19 22:00:42 PDT
I fear there's something yet wrong with this patch. When I try to compile the
C-Backend generated code for 2003-10-12-NANGlobalInits.ll with this patch
applied, I get an error from gcc:

101 brg@aluminum:~/llvm/test/Regression/CodeGen/CBackend% llvm-as < 2003-10-12-
NANGlobalInits.ll | llc -f -march=c  -o=- > foo.c
[...]
105 brg@aluminum:~/llvm/test/Regression/CodeGen/CBackend% gcc -S foo.c
foo.c:76: error: initializer element is not constant
foo.c:76: error: (near initialization for `NAN.field0')
Comment 25 Brian R. Gaeke 2004-07-19 22:13:01 PDT
114 brg@aluminum:~/llvm/test/Regression/CodeGen/CBackend% cat foo.c
struct l_unnamed0 { float field0; };
struct l_unnamed0 NAN = { __builtin_nan("nan") };
115 brg@aluminum:~/llvm/test/Regression/CodeGen/CBackend% gcc -c foo.c
foo.c:2: error: initializer element is not constant
foo.c:2: error: (near initialization for `NAN.field0')


It appears that the behavior of __builtin_nan doesn't exactly match its description
in the gcc manual! (the part about "considered a compile-time constant", that is...)
Comment 26 Bill Wendling 2004-07-19 22:24:32 PDT
I think the problem is in the use of "nan" as the initializer for
__builtin_nan(). If you use "" or a string with a hex value that's equivalent to
NaN, then it'll be okay:

#include <stdio.h>
#include <math.h>
                                                                                
struct {
  double d;
} S = { __builtin_nan("0x1.2f6a7ef9db22dp+5") };
                                                                                
int main()
{
  printf("S.d == %f\n", S.d);
}

works when compiling with g++. __builtin_nan("") works with gcc.
Comment 27 Brian R. Gaeke 2004-07-20 02:09:30 PDT
Ah... I see the problem now: I was testing on a non-HAVE_PRINTF_A platform
(Mac OS X). I'll check the patches in and try it on a Linux machine.
Comment 28 Brian R. Gaeke 2004-07-21 00:49:05 PDT
Actually, I was mistaken - Mac OS X does HAVE_PRINTF_A, but

             For a, A, e, E, f, F, g, and G conversions, positive and negative
             infinity are represented as inf and -inf respectively when using
             the lowercase conversion character, and INF and -INF respectively
             when using the uppercase conversion character.  Similarly, NaN is
             represented as nan when using the lowercase conversion, and NAN
             when using the uppercase conversion.

So you'll never get hex values for a NaN out of it. Crud.
Comment 29 Bill Wendling 2004-07-21 00:56:06 PDT
*@&%~!

Craptastic...Um...what to do then? Is there a platform-indenpendent way of
getting  the hex value of a FP number?
Comment 30 Chris Lattner 2004-07-21 00:57:52 PDT
Well we do currently assume that they are using IEEE 32/64 bit numbers.  You
could pull the bits out yourself...
Comment 31 Brian R. Gaeke 2004-07-21 15:59:09 PDT
union { double d; uint64_t u; } x; x.d = <doubleval>; sprintf (<buffer>, "0x%llx", x.u); 
Comment 32 Bill Wendling 2004-07-21 16:21:28 PDT
Created attachment 162 [details]
Fix for converting FP value to Hex

Thanks Brian for the suggestion.
Comment 33 Chris Lattner 2004-07-21 16:22:24 PDT
I thought that the __builtin_nan only took the nan mantissa value, not the 
whole FP #...

-Chris
Comment 34 Chris Lattner 2004-07-21 16:23:54 PDT
reassigning so I stop getting 2x mails :)
Comment 35 Brian R. Gaeke 2004-07-21 16:28:16 PDT
If __builtin_nan() is really supposed to be like C99 nan(), then it's
implementation-defined...
Comment 36 Chris Lattner 2004-07-21 16:28:58 PDT
Then why bother sending a bit pattern at all?

-Chris
Comment 37 Bill Wendling 2004-07-21 17:01:00 PDT
Good question. One thing is that __builtin_nan() seems to force it to be a
"quiet" NaN (at least according to the documentation). To do a "signalling" NaN,
you need to use __builtin_nans(). Since you're guaranteeing a format for FP#s,
we could grab the mantissa, determine what type of NaN it is, then issue that
builtin (either with "" or with a hex string for that platform-dependent
behaviour people want).
Comment 38 Chris Lattner 2004-07-21 17:03:18 PDT
Sounds groovy to me.  Getting QNAN vs SNAN right is important.  Distinguishing 
between various SNANs is much less so :)

-Chris
Comment 39 Bill Wendling 2004-07-21 17:09:29 PDT
<burns>Excellent!</burns> I'll see about doing this tonight. :-)

-bw
Comment 40 Bill Wendling 2004-07-22 00:01:19 PDT
Created attachment 163 [details]
Fix to get the mantissa to determine if this is a quiet or signalling NaN.
Comment 41 Bill Wendling 2004-07-22 00:02:33 PDT
Comment on attachment 163 [details]
Fix to get the mantissa to determine if this is a quiet or signalling NaN.

This should be it. :-) It now checks that the FP # starts with 0x7FF8 or 0x7FF4
for quiet and signalling NaNs (resp.). I also added another value to the
2003-10-12-NANGlobal.ll test.
Comment 43 Bill Wendling 2004-07-25 17:55:12 PDT
Hi Chris,

Did you see the newest patch I submitted to this bug? It should fix the
outputting of a "nan" for the LLVM_NAN tag...and also implement the signalling
and quiet NaNs.

-bw
Comment 44 Bill Wendling 2004-07-28 11:19:37 PDT
Brian,

Did you get a chance to try out this fix?

Fix to get the mantissa to determine if this is a quiet or signalling NaN. 
2004-07-22 00:01

-bw
Comment 45 Brian R. Gaeke 2004-08-25 14:02:52 PDT
So, I've applied the patches, and the test case seems to work now.
I'm wondering if anyone else has further tests to suggest? If not,
then I move that we close this bug.
Comment 46 Chris Lattner 2004-08-25 14:10:38 PDT
If it fixed the problem and causes no regressions, I say close it!  Thanks Bill
and Brian!

-Chris
Comment 47 Brian R. Gaeke 2004-08-25 15:09:25 PDT
Looks like we're done here.
Comment 48 Bill Wendling 2004-08-25 15:48:42 PDT
Thanks to Brian who caught a completely brain-dead mistake that I made.

-bw