Why are intermediate results of template recursion in object file? - d

While playing around with template recursion in D, I found that the intermediate results of the classical factorial are still in the object file. I suppose they are also in the executable...?
I can see that the actually executed code contains only the value (or a pointer to it) but:
Shouldn't there be a single mov statement without the intermediate data being saved for no reason?
This is the code:
int main()
{
static int x = factorial!(5);
return x;//factorial!(5);
}
template factorial(int n)
{
static if (n == 1)
const factorial = 1;
else
const factorial = n * factorial!(n-1);
}
and this is the output of obj2asm test.o:
( for your convenience: 1! = 1h, 2! = 2h, 3! = 6h, 4! = 18h, 5! = 78h )
FLAT group
;File = test_fac_01.d
extrn _main
public _deh_beg
public _deh_end
public _tlsstart
public _tlsend
public _D11test_fac_014mainFZi1xi
extrn _GLOBAL_OFFSET_TABLE_
public _Dmain
public _D11test_fac_0112__ModuleInfoZ
extrn _Dmodule_ref
public _D11test_fac_017__arrayZ
public _D11test_fac_018__assertFiZv
public _D11test_fac_0115__unittest_failFiZv
extrn _d_array_bounds
extrn _d_unittestm
extrn _d_assertm
.text segment
assume CS:.text
:
mov EAX,offset FLAT:_D11test_fac_0112__ModuleInfoZ[018h]#32
mov ECX,offset FLAT:_Dmodule_ref#32
mov RDX,[RCX]
mov [RAX],RDX
mov [RCX],RAX
ret
.text ends
.data segment
_D11test_fac_0112__ModuleInfoZ:
db 004h,000h,000h,0ffffff80h,000h,000h,000h,000h ;........
db 074h,065h,073h,074h,05fh,066h,061h,063h ;test_fac
db 05fh,030h,031h,000h,000h,000h,000h,000h ;_01.....
db 000h,000h,000h,000h,000h,000h,000h,000h ;........
dq offset FLAT:_D11test_fac_0112__ModuleInfoZ#64
.data ends
.bss segment
.bss ends
.rodata segment
.rodata ends
.tdata segment
_tlsstart:
db 000h,000h,000h,000h,000h,000h,000h,000h ;........
db 000h,000h,000h,000h,000h,000h,000h,000h ;........
.tdata ends
.tdata. segment
_D11test_fac_014mainFZi1xi:
db 078h,000h,000h,000h ;x...
.tdata. ends
.text._Dmain segment
assume CS:.text._Dmain
_Dmain:
push RBP
mov RBP,RSP
mov RAX,FS:[00h]
mov RCX,_D11test_fac_014mainFZi1xi#GOTTPOFF[RIP]
mov EAX,[RCX][RAX]
pop RBP
ret
nop
nop
nop
.text._Dmain ends
.data._D11test_fac_0117__T9factorialVi5Z9factorialxi segment
_D11test_fac_0117__T9factorialVi5Z9factorialxi:
db 078h,000h,000h,000h ;x...
.data._D11test_fac_0117__T9factorialVi5Z9factorialxi ends
.data._D11test_fac_0117__T9factorialVi4Z9factorialxi segment
_D11test_fac_0117__T9factorialVi4Z9factorialxi:
db 018h,000h,000h,000h ;....
.data._D11test_fac_0117__T9factorialVi4Z9factorialxi ends
.data._D11test_fac_0117__T9factorialVi3Z9factorialxi segment
_D11test_fac_0117__T9factorialVi3Z9factorialxi:
db 006h,000h,000h,000h ;....
.data._D11test_fac_0117__T9factorialVi3Z9factorialxi ends
.data._D11test_fac_0117__T9factorialVi2Z9factorialxi segment
_D11test_fac_0117__T9factorialVi2Z9factorialxi:
db 002h,000h,000h,000h ;....
.data._D11test_fac_0117__T9factorialVi2Z9factorialxi ends
.data._D11test_fac_0117__T9factorialVi1Z9factorialxi segment
_D11test_fac_0117__T9factorialVi1Z9factorialxi:
db 001h,000h,000h,000h ;....
.data._D11test_fac_0117__T9factorialVi1Z9factorialxi ends
.ctors segment
dq offset FLAT:#64
.ctors ends
.text._D11test_fac_017__arrayZ segment
assume CS:.text._D11test_fac_017__arrayZ
_D11test_fac_017__arrayZ:
push RBP
mov RBP,RSP
sub RSP,010h
mov RSI,RDI
mov RDI,offset FLAT:_D11test_fac_0112__ModuleInfoZ#64
call _d_array_bounds#PC32
nop
nop
.text._D11test_fac_017__arrayZ ends
.text._D11test_fac_018__assertFiZv segment
assume CS:.text._D11test_fac_018__assertFiZv
_D11test_fac_018__assertFiZv:
push RBP
mov RBP,RSP
sub RSP,010h
mov RSI,RDI
mov RDI,offset FLAT:_D11test_fac_0112__ModuleInfoZ#64
call _d_assertm#PC32
nop
nop
.text._D11test_fac_018__assertFiZv ends
.text._D11test_fac_0115__unittest_failFiZv segment
assume CS:.text._D11test_fac_0115__unittest_failFiZv
_D11test_fac_0115__unittest_failFiZv:
push RBP
mov RBP,RSP
sub RSP,010h
mov RSI,RDI
mov RDI,offset FLAT:_D11test_fac_0112__ModuleInfoZ#64
call _d_unittestm#PC32
leave
ret
.text._D11test_fac_0115__unittest_failFiZv ends
end

