1. Trying to use gdb on OS X
- Posted by jaygade Jun 06, 2013
- 3606 views
Argh! I'm trying to figure out how to use gdb to figure out why some integers and bitwise ops aren't being handled correctly in Euphoria 4.05 on OS X. While I understand basic C, and basic machine layout, it can be a bit tough, and I'm very new to gdb debugging.
The worst part is that two times when I set a break point, it altered the execution of the program. The first time, I set a breakpoint in be_runtime.c at binary_op line 2337, with a condition of fn=56 (which is and_bits). When I stepped into the breakpoint, I ended up crashing with an access violation. When I cleard the fn=56 condition and then continued past the breakpoint, I seemed to get stuck in an infinite loop.
And to top it all off, even with the -g3 or -ggdb3 compiler switches, I don't get macro expansion, and this area of the interpreter is riddled with C macros. Then trying to figure out how to properly examine a Euphoria object in the debugger to see what it's doing while manually referring back to the macro definition. I find myself bouncing back and forth between terminal window and gvim source window.
Ah, well, just venting. I'll figure it out. But if anyone has any good gdb tips I'd be glad to hear them. Still, it makes me appreciate the Euphoria debugger, that's for sure! I have a couple ideas and there are still lots of websites to refer to. It's just a learning curve.
2. Re: Trying to use gdb on OS X
- Posted by mattlewis (admin) Jun 06, 2013
- 3560 views
Then trying to figure out how to properly examine a Euphoria object in the debugger to see what it's doing while manually referring back to the macro definition. I find myself bouncing back and forth between terminal window and gvim source window.
I often do things like:
$ print StdPrint( 1, _some_eu_object_12345, 1 )
That's equivalent to "? some_eu_object", so you get something reasonable. For strings, you can do things like "print EPuts(1, _seq_to_print )" (from memory...may not be exactly right).
Matt
3. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 06, 2013
- 3536 views
Thanks, I'll try that.
I figure I'm kind of in the "stick the screwdriver into the electrical socket" point of debugging, just trying to understand the tools better and how the source works. It's a lot to digest, even though I've fiddled with bits of the source before.
4. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 09, 2013
- 3547 views
So here's where I'm at. For some reason, the computer isn't passing along (-1) or (#FFFFFFFF) correctly in the function call for and_bits() in be_runtime.c. It only seems to fail when combined with a larger integer integer that is promoted to an atom, like #DEADBEE5.
Here's my test program:
include std/convert.e include std/math.e printf(1, "1. and_bits(#3EADBEE5, -1): Exp: %x | Res: %x\n", {#3EADBEE5, and_bits(#3EADBEE5, -1)}) printf(1, "2. and_bits(#DEADBEE5, -1): Exp: %x | Res: %x\n", {#DEADBEE5, and_bits(#DEADBEE5, -1)}) printf(1, "3. and_bits(-1, #DEADBEE5): Exp: %x | Res: %x\n", {#DEADBEE5, and_bits(-1, #DEADBEE5)}) printf(1, "4. and_bits(-1, -1): Exp: %x | Res: %x\n", {-1, and_bits(-1, -1)}) printf(1, "5. and_bits(-1, #FFFFFFFF): Exp: %x | Res: %x\n", {-1, and_bits(-1, #FFFFFFFF)}) printf(1, "6. and_bits(#FFFFFFFF, #FFFFFFFF): Exp: %x | Res: %x\n", {#FFFFFFFF, and_bits(#FFFFFFFF, #FFFFFFFF)}) printf(1, "7. and_bits(3735928549, -1): Exp: %x | Res: %x\n", {3735928549, and_bits(3735928549, -1)}) printf(1, "8. and_bits(3735928549, #DEADBEE5): Exp: %x | Res: %x\n", {3735928549, and_bits(3735928549, #DEADBEE5)}) -- Fails tests 2, 3, 5, 7
1. and_bits(#3EADBEE5, -1): Exp: 3EADBEE5 | Res: 3EADBEE5 2. and_bits(#DEADBEE5, -1): Exp: DEADBEE5 | Res: 0 3. and_bits(-1, #DEADBEE5): Exp: DEADBEE5 | Res: 0 4. and_bits(-1, -1): Exp: FFFFFFFF | Res: FFFFFFFF 5. and_bits(-1, #FFFFFFFF): Exp: FFFFFFFF | Res: 0 6. and_bits(#FFFFFFFF, #FFFFFFFF): Exp: FFFFFFFF | Res: FFFFFFFF 7. and_bits(3735928549, -1): Exp: DEADBEE5 | Res: 0 8. and_bits(3735928549, #DEADBEE5): Exp: DEADBEE5 | Res: DEADBEE5
Using gdb, I've traced the program to be_runtime.c line 1761.
Dand_bits (a=0x804e20, b=0xbfffe220) at be_runtime.c:1761 1761 return and_bits( (unsigned long)(a->dbl), (unsigned long)(b->dbl));
At this point, a->dbl and b->dbl have the correct values (but in double format). The cast should take care of that and put both values on the stack for the and_bits() call.
(gdb) p/x (unsigned long)(a->dbl) $58 = 0xdeadbee5 (gdb) p/x (unsigned long)(b->dbl) $59 = 0xffffffff
I can tell gdb to return at that point and it works correctly, so the asm code must be being generated incorrectly. Now, my understanding of x86 assembler is even worse than my understanding of C, and my understanding of floating point and vector instructions is non-existent.
Here's the disassembly for line 1761:
0x002c1498 <Dand_bits+13>: mov 0xc(%ebp),%eax 0x002c149b <Dand_bits+16>: movsd (%eax),%xmm0 0x002c149f <Dand_bits+20>: movapd %xmm0,%xmm1 0x002c14a3 <Dand_bits+24>: lea 0x26de9(%ebx),%eax 0x002c14a9 <Dand_bits+30>: movapd (%eax),%xmm0 0x002c14ad <Dand_bits+34>: movapd %xmm0,%xmm2 0x002c14b1 <Dand_bits+38>: cmplesd %xmm1,%xmm2 0x002c14b6 <Dand_bits+43>: lea 0x26df9(%ebx),%eax 0x002c14bc <Dand_bits+49>: movapd (%eax),%xmm0 0x002c14c0 <Dand_bits+53>: minsd %xmm0,%xmm1 0x002c14c4 <Dand_bits+57>: xorpd %xmm0,%xmm0 0x002c14c8 <Dand_bits+61>: maxsd %xmm0,%xmm1 0x002c14cc <Dand_bits+65>: lea 0x26de9(%ebx),%eax 0x002c14d2 <Dand_bits+71>: movapd (%eax),%xmm0 0x002c14d6 <Dand_bits+75>: andpd %xmm2,%xmm0 0x002c14da <Dand_bits+79>: subpd %xmm0,%xmm1 0x002c14de <Dand_bits+83>: cvttpd2dq %xmm1,%xmm1 0x002c14e2 <Dand_bits+87>: movdqa %xmm2,%xmm0 0x002c14e6 <Dand_bits+91>: psllq $0x1f,%xmm0 0x002c14eb <Dand_bits+96>: pxor %xmm0,%xmm1 0x002c14ef <Dand_bits+100>: movd %xmm1,%edx 0x002c14f3 <Dand_bits+104>: mov 0x8(%ebp),%eax 0x002c14f6 <Dand_bits+107>: movsd (%eax),%xmm0 0x002c14fa <Dand_bits+111>: movapd %xmm0,%xmm1 0x002c14fe <Dand_bits+115>: lea 0x26de9(%ebx),%eax 0x002c1504 <Dand_bits+121>: movapd (%eax),%xmm0 0x002c1508 <Dand_bits+125>: movapd %xmm0,%xmm2 0x002c150c <Dand_bits+129>: cmplesd %xmm1,%xmm2 0x002c1511 <Dand_bits+134>: lea 0x26df9(%ebx),%eax 0x002c1517 <Dand_bits+140>: movapd (%eax),%xmm0 0x002c151b <Dand_bits+144>: minsd %xmm0,%xmm1 0x002c151f <Dand_bits+148>: xorpd %xmm0,%xmm0 0x002c1523 <Dand_bits+152>: maxsd %xmm0,%xmm1 0x002c1527 <Dand_bits+156>: lea 0x26de9(%ebx),%eax 0x002c152d <Dand_bits+162>: movapd (%eax),%xmm0 0x002c1531 <Dand_bits+166>: andpd %xmm2,%xmm0 0x002c1535 <Dand_bits+170>: subpd %xmm0,%xmm1 0x002c1539 <Dand_bits+174>: cvttpd2dq %xmm1,%xmm1 0x002c153d <Dand_bits+178>: movdqa %xmm2,%xmm0 0x002c1541 <Dand_bits+182>: psllq $0x1f,%xmm0 0x002c1546 <Dand_bits+187>: pxor %xmm0,%xmm1 0x002c154a <Dand_bits+191>: movd %xmm1,%eax 0x002c154e <Dand_bits+195>: mov %edx,0x4(%esp) 0x002c1552 <Dand_bits+199>: mov %eax,(%esp) 0x002c1555 <Dand_bits+202>: call 0x2c1423 <and_bits>
By the time that and_bits() gets called,
Breakpoint 4, and_bits (a=3735928549, b=0) at be_runtime.c:1754 1754 a = a & b;
b is equal to 0 when it should be 0xFFFFFFFF.
gcc --version i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)
I may try to remove the --ffast-math option and see what happens. I should also take a closer look at test 4 and figure out why it it passing.
5. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 09, 2013
- 3362 views
Okay, I changed the code of Dand_bits() in be_runtime.c to this and it works:
object Dand_bits(d_ptr a, d_ptr b) /* double a AND b */ { /* return and_bits( (unsigned long)(a->dbl), (unsigned long)(b->dbl));*/ unsigned long long i1, i2; i1 = (unsigned long long)(a->dbl); i2 = (unsigned long long)(b->dbl); return and_bits((unsigned long)(i1), (unsigned long)(i2)); }
I'm not sure why the cast to long long is necessary, just that the conversion was failing somewhere. Even though I'm compiling to 32 bits, the OS is 32/64 hybrid. I do remember seeing a 64-bit representation of -1 in gdb (0xffffffffffffffff) in my initial investigations, I just don't remember exactly how I got it.
jason$ file build/eui build/eui: Mach-O executable i386 jason$ uname -a Darwin 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun 7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386 i386
6. Re: Trying to use gdb on OS X
- Posted by jimcbrown (admin) Jun 09, 2013
- 3391 views
Okay, I changed the code of Dand_bits() in be_runtime.c to this and it works:
I'm not sure why the cast to long long is necessary, just that the conversion was failing somewhere. Even though I'm compiling to 32 bits, the OS is 32/64 hybrid. I do remember seeing a 64-bit representation of -1 in gdb (0xffffffffffffffff) in my initial investigations, I just don't remember exactly how I got it.
This seems similiar to the and_bits() issue that was seen in ARM a few months back.
http://openeuphoria.org/forum/m/120902.wc
http://openeuphoria.org/forum/m/120910.wc
Considering that, unlike ARM, OSX runs on identical hardware to the x86/x64 version (and using the same compiler suite to boot!), even if the results are suppose to be formally undefined/implementation dependent, I'm not sure why we'd get different results. But that appears to be what is happening here.
7. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 09, 2013
- 3381 views
It's funny because the cast worked in gdb, just not in the code generated by gcc.
Okay, I'll undo my code and try to add a #ifdef EOSX to the existing #ifdef ARM or something and try that.
Wait a minute -- can't we cast to long and then to unsigned long? It's the cast of (-1) to unsigned long which is implementation defined right?
Idea which might be portable: in Dand_bits, cast to long, then in and_bits cast to unsigned long.
8. Re: Trying to use gdb on OS X
- Posted by jimcbrown (admin) Jun 09, 2013
- 3317 views
It's funny because the cast worked in gdb, just not in the code generated by gcc.
That is odd.
Okay, I'll undo my code and try to add a #ifdef EOSX to the existing #ifdef ARM or something and try that.
Well, we know that your code works. Maybe the right thing to do is to add your code around an #ifdef EOSX.
Wait a minute -- can't we cast to long and then to unsigned long? It's the cast of (-1) to unsigned long which is implementation defined right?
Idea which might be portable: in Dand_bits, cast to long, then in and_bits cast to unsigned long.
Perhaps, but so is any bitwise operation involving more than 32bits according to the other thread.
9. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 09, 2013
- 3349 views
Okay, I'll undo my code and try to add a #ifdef EOSX to the existing #ifdef ARM or something and try that.
Well, we know that your code works. Maybe the right thing to do is to add your code around an #ifdef EOSX.
Well, the better goal would be to make something that is completely portable without an #ifdef if possible. Second would probably be to define a macro that converts dbl to int reliably and wrap that in #ifdef if necessary.
Wait a minute -- can't we cast to long and then to unsigned long? It's the cast of (-1) to unsigned long which is implementation defined right?
Idea which might be portable: in Dand_bits, cast to long, then in and_bits cast to unsigned long.
Perhaps, but so is any bitwise operation involving more than 32bits according to the other thread.
Right, I meant to include all of the D*_bits() functions.
Unfortunately, casting to long first still doesn't work because I can't get just the bits of 0xDEADBEE5 from the double into a 32-bit number. It works for 0xFFFFFFFF. I have to cast to long long and then to unsigned long.
Can we guarantee that long long is 64 bits on all platforms, or use int64_t or something? If so, then I think that's a good portable solution which could fix ARM and OSX and still work on Windows/Linux because conversion from long long to unsigned long is defined to work properly.
Regardless, I'll continue testing.
10. Re: Trying to use gdb on OS X
- Posted by jimcbrown (admin) Jun 09, 2013
- 3329 views
Okay, I'll undo my code and try to add a #ifdef EOSX to the existing #ifdef ARM or something and try that.
Well, we know that your code works. Maybe the right thing to do is to add your code around an #ifdef EOSX.
Well, the better goal would be to make something that is completely portable without an #ifdef if possible.
Well, speed is an issue here. Doing these casts unnecessarily on x86 might slow it down (but then gain it might not).
Second would probably be to define a macro that converts dbl to int reliably and wrap that in #ifdef if necessary.
I can't see a downside to that.
Wait a minute -- can't we cast to long and then to unsigned long? It's the cast of (-1) to unsigned long which is implementation defined right?
Idea which might be portable: in Dand_bits, cast to long, then in and_bits cast to unsigned long.
Perhaps, but so is any bitwise operation involving more than 32bits according to the other thread.
Right, I meant to include all of the D*_bits() functions.
Unfortunately, casting to long first still doesn't work because I can't get just the bits of 0xDEADBEE5 from the double into a 32-bit number. It works for 0xFFFFFFFF. I have to cast to long long and then to unsigned long.
Ouch.
Can we guarantee that long long is 64 bits on all platforms, or use int64_t or something? If so, then I think that's a good portable solution which could fix ARM and OSX and still work on Windows/Linux because conversion from long long to unsigned long is defined to work properly.
Regardless, I'll continue testing.
I'd say we should use int64_t, as that's standardized, while 'long long' is really a GNU extension.
Thankfully we no longer have to worry about Watcom. (IIRC it doesn't support either, we'd have to use __int64 or something there if we needed a 64bit integer type for some reason.)
11. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 09, 2013
- 3336 views
long long is in C99; isn't it reasonable to expect it to be present? We compile on gcc 99% of the time on all platforms so we can almost assume it. I don't mind changing it to int64_t, though, but if so then we may want to follow suit throughout the whole source.
Right now, I have edited some macros and comments in execute.h, and the D*_bits functions in be_runtime.c. Some of the changes to execute.h may be controversial since it's such an old file used in so many places, but I don't think I broke anything. Especially since I didn't #ifdef them.
As far as performance is concerned, I thought about that but I thought that wrapping more decisions around the casts would probably be worse in the long run than just performing the casts only when necessary. Most of them are performed by hardware anyway. It would probably have to be measured for sure. I doubt the cast to long long takes much longer than the cast to unsigned long, however. I didn't really add any new casting. Regardless, compiler optimizations should count for that too.
As for submitting my changes, I would like them reviewed and tested by others before actually committing them into the mainline 4.0 branch. I especially want to make sure they run on other platforms as I expect them to. Should I just commit to 4.0, or should I make a new tag or branch, or what? I'll post diffs to the forum if I need to.
Regardless, I'll make some new eubins for OS X which hopefully someone can test.
Changes made: execute.h
- I changed the order of the #defines so they could reference each other.
- I reordered the object representation comment into numeric order and added hex values.
- I redefined INT_VAL and MAKE_INT to cast to long long, and then to long in order to preserve bits during conversion from double.
- I redefined MAKE_UINT to work in terms of INT_VAL and MAXINT macros.
be_runtime.c
- I added INT_VAL macro in place of manual casts in all D*_bits() functions.
Edit: Oh, I think that OpenWatcom 1.2 supports long long. http://www.openwatcom.org/index.php/C99_Compliance
12. Re: Trying to use gdb on OS X
- Posted by jimcbrown (admin) Jun 09, 2013
- 3324 views
long long is in C99; isn't it reasonable to expect it to be present? We compile on gcc 99% of the time on all platforms so we can almost assume it. I don't mind changing it to int64_t, though, but if so then we may want to follow suit throughout the whole source.
Hmm, that's a good point. I guess one day in the far future, there might be a platform that gcc supports with 256 sized ints where long long isn't 64bits ... but that's probably not worrying about right now.
but if so then we may want to follow suit throughout the whole source.
I felt that we should do this anyways, considering that only int64_t et al guarantee a particular size in bits. This is a huge job though.
As far as performance is concerned, I thought about that but I thought that wrapping more decisions around the casts would probably be worse in the long run than just performing the casts only when necessary.
Well, #ifdefs take no runtime performance hit at all. Just saying.
Most of them are performed by hardware anyway. It would probably have to be measured for sure. I doubt the cast to long long takes much longer than the cast to unsigned long, however. I didn't really add any new casting. Regardless, compiler optimizations should count for that too.
Thinking it over, you're probably right. It's not like hardware is getting any slower, anyways.
As for submitting my changes, I would like them reviewed and tested by others before actually committing them into the mainline 4.0 branch. I especially want to make sure they run on other platforms as I expect them to. Should I just commit to 4.0, or should I make a new tag or branch, or what? I'll post diffs to the forum if I need to.
A new branch is probably best... I don't think it's a big deal if you commit straight to 4.0 though. Someone needs to test on ARM and the other platforms to make sure nothing breaks anyways...
Edit: Oh, I think that OpenWatcom 1.2 supports long long. http://www.openwatcom.org/index.php/C99_Compliance
Hmm. Maybe I was thinking of MSVC...
13. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 10, 2013
- 3275 views
Continuing on.
Test results summary: FAIL: t_callc.e FAIL: t_filesys.e FAIL: t_io.e FAIL: t_locale.e FAIL: t_net_http.e FAIL: t_socket.e Files (run: 147) (failed: 6) (96% success)
First eutest failure is for t_callc.e
interpreting t_callc.e: CMD 'eui -d UNITTEST -batch t_callc.e ' failed: can open lib818.dll, expected: TRUE but got: FALSE /Users/jason/Dropbox/src/openeuphoria/stable/osx/include/std/unittest.e:454 in procedure assert() can open lib818.dll ... called from /Users/jason/Dropbox/src/openeuphoria/stable/osx/tests/t_callc.e:148 --> See ex.err failed: unittesting crashed, expected: TRUE but got: FALSE 13 tests run, 11 passed, 2 failed, 85% success FAILURE: t_callc.e EUPHORIA error with status 256 Test results summary: FAIL: t_callc.e Files (run: 1) (failed: 1) (0% success)
Looking through the history, this was a 4.1 thing? There is no lib818.dll or lib818.c included with the 4.0 branch.
t_filesys.e fails. It looks like it has something to do with wildcard expansion.
interpreting t_filesys.e: CMD '/Users/jason/bin/eui -d UNITTEST -batch t_filesys.e ' failed: dir() #5, expected: "anything but '-1'" but got: -1 137 tests run, 136 passed, 1 failed, 99% success FAILURE: t_filesys.e program died with status 256
t_io.e fails. It looks like seek doesn't work on OS X like it does on other BSDs.
interpreting t_io.e: CMD '/Users/jason/bin/eui -d UNITTEST -batch t_io.e ' failed: Seek STDIN, expected: 1 but got: 0 failed: Seek STDOUT, expected: 1 but got: 0 failed: Seek STDERR, expected: 1 but got: 0 71 tests run, 68 passed, 3 failed, 96% success FAILURE: t_io.e program died with status 256
More to come. Time to quit for tonight.
14. Re: Trying to use gdb on OS X
- Posted by mattlewis (admin) Jun 10, 2013
- 3245 views
I'd say we should use int64_t, as that's standardized, while 'long long' is really a GNU extension.
Thankfully we no longer have to worry about Watcom. (IIRC it doesn't support either, we'd have to use __int64 or something there if we needed a 64bit integer type for some reason.)
Yes, we do this on 4.1, but he's trying to get 4.0 working under OSX. This is very disappointing. Could you try the following program?
#include <stdio.h> void convert_double( double d ){ long l = (long) d; unsigned long u = (unsigned long) d; printf("%g -> %lx %lx\n", d, l, u ); } int main(){ convert_double( (double) -1 ); convert_double( (double) 0xdeadbeef ); return 0; }
I tried this two different ways:
$ gcc -m32 test.c -o test && ./test -1 -> ffffffff ffffffff 3.73593e+09 -> 80000000 deadbeef gcc -m64 test.c -o test && ./test -1 -> ffffffffffffffff ffffffffffffffff 3.73593e+09 -> deadbeef deadbeef
Matt
15. Re: Trying to use gdb on OS X
- Posted by jimcbrown (admin) Jun 10, 2013
- 3192 views
he's trying to get 4.0 working under OSX. This is very disappointing.
Now that you mention it, it is very strange ... how did this break? None of this code was changed in the 4.0 branch since 4.0.0 was released (and that was back when we had an OSX dev and a few OSX testers).
16. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 10, 2013
- 3215 views
I can't try the test until tonight (after 4:30pm PDT), although I'm sure I'll get similar results since that was what I was seeing in gdb from the compiled code. However when typing the casts directly into gdb they worked correctly.
I don't know how/why it broke. My OS X has an older GCC (I think I posted the version number up thread).
It now seems odd to me that the casting has always worked and was never broken in other versions; you would expect to see some odd errors whenever a pointer got stored as a double and then cast back into an int.
Whoever wrote the t_math.e bit tests and used a number that was sure to be cast as a double did a good job of catching this.
17. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 10, 2013
- 3211 views
I might be overthinking this a bit. I need to write this out to wrap my head around it:
1. double contains a positive value in the integer range (0x00000000 - 0xFFFFFFFF). Conversion to unsigned long should be converted correctly. Conversion to signed long should fail if the double is greater than 0x7FFFFFFF.
2. double contains a negative value in the integer range (0x80000000 - 0xFFFFFFFF). Conversion to unsigned long should fail (implementation defined - I was getting 0 when testing above). Conversion to signed long should be okay.
When going to unsigned (which we generally want for bit and address manipulation) the key difference is the sign of the double is handled.
Looking at Matt's example up above, it looks like test 1 holds true. Test 2 converts to both unsigned and signed correctly in Matt's example (for -1), but I think on OS X it will fail.
18. Re: Trying to use gdb on OS X
- Posted by mattlewis (admin) Jun 10, 2013
- 3191 views
I might be overthinking this a bit. I need to write this out to wrap my head around it:
1. double contains a positive value in the integer range (0x00000000 - 0xFFFFFFFF). Conversion to unsigned long should be converted correctly. Conversion to signed long should fail if the double is greater than 0x7FFFFFFF.
The basic problem is that the behavior is undefined from the perspective of the C specs, so it's very possible that the version of gcc on OSX started doing something different, pre-empting the normal x86 behavior.
You might want to look into the function we added for these casts for ARM in the default branch. I don't know if that's a better solution than using a 64-bit integer or not.
Matt
19. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 10, 2013
- 3290 views
Whichever method turns out to be better -- casting doubles to 64-bit int or wrapping some platform targets in an #ifdef() and adding a function, I will have learned things which will help me when I move on to --screwing up-- messing around with the 4.1 source.
And hopefully we'll have a more robust 4.06 release that people can continue to use.
20. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 10, 2013
- 3235 views
Could you try the following program?
#include <stdio.h> void convert_double( double d ){ long l = (long) d; unsigned long u = (unsigned long) d; printf("%g -> %lx %lx\n", d, l, u ); } int main(){ convert_double( (double) -1 ); convert_double( (double) 0xdeadbeef ); return 0; }
I tried this two different ways:
$ gcc -m32 test.c -o test && ./test -1 -> ffffffff ffffffff 3.73593e+09 -> 80000000 deadbeef gcc -m64 test.c -o test && ./test -1 -> ffffffffffffffff ffffffffffffffff 3.73593e+09 -> deadbeef deadbeef
Matt
OS X results:
jason$ ./test-dbl32 -1 -> ffffffff 0 3.73593e+09 -> 80000000 deadbeef jason$ ./test-dbl64 -1 -> ffffffffffffffff ffffffffffffffff 3.73593e+09 -> deadbeef deadbeef
21. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 10, 2013
- 3219 views
I think I can guarantee the reason is because the compiler is using SSE* instructions for the conversion.
When I compile and run this the program size is the same, but the instructions are different -- it uses the normal 387 math instructions for the conversion:
/* test-dbl-ll32.c */ #include <stdio.h> void convert_double( double d ){ long l = (long) (long long) d; unsigned long u = (unsigned long) (long long) d; printf("%g -> %lx %lx\n", d, l, u ); } int main(){ convert_double( (double) -1 ); convert_double( (double) 0xdeadbeef ); return 0; }
Results:
jason$ ./test-dbl-ll32 -1 -> ffffffff ffffffff 3.73593e+09 -> deadbeef deadbeef
When I compile Matt's original code with either -march=i386 or -mfpmath=387:
jason$ ./test-dbl -1 -> ffffffff ffffffff 3.73593e+09 -> 80000000 deadbeef
Interesting: with -march-i386, the code uses only 387 instructions. With -mfpmath=387, I still see some SSE* instructions in the disassembly.
I'm going to post this now, but out of curiosity I will try compiling test-dbl-ll32 with -mfpmath=sse2.
Edit: the -msse and -msse2 code are identical, and they both use 387 instructions. Both are identical to the version with only -m32.
Edit2: Compiling Matt's original code with -mnosse works, but is subtly different from -march=i386.
While this should be tested on ARM to make sure, I am still currently of the opinion that casting double to long long (or int64_t) prior to casting to unsigned long (or whatever portable type) is the correct thing to do for all platforms, and that eliminating an #ifdef and eliminating a separate function to convert doubles to int is the best solution.
22. Re: Trying to use gdb on OS X
- Posted by mattlewis (admin) Jun 11, 2013
- 3173 views
While this should be tested on ARM to make sure, I am still currently of the opinion that casting double to long long (or int64_t) prior to casting to unsigned long (or whatever portable type) is the correct thing to do for all platforms, and that eliminating an #ifdef and eliminating a separate function to convert doubles to int is the best solution.
In cases where that's big enough, yes it will work. The problem comes when you have a value that's too large for the target type. I hadn't even considered stuff like SSE instructions.
Matt
23. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 11, 2013
- 3101 views
In cases where that's big enough, yes it will work. The problem comes when you have a value that's too large for the target type. I hadn't even considered stuff like SSE instructions.
Matt
How do you mean?
I don't mean to convert all doubles to integers, only doubles capable of representing Euphoria integers and C pointers. I expect us to be in 64-bit land for quite some time to come, but if we do move to 128-bit systems, <stdint.h> has intmax_t instead of int64_t. Do any 32-bit targets not support some kind of int64_t?
Regardless, I was thinking about this more this morning. Since 4.0 should be relatively stable, it will probably be better for me to change configure so that OSX doesn't use sse (or maybe so that no x86 uses sse?) instead of making the larger change to the Euphoria source itself (except for commenting the behavior).
For 4.1 I'm not sure. Will 4.1 support both 32- and 64-bit platforms?
24. Re: Trying to use gdb on OS X
- Posted by jimcbrown (admin) Jun 11, 2013
- 3062 views
For 4.1 I'm not sure. Will 4.1 support both 32- and 64-bit platforms?
Yes. The prealphas already have for some time.
In cases where that's big enough, yes it will work. The problem comes when you have a value that's too large for the target type. I hadn't even considered stuff like SSE instructions.
Matt
How do you mean?
I don't mean to convert all doubles to integers, only doubles capable of representing Euphoria integers and C pointers. I expect us to be in 64-bit land for quite some time to come, but if we do move to 128-bit systems, <stdint.h> has intmax_t instead of int64_t.
It's already a problem on 64bit, since we have long doubles (or extended floating points) that could in theory hold larger integer values than a 64bit int. We need a way to do bitwise ops on these superlarge values...
Do any 32-bit targets not support some kind of int64_t?
Nothing modern that I can think of. Win32s maybe? Or really old 32bit DOS systems?
25. Re: Trying to use gdb on OS X
- Posted by mattlewis (admin) Jun 11, 2013
- 3062 views
In cases where that's big enough, yes it will work. The problem comes when you have a value that's too large for the target type. I hadn't even considered stuff like SSE instructions.
How do you mean?
I don't mean to convert all doubles to integers, only doubles capable of representing Euphoria integers and C pointers.
The question becomes, what do we do with input that is beyond that? It's very possible to get values too big for a 64-bit integer coming out of a euphoria atom that has some of its lowest 32 bits set. It's not really a common or particularly useful thing to do, but someone will do it and we should figure out how we're going to handle it. We could simply throw an error, or just say behavior is undefined when the value is too big.
Matt
26. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 11, 2013
- 3087 views
It's already a problem on 64bit, since we have long doubles (or extended floating points) that could in theory hold larger integer values than a 64bit int. We need a way to do bitwise ops on these superlarge values...
Does Euphoria use long doubles or extended floats internally now?
Right now, a normal double can hold an integer with 52 bits of precision, and those 52 bits will fit into a 64-bit type. So what I'm doing is taking that double with 52 bits, converting it to a 64 bit integer, and keeping lowest 32 bits. Then we can perform bitwise operations on the number. Remember, this is all internal to the C backend routines -- no double in these cases should be outside the range of a 32-bit integer because that is how the backend is written. (Although I don't think there are any explicit checks for this.)
Going forward maybe we need explicit routines to handle real<-->integer conversions for users regardless of the underlying type or size. I see that the library has to_integer() which returns either 0 or a default value if the number is out of bounds of a Euphoria 29-bit integer.
Do any 32-bit targets not support some kind of int64_t?
Nothing modern that I can think of. Win32s maybe? Or really old 32bit DOS systems?
I think it's really a compiler function; the compiler knows enough about the underlying machine to know whether it can be done in the processor or with a routine (like the old pre-387 days). I was looking into it and I saw that djgpp supports long long. I guess it depends on how old of machines we want to support with new code, or else suggest users use existing older software with older machines.
If and when we work with more embedded architectures, we'll have to solve whatever other problems crop up then.
The question becomes, what do we do with input that is beyond that? It's very possible to get values too big for a 64-bit integer coming out of a euphoria atom that has some of its lowest 32 bits set. It's not really a common or particularly useful thing to do, but someone will do it and we should figure out how we're going to handle it. We could simply throw an error, or just say behavior is undefined when the value is too big.
Matt
How does Euphoria handle converting doubles too large for a Euphoria integer now? (Tested on Windows with 4.05...) It fails with a type check error. Should this behavior change?
Remember, this is all for internal conversions between an integral atom stored as a C double and one stored as a C integer used either as an integer (for bitwise ops) or as a pointer. Not for user type conversions between arbitrary doubles and integers (at this point).
I created the change because I compiled 4.0 on OS X, found that I was failing the *bits() routines in t_math.e, and tried to figure out why. I found that certain doubles were not being converted to unsigned longs correctly. Although I didn't realize it at the time, the same problem seems to be related to the ARM port failing the same tests, only I came up with a different solution.
Converting between (larger) doubles and (larger) integers and performing bitwise operations on them is certainly a worthwhile goal, but it wasn't the main thrust of my bugfixing.
I realize that this only applies to 32-bit versions of Euphoria, not necessarily to 64-bit versions, but it will have to be accounted for if there is one codebase which can be compiled to both.
27. Re: Trying to use gdb on OS X
- Posted by mattlewis (admin) Jun 11, 2013
- 3060 views
It's already a problem on 64bit, since we have long doubles (or extended floating points) that could in theory hold larger integer values than a 64bit int. We need a way to do bitwise ops on these superlarge values...
Does Euphoria use long doubles or extended floats internally now?
64-bit euphoria does.
Right now, a normal double can hold an integer with 52 bits of precision, and those 52 bits will fit into a 64-bit type. So what I'm doing is taking that double with 52 bits, converting it to a 64 bit integer, and keeping lowest 32 bits. Then we can perform bitwise operations on the number. Remember, this is all internal to the C backend routines -- no double in these cases should be outside the range of a 32-bit integer because that is how the backend is written. (Although I don't think there are any explicit checks for this.)
The number stored in a double doesn't necessarily fit into a 64-bit integer. Remember, part of the double precision floating point value is an exponent.
The question becomes, what do we do with input that is beyond that? It's very possible to get values too big for a 64-bit integer coming out of a euphoria atom that has some of its lowest 32 bits set. It's not really a common or particularly useful thing to do, but someone will do it and we should figure out how we're going to handle it. We could simply throw an error, or just say behavior is undefined when the value is too big.
How does Euphoria handle converting doubles too large for a Euphoria integer now? (Tested on Windows with 4.05...) It fails with a type check error. Should this behavior change?
Remember, this is all for internal conversions between an integral atom stored as a C double and one stored as a C integer used either as an integer (for bitwise ops) or as a pointer. Not for user type conversions between arbitrary doubles and integers (at this point).
I'm not sure...but I don't feel like spending a whole lot of time figuring it out right now. As I said, it's kind of a weird situation that most people aren't going to encounter unless they're trying to break things. A type check error is probably appropriate, since it's probably a bug in their code that caused the problem in the first place.
I created the change because I compiled 4.0 on OS X, found that I was failing the *bits() routines in t_math.e, and tried to figure out why. I found that certain doubles were not being converted to unsigned longs correctly. Although I didn't realize it at the time, the same problem seems to be related to the ARM port failing the same tests, only I came up with a different solution.
Converting between (larger) doubles and (larger) integers and performing bitwise operations on them is certainly a worthwhile goal, but it wasn't the main thrust of my bugfixing.
I realize that this only applies to 32-bit versions of Euphoria, not necessarily to 64-bit versions, but it will have to be accounted for if there is one codebase which can be compiled to both.
Yes. In general, my approach for this sort of thing is to write some tests that fail and then try to figure out how to fix it. That way the issue is less likely to be forgotten, especially if I'm not sure how to get it fixed.
Matt
28. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 11, 2013
- 3126 views
Should I create a ticket? I don't want this issue to get lost either.
The number stored in a double doesn't necessarily fit into a 64-bit integer. Remember, part of the double precision floating point value is an exponent.
Yes, I know this, and I thought that I had addressed it a couple of times unless I'm missing some other point.
Currently 32-bit Euphoria has 29-bit integers, and it can perform bitwise operations on integers up to 32 bits in size. Bitwise operations on integers larger than 32 bits produce incorrect results.
Integers which are between 30 and 32 bits in size are stored as doubles. In order to preserve all 32 bits, the C backend has to cast the double to a C integer (long or unsigned long), perform the operation, and then store it back to a double.
The C standard says that casting a double to a long that is too big to fit into the long is undefined, and that casting double that is too big or is negative to an unsigned long is undefined.
There was a bug in the Euphoria C backend which relied upon this undefined behavior working a certain way. The fact that it worked until now is why it wasn't caught until we started moving into ARM and OS X platforms. The bug manifested itself as incorrect results from bitwise operations when the arguments were negative or were larger than 29 bits.
At this point I am not trying to address other behavior or larger values, although some very good points have been raised. Changing the D*bits() functions to work on the full range of integers that can be accurately represented is an interesting, but separate, problem.
29. Re: Trying to use gdb on OS X
- Posted by jimcbrown (admin) Jun 11, 2013
- 3078 views
The number stored in a double doesn't necessarily fit into a 64-bit integer. Remember, part of the double precision floating point value is an exponent.
Yes, I know this, and I thought that I had addressed it a couple of times unless I'm missing some other point.
Currently 32-bit Euphoria has 29-bit integers, and it can perform bitwise operations on integers up to 32 bits in size. Bitwise operations on integers larger than 32 bits produce incorrect results.
Integers which are between 30 and 32 bits in size are stored as doubles.
It's 31-bit. (Actually, it's 4 separate integer types, each which holds 29-bit values, but which when counted together map up to a full 31 unique bits. Roughly speaking, it's like we have one integer type to represent all positive integers and another to represent all negative integers, or something.)
Only 32bit values should need to be stored as doubles.
30. Re: Trying to use gdb on OS X
- Posted by jaygade Jun 11, 2013
- 3136 views
The number stored in a double doesn't necessarily fit into a 64-bit integer. Remember, part of the double precision floating point value is an exponent.
Yes, I know this, and I thought that I had addressed it a couple of times unless I'm missing some other point.
Currently 32-bit Euphoria has 29-bit integers, and it can perform bitwise operations on integers up to 32 bits in size. Bitwise operations on integers larger than 32 bits produce incorrect results.
Integers which are between 30 and 32 bits in size are stored as doubles.
It's 31-bit. (Actually, it's 4 separate integer types, each which holds 29-bit values, but which when counted together map up to a full 31 unique bits. Roughly speaking, it's like we have one integer type to represent all positive integers and another to represent all negative integers, or something.)
Only 32bit values should need to be stored as doubles.
Okay -- I should know that. smh.