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 static_assert
.
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 %rbp
register).
The value computed at runtime is : 2.7182815255731922
Here is the assembly code generated for exp(double)
function:
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.