You shouldn't use templates when what you want is compile-time function execution. Just write the function as you would and call it in a static context.
int main()
{
static int x = factorial(5); // static causes CTFE
return x;
}
int factorial(int n)
{
if (n == 1)
return 1;
else
return n * factorial(n-1);
}
This won't result in any extra symbols because factorial is evaluated at compile time. There are no symbols other than factorial itself. Your template trick instantiates symbols to achieve the same effect, but it's not symbols you want.
Alternatively, if you still want to use templates, but don't want symbols then you can use manifest constants via enum.
template factorial(int n)
{
static if (n == 1)
enum factorial = 1;
else
enum factorial = n * factorial!(n-1);
}
Notice the change from const to enum. enum values are purely compile-time, so they produce no symbols or data in the object files.

Related

Maintain x*x in C++

I have the following while-loop
uint32_t x = 0;
while(x*x < STOP_CONDITION) {
if(CHECK_CONDITION) x++
// Do other stuff that modifies CHECK_CONDITION
}
The STOP_CONDITION is constant at run-time, but not at compile time. Is there are more efficient way to maintain x*x or do I really need to recompute it every time?
Note: According to the benchmark below, this code runs about 1 -- 2% slower than this option. Please read the disclaimer included at the bottom!
In addition to Tamas Ionut's answer, if you want to maintain STOP_CONDITION as the actual stop condition and avoid the square root calculation, you could update the square using the mathematical identity
(x + 1)² = x² + 2x + 1
whenever you change x:
uint32_t x = 0;
unit32_t xSquare = 0;
while(xSquare < STOP_CONDITION) {
if(CHECK_CONDITION) {
xSquare += 2 * x + 1;
x++;
}
// Do other stuff that modifies CHECK_CONDITION
}
Since the 2*x + 1 is just a bit shift and an increment, the compiler should be able to optimize this fairly well.
Disclaimer: Since you asked "how can I optimize this code" I answered with one particular way to possibly make it faster. Whether the double + increment is actually faster than a single integer multiplication should be tested in practice. Whether you should optimize the code is a different question. I assume you have already benchmarked the loop and found it to be a bottleneck, or that you have a theoretical interest in the question. If you are writing production code that you wish to optimize, first measure the performance and then optimize where needed (which is probably not the x*x in this loop).
What about:
uint32_t x = 0;
double bound= sqrt(STOP_CONDITION);
while(x < bound) {
if(CHECK_CONDITION) x++
// Do other stuff that modifies CHECK_CONDITION
}
This way, you're getting rid of that extra computation.
I made a small benchmarking for Tamas Ionut and CompuChip answers and here are the results:
Tamas Ionut: 19.7068
The code of this method:
uint32_t x = 0;
double bound= sqrt(STOP_CONDITION);
while(x < bound) {
if(CHECK_CONDITION) x++
// Do other stuff that modifies CHECK_CONDITION
}
CompuChip: 20.2056
The code of this method:
uint32_t x = 0;
unit32_t xSquare = 0;
while(xSquare < STOP_CONDITION) {
if(CHECK_CONDITION) {
xSquare += 2 * x + 1;
x++;
}
// Do other stuff that modifies CHECK_CONDITION
}
with STOP_CONDITION = 1000000 and repeating the process 1000000 times
Environment:
Compiler : MSVC 2013
OS : Windows 8.1 - X64
Processor: Core i7-4510U
#2.00 GHZ
Release Mode - Maximize Speed (/O2)
I would say, optimization in readibility is better than optimization in Performance in your case since we are talking about a very small Performance optimization
The compliter can optimize a lot for you regarding Performance but readibility lies in the responsibility of the programmer
I believe Tamas Ionut solution is better than that of CompuChip because we only have x++ inside the for loop. However, a comparison between uint32_t and double will kill the deal. It would be more efficient if we use uint32_t for bound instead of using double. This approach has less problem with numerical overflow because x cannot be greater than 2^16 = 65536 if we want to have a correct x^2 value.
If we also do a heavy work in the loop then results obtained from both approach should be very similar, however, Tamas Ionut approach is more simple and easier to read.
Below is my code and the corresponding assembly code obtained using clang version 3.8.0 with -O3 flag. It is very clear from the assembly code that the first approach is more efficient.
using T = size_t;
void test1(const T stopCondition, bool checkCondition) {
T x = 0;
while (x < stopCondition) {
if (checkCondition) {
x++;
}
// Do something heavy here
}
}
void test2(const T stopCondition, bool checkCondition) {
T x = 0;
T xSquare = 0;
const T threshold = stopCondition * stopCondition;
while (xSquare < threshold) {
if (checkCondition) {
xSquare += 2 * x + 1;
x++;
}
// Do something heavy here
}
}
(gdb) disassemble test1
Dump of assembler code for function _Z5test1mb:
0x0000000000400be0 <+0>: movzbl %sil,%eax
0x0000000000400be4 <+4>: mov %rax,%rcx
0x0000000000400be7 <+7>: neg %rcx
0x0000000000400bea <+10>: nopw 0x0(%rax,%rax,1)
0x0000000000400bf0 <+16>: add %rax,%rcx
0x0000000000400bf3 <+19>: cmp %rdi,%rcx
0x0000000000400bf6 <+22>: jb 0x400bf0 <_Z5test1mb+16>
0x0000000000400bf8 <+24>: retq
End of assembler dump.
(gdb) disassemble test2
Dump of assembler code for function _Z5test2mb:
0x0000000000400c00 <+0>: imul %rdi,%rdi
0x0000000000400c04 <+4>: test %sil,%sil
0x0000000000400c07 <+7>: je 0x400c2e <_Z5test2mb+46>
0x0000000000400c09 <+9>: xor %eax,%eax
0x0000000000400c0b <+11>: mov $0x1,%ecx
0x0000000000400c10 <+16>: test %rdi,%rdi
0x0000000000400c13 <+19>: je 0x400c42 <_Z5test2mb+66>
0x0000000000400c15 <+21>: data32 nopw %cs:0x0(%rax,%rax,1)
0x0000000000400c20 <+32>: add %rcx,%rax
0x0000000000400c23 <+35>: add $0x2,%rcx
0x0000000000400c27 <+39>: cmp %rdi,%rax
0x0000000000400c2a <+42>: jb 0x400c20 <_Z5test2mb+32>
0x0000000000400c2c <+44>: jmp 0x400c42 <_Z5test2mb+66>
0x0000000000400c2e <+46>: test %rdi,%rdi
0x0000000000400c31 <+49>: je 0x400c42 <_Z5test2mb+66>
0x0000000000400c33 <+51>: data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
0x0000000000400c40 <+64>: jmp 0x400c40 <_Z5test2mb+64>
0x0000000000400c42 <+66>: retq
End of assembler dump.

