While trying out constexpr I was wondering what else can I get compiler to compute. I can pretty much do any integral computation with both C++ Template Metaprogramming and constexpr, but can I do some floating point computations? It appears I can!!
So I set out to compute
exp(z), using Taylor series implementation for exp:
In here I have
pow function to compute powers of a floating point number, mine is bit limited as it can do integral powers only;
factorial straight forward.
exp function function goes to
10 component evaluation. With
exp(1) I do see value of
2.7182815255731922 in debug window while
cout prints it to be
2.71828. Since this is floating point value I can’t do
Now what’s the proof that it was all compile time rather than runtime evaluation, the only way to confirm is to look at what is executed by processor, that’s right assembly:
cpp-cxpr`main at main.cpp:388: 0x100000cc0: pushq %rbp 0x100000cc1: movq %rsp, %rbp 0x100000cc4: movl $0x0, %eax 0x100000cc9: movsd 0x247(%rip), %xmm0 0x100000cd1: movl $0x0, -0x4(%rbp) 0x100000cd8: movsd %xmm0, -0x10(%rbp) 0x100000cdd: popq %rbp 0x100000cde: ret
Note: there is no instruction for
callq, which means no function was called. Since
exp(1.0) was evaluated at compile time it’s value was loaded in to register
%xmm0 at line 5 and then loaded into a local variable (negative offset from
%rbp register) at line 7. Life is a bit easy working with debug builds.
Had we not evaluated
exp(z) on compile time, just remove constexpr from code in main:
generated assembly for main function is:
cpp-cxpr`main at main.cpp:388: 0x1000009e0: pushq %rbp 0x1000009e1: movq %rsp, %rbp 0x1000009e4: subq $0x10, %rsp 0x1000009e8: movabsq $0x1, %rax 0x1000009f2: cvtsi2sdq %rax, %xmm0 0x1000009f7: movl $0x0, -0x4(%rbp) 0x1000009fe: callq 0x100000b10 ; exp(double) at main.cpp:354 0x100000a03: movl $0x0, %eax 0x100000a08: movsd %xmm0, -0x10(%rbp) 0x100000a0d: addq $0x10, %rsp 0x100000a11: popq %rbp 0x100000a12: ret
Here we are loading abs value 1 into register
%rax (line 5), then converting int value to floating point with
cvtsi2sdq instruction at line 6 and writing that in
%xmm0 register. Then at line 8 we are calling
exp(double) function which will again put the value in register
%xmm0. This value is then moved to a local variable at line 10 (negative offset from
The value computed at runtime is :
Here is the assembly code generated for
cpp-cxpr`exp(double) at main.cpp:354: 0x100000b10: pushq %rbp 0x100000b11: movq %rsp, %rbp 0x100000b14: subq $0x90, %rsp 0x100000b1b: movl $0x2, %edi 0x100000b20: movabsq $0x1, %rax 0x100000b2a: cvtsi2sdq %rax, %xmm1 0x100000b2f: movsd %xmm0, -0x8(%rbp) 0x100000b34: addsd -0x8(%rbp), %xmm1 0x100000b39: movsd -0x8(%rbp), %xmm0 0x100000b3e: movsd %xmm1, -0x10(%rbp) 0x100000b43: callq 0x100000d20 ; pow(double, int) at main.cpp:344 0x100000b48: movl $0x2, %edi 0x100000b4d: movsd %xmm0, -0x18(%rbp) 0x100000b52: callq 0x100000d90 ; factorial(int) at main.cpp:349 0x100000b57: movl $0x3, %edi ... ... ; many more instructions to make 7 more calls to pow and factorial ... ... 0x100000d17: addq $0x90, %rsp 0x100000d1e: popq %rbp 0x100000d1f: ret ; ; similarly there is code for pow and factorial ;
That’s all I have for this post..
Till next time.