llvm: NewFloat does not generate the expected output

Hello all,

I would first like to say thanks for an amazing library and I appreciate the hard work and dedication to support LLVM through Go.

However, using the library in a project of my own to generate LLVM instructions for different variables, I ran into a problem where using NewFloat did not produce the expected results. I will demonstrate using a C program as a comparison.

Using clang -S -emit-llvm main.c on the following program:

Input C Program:

int main() {
  float j = 1.1;

  return 0;
}

Produces the following store instruction for the float variable:

store float 0x3FF19999A0000000, float* %2, align 4

This is a 64 bit float with the last 28 bits dropped and converted to hex. (According to: http://lists.llvm.org/pipermail/llvm-dev/2011-April/039811.html)

However, attempting to generate the same instruction using this library:

mainBlock.NewStore(constant.NewFloat(value, types.Float), mainBlock.NewAlloca(types.Float))

where value is the float literal 1.1.

I obtained the following instruction:

store float 1.1, float* %1

Putting this into LLVM to generate assembly using:

llc -march=x86 -o main.expr.assembly main.expr.ll

Generates an error of:

llc: main.expr.ll:6:14: error: floating point constant invalid for type
        store float 1.1, float* %1

I can provide more information if needed, but a few questions:

  1. Is this expected behavior?
  2. Should this be expected behavior?
  3. If yes, why does this produce code that doesn’t work?

I can get it to work using types.Double, and if that is the solution then so be it for now, but I’d like to investigate if this is actually the expected output.

Again, Thanks for the work and dedication

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 42 (36 by maintainers)

Commits related to this issue

Most upvoted comments

so the drop is as expected?

Normally, yes - these are 64-bit floats and the precision does not cover that resolution. However, with double-double arithmetic, these values do not correspond to the same bits as just adding two floats together. According to Wikipedia (4), it should be possible to represent something like e-1074.

Just my thoughts after looking into it, I come across a few different things:

Taking a look at double-double precision on Wikipedia (4) and a Go double-double lib (2), it does not appear that the floats are simply just added together, they need to be dissected as each one does not contain symmetric significand and mantissa values. This is consistent with examples shown in (1) and (3) as well. Also, looking at Go’s math.big, it implements arbitrary precision arithmetic, not double-double so I don’t think we can be sure if that example is supposed to be correct.

I am not sure how much this helps, but just some thoughts and links for direction.

I agree with @dannypsnl, I don’t think this is important for the release. PPC is already very obscure and I would be surprised if anyone actually uses 128-bit floats in combination.

Is the ppc_fp128 precision loss a blocker for the v0.3 release? If not, I’ll try to have v0.3 tagged before the end of the year 😃

No, I don’t think it would be a blocker, postponing fixing is ok.

--- FAIL: TestNewFloatFromStringForFP128 (0.00s)
    const_float_test.go:82: "0xL00000000000000003fff000001000000": floating-point value string mismatch; expected "0xL00000000000000003fff000001000000", got "0xL00000000000000003FFF000001000000"
    const_float_test.go:82: "0xL00000000000000003fff000002000000": floating-point value string mismatch; expected "0xL00000000000000003fff000002000000", got "0xL00000000000000003FFF000002000000"
    const_float_test.go:82: "0xL00000000000000013fff000000000000": floating-point value string mismatch; expected "0xL00000000000000013fff000000000000", got "0xL00000000000000013FFF000000000000"
    const_float_test.go:82: "0xL000fffff00000000000fffff00000000": floating-point value string mismatch; expected "0xL000fffff00000000000fffff00000000", got "0xL000FFFFF00000000000FFFFF00000000"
    const_float_test.go:82: "0xL00ff00ff00ff00ff00ff00ff00ff00ff": floating-point value string mismatch; expected "0xL00ff00ff00ff00ff00ff00ff00ff00ff", got "0xL00FF00FF00FF00FF00FF00FF00FF00FF"
    const_float_test.go:82: "0xL08000000000000003fff000000000000": floating-point value string mismatch; expected "0xL08000000000000003fff000000000000", got "0xL08000000000000003FFF000000000000"
    const_float_test.go:82: "0xLf8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8": floating-point value string mismatch; expected "0xLf8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8", got "0xLF8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8"

I think these cases seem like just formatting problems.

The point was double-double arithmetic, it seems very like what we are looking for, two adjacent doubles!

Yes, this definitely seems to the what ppc_fp128 corresponds to. Thanks for doing the research on this issue 😃

From lib/Support/APFloat.cpp:

/* The IBM double-double semantics. Such a number consists of a pair of IEEE
   64-bit doubles (Hi, Lo), where |Hi| > |Lo|, and if normal,
   (double)(Hi + Lo) == Hi. The numeric value it's modeling is Hi + Lo.
   Therefore it has two 53-bit mantissa parts that aren't necessarily adjacent
   to each other, and two 11-bit exponents.
...
*/

Just register one, dannypsnl as GitHub ID.

Great. Do you know the four digits at the end of the user name? (e.g. dannypsnl#1234)

Updated, didn’t know it required that number www

Sure, I would take a look later. 😃

On Sat, Dec 7, 2019 at 10:38 AM Robin Eklind notifications@github.com wrote:

Support for the 0xL and 0xM hexadecimal floating-point representations have not yet been implemented.

@dannypsnl https://github.com/dannypsnl, would you like to take a look at this? 😃

To try, use e.g.

constant.NewFloatFromString(“0xL00000000000000000000000000000000”) constant.NewFloatFromString(“0xL00000000000000007FFF000000000000”)

constant.NewFloatFromString(“0xM00000000000000000000000000000000”) constant.NewFloatFromString(“0xM400F000000000000BCB0000000000000”)

I have summarized the 0xL and 0xM prefixes from test cases below: 0xL prefix

constant.NewFloatFromString(“0xL0”) constant.NewFloatFromString(“0xL00000000000000000000000000000000”) constant.NewFloatFromString(“0xL00000000000000000001000000000000”) constant.NewFloatFromString(“0xL00000000000000003FFF000000000000”) constant.NewFloatFromString(“0xL00000000000000003fff000001000000”) constant.NewFloatFromString(“0xL00000000000000003fff000002000000”) constant.NewFloatFromString(“0xL00000000000000004000000000000000”) constant.NewFloatFromString(“0xL00000000000000004001400000000000”) constant.NewFloatFromString(“0xL00000000000000004004C00000000000”) constant.NewFloatFromString(“0xL00000000000000004201000000000000”) constant.NewFloatFromString(“0xL00000000000000005001000000000000”) constant.NewFloatFromString(“0xL00000000000000007FFF000000000000”) constant.NewFloatFromString(“0xL00000000000000007FFF800000000000”) constant.NewFloatFromString(“0xL00000000000000008000000000000000”) constant.NewFloatFromString(“0xL00000000000000013fff000000000000”) constant.NewFloatFromString(“0xL00000000000000018000000000000000”) constant.NewFloatFromString(“0xL000fffff00000000000fffff00000000”) constant.NewFloatFromString(“0xL00ff00ff00ff00ff00ff00ff00ff00ff”) constant.NewFloatFromString(“0xL01”) constant.NewFloatFromString(“0xL08000000000000003fff000000000000”) constant.NewFloatFromString(“0xL300000000000000040089CA8F5C28F5C”) constant.NewFloatFromString(“0xL5000000000000000400E0C26324C8366”) constant.NewFloatFromString(“0xL8000000000000000400A24E2E147AE14”) constant.NewFloatFromString(“0xL999999999999999A3FFB999999999999”) constant.NewFloatFromString(“0xLEB851EB851EB851F400091EB851EB851”) constant.NewFloatFromString(“0xLF000000000000000400808AB851EB851”) constant.NewFloatFromString(“0xLf8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8”)

0xM prefix

constant.NewFloatFromString(“0xM00000000000000000000000000000000”) constant.NewFloatFromString(“0xM3DF00000000000000000000000000000”) constant.NewFloatFromString(“0xM3FF00000000000000000000000000000”) constant.NewFloatFromString(“0xM40000000000000000000000000000000”) constant.NewFloatFromString(“0xM400C0000000000300000000010000000”) constant.NewFloatFromString(“0xM400F000000000000BCB0000000000000”) constant.NewFloatFromString(“0xM403B0000000000000000000000000000”) constant.NewFloatFromString(“0xM405EDA5E353F7CEE0000000000000000”) constant.NewFloatFromString(“0xM4093B400000000000000000000000000”) constant.NewFloatFromString(“0xM41F00000000000000000000000000000”) constant.NewFloatFromString(“0xM4D436562A0416DE00000000000000000”) constant.NewFloatFromString(“0xM80000000000000000000000000000000”) constant.NewFloatFromString(“0xM818F2887B9295809800000000032D000”) constant.NewFloatFromString(“0xMC00547AE147AE1483CA47AE147AE147A”)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/llir/llvm/issues/31?email_source=notifications&email_token=AFH4GH4KOHCKPL22DMRLSQ3QXMEBXA5CNFSM4FCRXVZ2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEGF3ZOQ#issuecomment-562805946, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFH4GH5CVNWHY4NSXQ7EGGDQXMEBXANCNFSM4FCRXVZQ .

Hi Scott,

Thanks for the detailed report!

The representation of floating-point numbers is not yet up to pair with LLVM; so in that sense it is expected behaviour. It definitely should not be expected, and leading up to a 1.0 release of llir/llvm this has to be resolved. So, good that we now have an issue to refer to, for the implementation of floating-point values.

Any help implementing support for this would be greatly appreciated. We’ve given it a few attempts, but so far only cover a subset of the valid representations.

The most relevant code is currently located in https://github.com/llir/llvm/tree/master/internal/floats It could be expanded, more thoroughly tested and eventually made complete, for float80, float128, float16, etc.

The current implementation of floating-point constants is riddled with TODO notes, see for instance https://github.com/llir/llvm/blob/master/ir/constant/float.go#L50

case strings.HasPrefix(s, "0xK"):
	// TODO: Implement support for the 0xK floating-point representation format.

For your specific use case of float values, see https://github.com/llir/llvm/blob/master/ir/constant/float.go#L121

I wish we were in a better state handling floating-point values, but the work has just not yet been done. So, for this we warmly invite you to help take a stab at it 😃

I’d be glad to help you get acquainted with the code base if you’d like.

The end goal is to support encoding and decoding of these floating-point types (including their hexadecimal representations):

  • half: 16-bit floating point type
    • encode
    • decode
  • float: 32-bit floating point type
    • encode
    • decode
  • double: 64-bit floating point type
    • encode
    • decode
  • fp128: 128-bit floating point type (112-bit mantissa)
    • encode
    • decode
  • x86_fp80: 80-bit floating point type (x87)
    • encode
    • decode
  • ppc_fp128: 128-bit floating point type (two 64-bits, PowerPC)
    • encode
    • decode

Cheerful regards from a sunny Sweden, Robin

P.S. as a small side note, and as outlined in issue #29 for the upcoming release, we’ve been working recently to have a grammar that covers the entire LLVM IR language. As outlined, the repo https://github.com/mewmew/l has been used during the experimental phase and its grammar will be merged back into the llir/llvm repo once the code base matures. Some more work on float80 has been done at https://github.com/mewmew/l/tree/master/internal/float80 but I don’t think it’s complete. So you can simply take a look, and then use the resources that you find to come up with a more complete implementation.