A variation of fibonacci assembly x86, have to call it from a c++ main method, kind of lost at few parts

This is the fibonacci code,
unsigned int fib(unsigned int n)
{
if (n==1 || n ==2)
return 1;
else
return fib(n-2) + fib(n-1);
}
but instead for my code, I have to change formula to a new one,
f(n-2)/2 + f(n-1) * 2, so the sequence is 1, 2, 4, 9, 20, 44, 98, 218
I need to write a recursive function called Mobonacci in assembly to calculate the nth number in sequence, and also a main function in c++ that reads a positive number n, then cals mobonacci assembly function with parameter n, then print our result
So I'm kind of confused, do I write the function in assembly like I did below, then write a c++ function to call it? and how would guys change my code from fibonacci to the new formula? Here is my code, what do I need to change and did I need create new part that let the code read input? Also is my code too short? do I need add anything else?
.code
main PROC
mov ecx,0
push 4 ; calculate the nth fib
call Fib ; calculate fib (eax)
call WriteDec
call Crlf
exit
main ENDP
Fib PROC
add ecx,1
push ebp
mov ebp,esp
mov eax,[ebp+8] ; get n
cmp eax,2 ; n == 2?
je exception2
cmp eax,1 ; n == 1?
je exception2
dec eax
push eax ; Fib(n-1)
call fib
add eax,
jmp Quit
Exception2:
dec eax
Quit:
pop ebp ; return EAX
ret 4 ; clean up stack
Fib ENDP
END main
Depends on where you are trying to insert asm code into your c++ code...
For gcc/linux you can do something like :
//Simple example:
void *frame; /* Frame pointer */
__asm__ ("mov %%ebp,%0":"=r"(frame));
//Complicated example:
int foo(void) {
int joe=1234, fred;
__asm__(
" mov %1,%%eax\n"
" add $2,%%eax\n"
" mov %%eax,%0\n"
:"=r" (fred) /* %0: Out */
:"r" (joe) /* %1: In */
:"%eax" /* Overwrite */
);
return fred;
}
The important thing is to understand how to use your asm function in cpp.
You can find some useful things about this subject here : https://www.cs.uaf.edu/2011/fall/cs301/lecture/10_12_asm_c.html
About the second part of your question.
To multiple, you can use the command "mul" and to make a division "div".
So if you want to do f(n-1) * 2
You have to get you register %eax after the "call fib" and use mul.
Just have a look here:
http://www.tutorialspoint.com/assembly_programming/assembly_arithmetic_instructions.htm

