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