Re: Where do I put .h files?

160 views
Skip to first unread message

Matt Godbolt

unread,
Mar 8, 2018, 5:34:35 PM3/8/18
to Larry Baker, compiler-explo...@googlegroups.com
Hi Larry,

Thanks for your email. Compiler Explorer doesn't support multiple files including each other - it works best on short snippets only. If you would like to see how the code is generated for code that relies on header files you have a couple of options:

1. Paste the code all together in one file: https://godbolt.org/g/1F6Tcs for example
2. Run a local instance of Compiler Explorer. This local instance will be able to read the files on your local hard disk, and so you can point the include path at your code (`-I/path/to/my/local/files`) and include files from there. That's a little more work, if you wish to do so there are some instructions here: https://github.com/mattgodbolt/compiler-explorer/#developing-or-running-a-local-instance

Please let us know if you have any other questions or thoughts!

Thanks, Matt :)

On Thu, Mar 8, 2018 at 4:26 PM Larry Baker <ba...@usgs.gov> wrote:
Matt,

Your Compiler Explorer web page is exactly what I am looking for to view the optimized assembler from various C compilers.  I have a really small test case that needs two header files.  I can open the .c source and the two .h files in separate editor panes, but the .c compile does not find the .h files, so it fails.  How do I compile my .c files on your Compiler Explorer that also need my .h files?

Thanks,

Larry Baker
US Geological Survey
650-329-5608
ba...@usgs.gov



Larry Baker

unread,
Mar 8, 2018, 5:47:23 PM3/8/18
to Matt Godbolt, Larry Baker, compiler-explo...@googlegroups.com
Matt,

Thanks for your quick response.

1. Paste the code all together in one file: https://godbolt.org/g/1F6Tcs for example

That's what I did.  The code is very small.  The .h files simply declare function prototypes.

Very handy tool.

In case you are interested (see below), I'm trying to figure out why LLVM (and, apparently GCC) generate what looks to me like useless code for a very simple line of code (lines 111 and 113): val /= 256;  I'm seeing sign extends and logical shifts (LLVM; the GCC code is slightly different) to val, and then a right shift that looks to me like it throws away what it just did!  I am using int32_t's and int's and integer literals.  I don't think I'm misunderstanding the semantics I want.  I am wondering if there are other registers/half registers that are implicitly being set up for the divide (sar eax, 8).  Only in 64-bit mode?  I'll experiment.  Can't waste much time on this.  More curious than anything about what I must be telling the compilers to do.

Thanks a lot.

Larry Baker
US Geological Survey
650-329-5608
ba...@usgs.gov

#include <stdint.h>
#include <time.h>

#ifndef __GCF_H__
#define __GCF_H__

typedef struct
{
int day;
int sec;
}
gcf_time;

typedef struct gcf_block_struct
{
unsigned char *buf;
int size;
int csize;

unsigned char *text;
int tlen;

char sysid[7];
char strid[7];
gcf_time start;
time_t estart;
double estart_offset; /* RL 2016 */
double sample_rate;
int format;
int records;
int samples;

int fic;
int ric;

int data[2048];
}
*gcf_block;

#define GCF_BLOCK_LEN 1024

#define GCF_EPOCH 627264000L

int gcf_dispatch (uint8_t * buf, int sz);

#endif /* __GCF_H__ */

#ifndef __GPUTIL_H__
#define __GPUTIL_H__

int gp_uint8 (uint8_t * buf);
int gp_uint16 (uint8_t * buf);
int gp_uint24 (uint8_t * buf);
int gp_uint32 (uint8_t * buf);
int gp_int8 (uint8_t * buf);
int gp_int16 (uint8_t * buf);
int gp_int24 (uint8_t * buf);
int gp_int32 (uint8_t * buf);
int gp_a_to_base36 (char *a);
char *gp_base36_to_a (int i);

#endif /* __GPUTIL_H__ */

void warning( const char * );

int
extract_24 (gcf_block b)
{
uint8_t *ptr = b->buf;
int *optr = b->data;

int32_t val;
int n = b->samples;

ptr += 16;

val = gp_int32 (ptr);
ptr += 4;

if ( ( ( val & 0xFF800000 ) != 0 ) &&
( ( val & 0xFF800000 ) != 0xFF800000 ) ) {
warning(("claimed 24 bit data isn't"));
return 1;
}

if (gp_int24 (ptr)) {
warning(("First difference is not zero"));
return 1;
}

/* Summation must be done using 24-bit arithmetic, i.e., modulo 2^24 with */
/* silent overflow and underflow. Without a 24-bit int datatype, we use */
/* 32 bit int summands, left shifting them (multiplying by 256) into the */
/* upper 24 bits of a 32-bit int for the modular arithmetic. The sum is */
/* then arithmetically right shifted and sign extended (divided by 256) */
/* into the proper position for the int output buffer. */

/* Note: >> cannot be used for the arithmetic right shift since the C90 */
/* standard (6.3.7) says whether >> is a logical or arithmetic shift for */
/* negative signed int values is implementation-defined. Thus, >> does */
/* not guarantee a sign-extended result. We use * 256 instead of << 8 */
/* for the left shift to be consistent with the use of / 256 instead of */
/* >> 8 for the right shift. Optimizing compilers generate the same code */
/* either way. */

val *= 256;
while (n--)
{
val += gp_int24 (ptr) * 256;
ptr += 3;
*(optr++) = val / 256;
}
val /= 256;

if ( ( ( val & 0xFF800000 ) != 0 ) &&
( ( val & 0xFF800000 ) != 0xFF800000 ) ) {
warning(("claimed 24 bit data isn't"));
return 1;
}

b->fic = val;
b->ric = gp_int32 (ptr);

if (b->fic != b->ric) {
warning(("Fic!=Ric"));
return 1;
}

return 0;
}