Override function call in VxWorks

My problem is the following. I have a code unit composed of various c files, say for example
file1.c
file2.c
file3.c
that are all compiled with GCC to a unique object "object.o", which in turn is then linked with other objects to give at the end the executable "application.out", running on VxWorks.
Since I'm doing unit testing on "object.o", my need is to stimulate all the possible ways through the code. Specifically, there are situations where I should have mock functions executed instead of original ones in order to simulate error occurrences.
Suppose for example that there is a function "func_caller" that I'm trying to test that, at some point in the execution, makes a call to another function "func_called" (declared as static).
Since I DON'T WANT TO MODIFY THE ORIGINAL CODE, I wonder if there is a way to manipulate the instruction pointers in such a way that when "func_called" is called, it actually executes another mock function "func_called_mock" and the caller "func_caller" does not notice anything.
Thanks in advance.
The most direct method of overriding function calls would be to use VxWorks's load time linking. Consider the following source:
file1.c:
#include <stdio.h>
int function1 (void);
int function1 ()
{
printf ("function1 called\n");
return 1;
}
file2.c:
#include <stdio.h>
int function2 (void);
int function2 ()
{
printf ("function2 called\n");
return 2;
}
file3.c:
int function1 (void);
int function2 (void);
int function3 (void);
int function3 ()
{
function1 ();
function2 ();
return 0;
}
mock.c:
#include <stdio.h>
int function1 (void);
int function2 (void);
int function1 ()
{
printf ("mock function1 called\n");
return 1;
}
int function2 ()
{
printf ("mock function2 called\n");
return 2;
}
When you load an object, its functions are added to the global symbol table.
-> ld < file1.o
value = 273740816 = 0x1050f410
-> lkup "function"
function1 0x108b0000 text (file1.o)
value = 0 = 0x0
->
When you load an object that uses functions already in the symbol table, each call will be immediately resolved to the last address associated with that symbol in the table.
-> ld < file2.o
value = 292535232 = 0x116fbbc0
-> ld < file3.o
value = 292537592 = 0x116fc4f8
-> lkup "function"
function1 0x108b0000 text (file1.o)
function2 0x108d0000 text (file2.o)
function3 0x108f0000 text (file3.o)
value = 0 = 0x0
-> l function3
function3:
0x108f0000 55 PUSH EBP
0x108f0001 89 e5 MOV EBP, ESP
0x108f0003 56 PUSH ESI
0x108f0004 57 PUSH EDI
0x108f0005 e8 f6 ff fb ff CALL function1
0x108f000a e8 f1 ff fd ff CALL function2
0x108f000f 31 c0 XOR EAX, EAX
0x108f0011 5f POP EDI
0x108f0012 5e POP ESI
0x108f0013 89 ec MOV ESP, EBP
value = 0 = 0x0
-> function3
function1 called
function2 called
value = 0 = 0x0
->
Although l() helpfully displays function names, no symbols are actually loaded into memory with the object. Instead, a call to the last address associated with the function is loaded. So, a previously loaded function may be overridden by loading another function of the same name.
-> unld "file3.o"
value = 0 = 0x0
-> ld < mock.o
value = 292537592 = 0x116fc4f8
-> ld < file3.o
value = 292539496 = 0x116fcc68
-> lkup "function"
function1 0x108f0000 text (mock.o)
function1 0x108b0000 text (file1.o)
function2 0x108f0020 text (mock.o)
function2 0x108d0000 text (file2.o)
function3 0x10910000 text (file3.o)
value = 0 = 0x0
-> l function3
function3:
0x10910000 55 PUSH EBP
0x10910001 89 e5 MOV EBP, ESP
0x10910003 56 PUSH ESI
0x10910004 57 PUSH EDI
0x10910005 e8 f6 ff fd ff CALL function1
0x1091000a e8 11 00 fe ff CALL function2
0x1091000f 31 c0 XOR EAX, EAX
0x10910011 5f POP EDI
0x10910012 5e POP ESI
0x10910013 89 ec MOV ESP, EBP
value = 0 = 0x0
-> function3
mock function1 called
mock function2 called
value = 0 = 0x0
->
Note that for this method to work, the called and calling functions cannot be compiled into the same object. You might also note that the addresses to be called don't match those in the symbol table. This is the result of executing the above in VxSim. The VxSim loader actually calls the loader of the underlying operating system. So, these addresses don't match those in the symbol table and the assembly reflects the underlying Pentium architecture on which WorkBench is being run.
A function call may also be overridden by directly manipulating the address to be called in memory. This method is going to be implementation dependent. Below, this is demonstrated for source compiled for PPC using the gcc -mlongcall option. This has been run on an actual target, not VxSim.
-> ld < file1.o
value = 33538216 = 0x1ffc0a8 = function1 + 0x498
-> ld < file2.o
value = 33548336 = 0x1ffe830 = function2 + 0x80
-> ld < mock.o
value = 33549600 = 0x1ffed20 = function2 + 0x570
-> ld < file3.o
value = 33550744 = 0x1fff198 = function2 + 0x9e8
->
-> lkup "function"
function1 0x01ffbef8 text (mock.o)
function1 0x01ffbc10 text (file1.o)
function2 0x01ffbf58 text (mock.o)
function2 0x01ffe7b0 text (file2.o)
function3 0x01ffe558 text (file3.o)
value = 0 = 0x0
->
-> function3
mock function1 called
mock function2 called
value = 0 = 0x0
->
-> l function3
function3:
0x1ffe558 9421ffe8 stwu r1,-24(r1)
0x1ffe55c 7c0802a6 mfspr r0,LR
0x1ffe560 93a1000c stw r29,12(r1)
0x1ffe564 93c10010 stw r30,16(r1)
0x1ffe568 93e10014 stw r31,20(r1)
0x1ffe56c 9001001c stw r0,28(r1)
0x1ffe570 7c3f0b78 or r31,r1,r1
0x1ffe574 3d200200 lis r9,512
0x1ffe578 3ba9bef8 addi r29,r9,-16648
0x1ffe57c 7fa803a6 mtspr LR,r29
value = 33547648 = 0x1ffe580 = function3 + 0x28
->
-> *0x1ffe578
function3 + 0x20 = 0x1ffe578: value = 1000980216 = 0x3ba9bef8
-> *0x1ffe578 = 0x3ba9bc10
function3 + 0x20 = 0x1ffe578: value = 1000979472 = 0x3ba9bc10
-> *0x1ffe578
function3 + 0x20 = 0x1ffe578: value = 1000979472 = 0x3ba9bc10
->
-> function3
function1 called
mock function2 called
value = 0 = 0x0
->
Obviously, directly manipulating pointers in memory is going to quickly become tedious. Additionally, memory protection will prevent you from changing RTPs or objects loaded in VxSim. (Hence, why I ran this on actual hardware.) I mention the possibility, primarily because it seems to best match your question's statement.
Finally, for non-trivial unit testing, you may want to consider a tool designed specifically for the task. Try a search on "vxworks unit test framework". I don't have deep experience with any particular tool (and don't want to come across spammy). Perhaps, someone else here can provide a good suggestion.

