I am in the process of comparing Fortran 90 vs C++ for a presentation. One of my comparisons relies on the assembly generated for simple programs by g++
and gfortran
.
One example reads as follows:
#include<cstdio> // quick and dirty number formatting
template<int N>
double dot(double x[], double y[]){
return x[N-1] * y[N-1] + dot<N-1>(x, y);
}
template<>
double dot<1>(double x[], double y[]){
return x[0] * y[0];
}
int main(){
double x[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
double y[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
printf("x.y = %23.16E\n", dot<10>(x, y));
}
The following assembler is generated by the command g++ -S -O3 myprogram.cpp
using g++ 4.7.2 on OS X 10.7.4 x86_64-apple-darwin11.4.2:
.text
.align 4,0x90
.globl __Z3dotILi1EEdPdS0_
__Z3dotILi1EEdPdS0_:
LFB2:
movsd (%rdi), %xmm0
mulsd (%rsi), %xmm0
ret
LFE2:
.cstring
LC1:
.ascii "x.y = %23.16E\12\0"
.section __TEXT,__text_startup,regular,pure_instructions
.align 4
.globl _main
_main:
LFB3:
leaq LC1(%rip), %rdi
subq $8, %rsp
LCFI0:
movl $1, %eax
movsd LC0(%rip), %xmm0
call _printf
xorl %eax, %eax
addq $8, %rsp
LCFI1:
ret
LFE3:
.literal8
.align 3
LC0:
.long 0
.long 1081610240
.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame1:
.set L$set$0,LECIE1-LSCIE1
.long L$set$0
LSCIE1:
.long 0
.byte 0x1
.ascii "zR\0"
.byte 0x1
.byte 0x78
.byte 0x10
.byte 0x1
.byte 0x10
.byte 0xc
.byte 0x7
.byte 0x8
.byte 0x90
.byte 0x1
.align 3
LECIE1:
LSFDE1:
.set L$set$1,LEFDE1-LASFDE1
.long L$set$1
LASFDE1:
.long LASFDE1-EH_frame1
.quad LFB2-.
.set L$set$2,LFE2-LFB2
.quad L$set$2
.byte 0
.align 3
LEFDE1:
LSFDE3:
.set L$set$3,LEFDE3-LASFDE3
.long L$set$3
LASFDE3:
.long LASFDE3-EH_frame1
.quad LFB3-.
.set L$set$4,LFE3-LFB3
.quad L$set$4
.byte 0
.byte 0x4
.set L$set$5,LCFI0-LFB3
.long L$set$5
.byte 0xe
.byte 0x10
.byte 0x4
.set L$set$6,LCFI1-LCFI0
.long L$set$6
.byte 0xe
.byte 0x8
.align 3
LEFDE3:
.constructor
.destructor
.align 1
.subsections_via_symbols
The dot product is 385, and it seems that it was calculated at compile time, but I cannot seem to find exactly where. I suspect it is somewhere in the following assembler segment:
movl $1, %eax
movsd LC0(%rip), %xmm0
call _printf
xorl %eax, %eax
addq $8, %rsp
LCFI1:
ret
LFE3:
.literal8
.align 3
LC0:
.long 0
.long 1081610240
.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
My (very, very limited) understanding of assembly, would tell me that the dot product was calculated by the compiler and placed in a register (LC0). Then the instruction movsd LC0(%rip), %xmm0
places the value in a string, and calls printf
on the resulting, formatted string.
Is this the case? Is the actual number 385 included somewhere in this output, or is it calculated elsewhwere?
Thank you!
In case anybody wonders how the assembly produced by gfortran looks like, I am attaching it below. Notice that even though is known at compile time, and I'm using Fortran's intrinsic dot_product
operator, the generated assembly is substantially larger (130 lines vs. 90 lines in the C++ version), and it seems that the optimizer is not able to reduce the operation.
Program (notice that I am using the intrinsic, built-in dot_product
operator):
PROGRAM MAIN
REAL(8), DIMENSION(10):: X = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)
REAL(8), DIMENSION(10):: Y = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)
PRINT "(A, E23.16)", "x.y = ", DOT_PRODUCT(X, Y)
ENDPROGRAM MAIN
Assembly (gfortran -S -O3 myprogram.cpp using gcc 4.7.2 on OS X 10.7.4 x86_64-apple-darwin11.4.2)
.cstring
LC0:
.ascii "dotproduct-intrinsic.f90\0"
.const
LC1:
.ascii "(A, E23.16)"
LC2:
.ascii "x.y = "
.text
.align 4,0x90
_MAIN__:
LFB0:
leaq LC0(%rip), %rax
subq $504, %rsp
LCFI0:
movq %rax, 24(%rsp)
leaq 16(%rsp), %rdi
leaq LC1(%rip), %rax
movl $5, 32(%rsp)
movq %rax, 88(%rsp)
movl $11, 96(%rsp)
movl $4096, 16(%rsp)
movl $6, 20(%rsp)
call __gfortran_st_write
leaq 16(%rsp), %rdi
movl $6, %edx
leaq LC2(%rip), %rsi
call __gfortran_transfer_character_write
leaq 8(%rsp), %rsi
movl $8, %edx
movabsq $4645480607818711040, %rax
leaq 16(%rsp), %rdi
movq %rax, 8(%rsp)
call __gfortran_transfer_real_write
leaq 16(%rsp), %rdi
call __gfortran_st_write_done
addq $504, %rsp
LCFI1:
ret
LFE0:
.section __TEXT,__text_startup,regular,pure_instructions
.align 4
.globl _main
_main:
LFB1:
subq $8, %rsp
LCFI2:
call __gfortran_set_args
leaq _options.3.1864(%rip), %rsi
movl $8, %edi
call __gfortran_set_options
call _MAIN__
xorl %eax, %eax
addq $8, %rsp
LCFI3:
ret
LFE1:
.const
.align 5
_options.3.1864:
.long 68
.long 1023
.long 0
.long 0
.long 1
.long 1
.long 0
.long 1
.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame1:
.set L$set$0,LECIE1-LSCIE1
.long L$set$0
LSCIE1:
.long 0
.byte 0x1
.ascii "zR\0"
.byte 0x1
.byte 0x78
.byte 0x10
.byte 0x1
.byte 0x10
.byte 0xc
.byte 0x7
.byte 0x8
.byte 0x90
.byte 0x1
.align 3
LECIE1:
LSFDE1:
.set L$set$1,LEFDE1-LASFDE1
.long L$set$1
LASFDE1:
.long LASFDE1-EH_frame1
.quad LFB0-.
.set L$set$2,LFE0-LFB0
.quad L$set$2
.byte 0
.byte 0x4
.set L$set$3,LCFI0-LFB0
.long L$set$3
.byte 0xe
.byte 0x80,0x4
.byte 0x4
.set L$set$4,LCFI1-LCFI0
.long L$set$4
.byte 0xe
.byte 0x8
.align 3
LEFDE1:
LSFDE3:
.set L$set$5,LEFDE3-LASFDE3
.long L$set$5
LASFDE3:
.long LASFDE3-EH_frame1
.quad LFB1-.
.set L$set$6,LFE1-LFB1
.quad L$set$6
.byte 0
.byte 0x4
.set L$set$7,LCFI2-LFB1
.long L$set$7
.byte 0xe
.byte 0x10
.byte 0x4
.set L$set$8,LCFI3-LCFI2
.long L$set$8
.byte 0xe
.byte 0x8
.align 3
LEFDE3:
.subsections_via_symbols
Thanks to @JerryCoffin's answer , I am able to verify that, indeed, the bit pattern
LC0:
.long 0
.long 1081610240
found in the assembly output corresponds to the number 385.
I used the exact same program provided by @JerryCoffin , namely:
#include <stdio.h>
#pragma pack(1)
struct x {
long x, y;
};
int main() {
x v = {0, 1081610240};
printf("%f\n", *(double *)&v);
return 0;
}
with the only caveat that I had to compile it using 32 bits target: g++ verification.cpp -m32
.
Yes -- this:
LC0:
.long 0
.long 1081610240
Is the bit pattern for a double with the value 385 1 . So what's happening is that this:
LCFI0:
movl $1, %eax
movsd LC0(%rip), %xmm0
call _printf
...is loading that value into an XMM register, then calling printf
to print it out. I can't say for sure, but if I had to guess, I would say the 1
is telling it that it's passing one parameter to be printed out.
1 In case you care to verify that, try this:
#include <stdio.h>
#pragma pack(1)
struct x {
long x, y;
};
int main() {
x v = {0, 1081610240};
printf("%f\n", *(double *)&v);
return 0;
}
Officially, of course, this is non-portable, etc., but with the same compiler on the same machine, chances are about 99% that you'll get the same output I did -- 385
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.