Larry Baker

unread,
Mar 8, 2018, 6:06:31 PM3/8/18
to Matt Godbolt, Larry Baker, compiler-explo...@googlegroups.com
The GCC code looks more like it is rounding the fractional part.  I bet that is what the code is doing.  The unoptimized code just does a idiv.  Since the optimized code is avoiding the division, it has to do the rounding first.  Interesting!

Larry Baker
US Geological Survey
650-329-5608
ba...@usgs.gov



Matt Godbolt

unread,
Mar 8, 2018, 6:35:26 PM3/8/18
to compiler-explo...@googlegroups.com, Larry Baker
It's a lot of fun investigating this kind of :) Just as a head-up it's usually best to share short URL links; pasting the code in is error-prone :) (I find it hard to read the code as it is with the loss of indentation...)

Divides are icky on x86 and have several implied register inputs, EDX and EAX are both input *and* output. If you mouse over the instruction you get a little detail, but right-clicking and choosing "View Asm doc" takes you to http://www.felixcloutier.com/x86/IDIV.html - which hopefully helps explains a bit more!

That said; when I pasted your code and used GCC trunk and -O3 I don't see any divide instructions: https://godbolt.org/g/3yivA7 -- what are you seeing? :)

--
You received this message because you are subscribed to the Google Groups "Compiler Explorer Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to compiler-explorer-di...@googlegroups.com.
To post to this group, send email to compiler-explo...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/compiler-explorer-discussion/E4233EE6-B8D5-4BD7-89C1-9433B115CAA6%40usgs.gov.
For more options, visit https://groups.google.com/d/optout.

Matt Godbolt

unread,
Mar 8, 2018, 6:36:08 PM3/8/18
to compiler-explo...@googlegroups.com, Larry Baker
Ah I see your other reply, apologies: yes the unoptimized compiler generates pretty much the dumbest code you've ever seen (on purpose). The optimized stuff avoids the divide and uses shifts (where it knows it's safe to do so).

Matt Godbolt

unread,
Mar 8, 2018, 6:42:30 PM3/8/18
to compiler-explo...@googlegroups.com, Larry Baker
Aha, and exactly as you said: (from the snippet above, my comments)

  test ebp, ebp   ; check sign of ebp
  lea ebx, [rbp+255] ; ebx = ebp + 255
  cmovns ebx, ebp  ; ebx = (ebp >= 0) ? ebp : ebx -- ie it's "ebp" or "ebp + 255" depending on the sign (for rounding as you say)
  sar ebx, 8  ; now do the shift

Of course, C "rounds to zero" for signed values, whereas a shift would round to negative infinity. Adding 255 if it was negative before shifting adjusts for this.

Compilers are very clever :)

Larry Baker

unread,
Mar 8, 2018, 6:46:36 PM3/8/18
to Matt Godbolt, Larry Baker, compiler-explo...@googlegroups.com
It is beyond me why C did not require arithmetic shifts for signed operands and logical shifts for unsigned operands.  Or rotates.  C is basically a portable assembly language anyway.  This is all so much easier (as is 24-bit integer arithmetic, or any precision integer arithmetic) in a real assembly language!  Not portable, of course. :(

Larry Baker
US Geological Survey
650-329-5608
ba...@usgs.gov



Larry Baker

unread,
Mar 8, 2018, 6:57:16 PM3/8/18
to Matt Godbolt, Larry Baker, compiler-explo...@googlegroups.com
  cmovns ebx, ebp  ; ebx = (ebp >= 0) ? ebp : ebx -- ie it's "ebp" or "ebp + 255" depending on the sign (for rounding as you say)

LLVM code is even more clever.  Nothing is conditional.  It shifts the sign bit around to fill the lower 8 bits of the "0 vs. 255" operand.

I'm also seeing cleverness in LLVM for my alternative.  I want to OR the 32-bit sign extension depending on whether the 24-bit value is signed:

/* val = val modulo 24 bits; i.e., extend the sign bit 23 to bits 24-31 */
val = ( val & 0x00FFFFFF ) | ( ( ( val >> 23 ) & 1 ) * 0xFF000000 );

I saw LLVM realized the & 1 result was either 0 or 1, negated that and ANDed that with the 0xFF000000 mask.  Voila, a multiple by 0 or 1!

GCC has its own cleverness by shifting the bit 23 sign bit into bit 31, then shifting that around to construct the ( sign * 0xFF000000 ) mask.

Impressive.

Thanks for providing such a useful tool.

Larry Baker
US Geological Survey
650-329-5608
ba...@usgs.gov



Matt Godbolt

unread,
Mar 9, 2018, 9:13:06 AM3/9/18
to compiler-explo...@googlegroups.com, Larry Baker
Thanks! This was a fun conversation :) Hope you've been able to discover what you needed!

Cheers, Matt :)

Reply all
Reply to author
Forward
0 new messages