C++ Conditional Operator versus if-else

I have always wondered about this. Let's say we have a variable, string weight, and an input variable, int mode, which can be 1 or 0.
Is there a clear benefit to using:
weight = (mode == 1) ? "mode:1" : "mode:0";
over
if(mode == 1)
weight = "mode:1";
else
weight = "mode:0";
beyond code readability? Are speeds at all affected, is this handled differently by the compiler (such as the ability of certain switch statements to be converted to jump tables)?
The key difference between the conditional operator and an if/else block is that the conditional operator is an expression, rather than a statement. Thus, there are few places you can use the conditional operator where you can't use an if/else. For example, initialization of constant objects, like so:
const double biasFactor = (x < 5) ? 2.5 : 6.432;
If you used if/else in this case, biasFactor would have to be non-const.
Additonally, constructor initializer lists call for expressions rather than statements as well:
X::X()
: myData(x > 5 ? 0xCAFEBABE : OxDEADBEEF)
{
}
In this case, myData may not have any assignment operator or non-const member functions defined--its constructor may be the only way to pass any parameters to it.
Also, note that any expression can be turned into a statement by adding a semicolon at the end--the reverse is not true.
No, this is purely about presenting the code to a human reader. I'd expect any compiler to generate identical code for these.
With mingw, the assembly code generated with
const char * testFunc()
{
int mode=1;
const char * weight = (mode == 1)? "mode:1" : "mode:0";
return weight;
}
is:
testFunc():
0040138c: push %ebp
0040138d: mov %esp,%ebp
0040138f: sub $0x10,%esp
10 int mode=1;
00401392: movl $0x1,-0x4(%ebp)
11 const char * weight = (mode == 1)? "mode:1" : "mode:0";
00401399: cmpl $0x1,-0x4(%ebp)
0040139d: jne 0x4013a6 <testFunc()+26>
0040139f: mov $0x403064,%eax
004013a4: jmp 0x4013ab <testFunc()+31>
004013a6: mov $0x40306b,%eax
004013ab: mov %eax,-0x8(%ebp)
12 return weight;
004013ae: mov -0x8(%ebp),%eax
13 }
And with
const char * testFunc()
{
const char * weight;
int mode=1;
if(mode == 1)
weight = "mode:1";
else
weight = "mode:0";
return weight;
}
is:
testFunc():
0040138c: push %ebp
0040138d: mov %esp,%ebp
0040138f: sub $0x10,%esp
11 int mode=1;
00401392: movl $0x1,-0x8(%ebp)
12 if(mode == 1)
00401399: cmpl $0x1,-0x8(%ebp)
0040139d: jne 0x4013a8 <testFunc()+28>
13 weight = "mode:1";
0040139f: movl $0x403064,-0x4(%ebp)
004013a6: jmp 0x4013af <testFunc()+35>
15 weight = "mode:0";
004013a8: movl $0x40306b,-0x4(%ebp)
17 return weight;
004013af: mov -0x4(%ebp),%eax
18 }
Pretty much the same code is generated. The performance of your application shouldn't depend on small details like this one.
So, no it doesn't make a difference.

