G++ could optimize ASM code more

Daniel Marschall daniel-marschall@viathinksoft.de
Wed May 9 06:29:00 GMT 2012


Hello dear people,

As I was optimizing my program, I found a few things which looked odd 
to me in the assembler code.

I am on an AMD x64_32 box running Debian Squeeze, GCC: (Debian 4.4.5-8) 
4.4.5.

I am using "-O3 -mtune=native" for optimization.

=================================================================

(1)

I was using a performane-critical for-loop (very tight) which is 
iterated a few million times. Of couse, it is important that this for 
loop uses as less OP codes as possible since it makes a difference in 
such great dimensions. Also, I was using OpenMP.

I marked the loop with a "NOP" margin and looked if the loop contents 
could be optimized. I found out that "-O3" did not optimize a special 
case where I needed to do a manual cast to make the loop 1 OP code 
shorter. Please look at the following short example which reproduces the 
scenario:

(I do not know if this behavior can be reproduced without using OpenMP)

#include <stdlib.h>
#include <stdio.h>
#include <strings.h>

// Please toggle and compare ASM output ("nop" markers). Using 
"addcast" makes output 1 OP code shorter.
#define addcast

int main(void) {
	volatile int idx_a = 0;
	volatile int idx_b = 1;

	unsigned char *a;
	a = (unsigned char*)malloc(2);
	a[0]=3;
	a[1]=9;

	unsigned long long int c = 0;

	#pragma omp parallel for firstprivate(a) reduction(+:c)
	for (int i = 0; i < 2; ++i) {
		__asm__ ("nop"); // marker
		#ifdef addcast
		// Contains a cast to "unsigned long long int", which was not done by 
"-O3"
		// This cast makes the output 1 OP code shorter
		// imulq   %rdi, %rdx      # tmp80, tmp81
		// addq    %rdx, %rcx      # tmp81, c
		c += (unsigned long long int)a[idx_a] * a[idx_b];
		#else
		// Using "-O3", it produces 1 OP code which could be optimized away
		// imull   %edi, %edx      # tmp80, tmp81  <-- the compiler should 
use imulq instead of imull
		// movslq  %edx,%rdx       # tmp81, tmp82  <-- not neccessary... 
BETTER: optimize away using imulq !
		// addq    %rdx, %rcx      # tmp82, c
		c += a[idx_a] * a[idx_b];
		#endif
		__asm__ ("nop"); // marker
	}

	free(a);

	return c;
}

=================================================================

(2)

Compiling following program:

#include <stdio.h>
#include <strings.h>
int main(void) {
         volatile unsigned char a = 4;
         volatile unsigned char b = 6;
         volatile unsigned long long int c = a * b;
         return c;
}

produces:

         .file   "main.c"
         .text
         .p2align 4,,15
.globl main
         .type   main, @function
main:
.LFB16:
         .cfi_startproc
         .cfi_personality 0x3,__gxx_personality_v0
         movb    $4, -1(%rsp)
         movb    $6, -2(%rsp)
         movzbl  -1(%rsp), %edx
         movzbl  -2(%rsp), %eax
         movzbl  %dl, %edx
         movzbl  %al, %eax
         imull   %edx, %eax
         cltq
         movq    %rax, -16(%rsp)     # REDUNDANT??
         movq    -16(%rsp), %rax     # REDUNDANT??
         ret
         .cfi_endproc
.LFE16:
         .size   main, .-main
         .ident  "GCC: (Debian 4.4.5-8) 4.4.5"
         .section        .note.GNU-stack,"",@progbits

AFAIK, the two movq statements are redundant. What do they do? The just 
do rax=rsp[-16] and rsp[-16]=rax . Or am I wrong?

=================================================================

Best regards and I hope I could help

Daniel Marschall



More information about the Gcc mailing list