Why are some statements skipped in gdb?

I'm debugging this code :
len = NGX_SYS_NERR * sizeof(ngx_str_t);
ngx_sys_errlist = malloc(len);
if (ngx_sys_errlist == NULL) {
goto failed;
}
for (err = 0; err < NGX_SYS_NERR; err++) {
But in gdb if (ngx_sys_errlist == NULL) { is skipped directly:
(gdb)
59 ngx_sys_errlist = malloc(len);
(gdb) n
64 for (err = 0; err < NGX_SYS_NERR; err++) {
I also have experienced this before,but never knows the reason,anyone knows?
Is it a bug?
UPDATE
0x000000000041be9d <ngx_strerror_init+0>: mov %rbx,-0x30(%rsp)
0x000000000041bea2 <ngx_strerror_init+5>: mov %rbp,-0x28(%rsp)
0x000000000041bea7 <ngx_strerror_init+10>: mov %r12,-0x20(%rsp)
0x000000000041beac <ngx_strerror_init+15>: mov %r13,-0x18(%rsp)
0x000000000041beb1 <ngx_strerror_init+20>: mov %r14,-0x10(%rsp)
0x000000000041beb6 <ngx_strerror_init+25>: mov %r15,-0x8(%rsp)
0x000000000041bebb <ngx_strerror_init+30>: sub $0x38,%rsp
0x000000000041bebf <ngx_strerror_init+34>: mov $0x840,%edi
0x000000000041bec4 <ngx_strerror_init+39>: callq 0x402388 <malloc#plt>
0x000000000041bec9 <ngx_strerror_init+44>: mov %rax,0x26e718(%rip) # 0x68a5e8 <ngx_sys_errlist>
0x000000000041bed0 <ngx_strerror_init+51>: mov $0x840,%r12d
0x000000000041bed6 <ngx_strerror_init+57>: test %rax,%rax
0x000000000041bed9 <ngx_strerror_init+60>: je 0x41bf56 <ngx_strerror_init+185>
0x000000000041bedb <ngx_strerror_init+62>: mov $0x0,%r13d
0x000000000041bee1 <ngx_strerror_init+68>: mov $0x0,%r14d
0x000000000041bee7 <ngx_strerror_init+74>: mov $0x0,%r15d
0x000000000041beed <ngx_strerror_init+80>: mov %r13d,%edi
0x000000000041bef0 <ngx_strerror_init+83>: callq 0x402578 <strerror#plt>
UPDATE
Nobody else ever met the same thing in using gdb? It happens to me frequently when debugging.
Most likely the two statements were optimized into a single set-and-test expression, which then can't be decomposed into the original two lines. The generated pseudocode is likely to be something like
call _malloc
jz _failed
mov acc, _ngx_sys_errlist
where the test now happens before the assignment; do you let the source level trace go backwards to reflect this?
please check,
a) if you are debugging release build (if there exists one)
b) if your source file is modified
if you still have the issue, please provide the details (Complier with version, degugger version , platform and code ...)