| Bart <bc@freeuk.com>: Sep 11 08:56PM +0100 On 11/09/2021 18:15, David Brown wrote: >> relationship with the original source code. If you're trying to trace a >> logic problem, how do you map machine code to the corresponding source? > It's a /lot/ easier with -O1 than -O0. Or you use the debugger. Oh, you mean look at the ASM manually? In that case definitely through -O0. If I take this fragment: for (int i=0; i<100; ++i) { a[i]=b+c*d; fn(a[i]); } then using 'gcc -S -O1 -fverbose-asm' gives me: # c.c:8: a[i]=b+c*d; movl $0, %esi #, _2 movl $100, %ebx #, ivtmp_4 .L2: # c.c:9: fn(a[i]); movl %esi, %ecx # _2, call fn # # c.c:7: for (int i=0; i<100; ++i) { subl $1, %ebx #, ivtmp_4 jne .L2 #, Where's all my code gone?! With -O0 I get this: # c.c:7: for (int i=0; i<100; ++i) { movl $0, -4(%rbp) #, i # c.c:7: for (int i=0; i<100; ++i) { jmp .L2 # .L3: # c.c:8: a[i]=b+c*d; movl -8(%rbp), %eax # c, tmp87 imull -12(%rbp), %eax # d, tmp87 movl %eax, %edx # tmp87, _1 # c.c:8: a[i]=b+c*d; movl -16(%rbp), %eax # b, tmp88 addl %eax, %edx # tmp88, _2 # c.c:8: a[i]=b+c*d; movl -4(%rbp), %eax # i, tmp90 cltq movl %edx, -64(%rbp,%rax,4) # _2, a[i_4] # c.c:9: fn(a[i]); movl -4(%rbp), %eax # i, tmp92 cltq movl -64(%rbp,%rax,4), %eax # a[i_4], _3 movl %eax, %ecx # _3, call fn # # c.c:7: for (int i=0; i<100; ++i) { addl $1, -4(%rbp) #, i .L2: # c.c:7: for (int i=0; i<100; ++i) { cmpl $99, -4(%rbp) #, i jle .L3 #, This looks pretty dreadful (gas format always does; gcc can produce Intel-style eee below). However you can much more clearly match the elements of your C code with lines of the ASM. My own C compiler can produce this, which is one step back from it's -S output: mov word32 [Dframe+i], 0 jmp L4 L5: iwiden D0, word32 [Dframe+i] mov A1, [Dframe+b] mov A3, [Dframe+c] imul A3, [Dframe+d] add A1, A3 mov [Dframe+D0*4+a], A1 sub Dstack, 32 iwiden D10, word32 [Dframe+i] mov A10, [Dframe+D10*4+a] call fn* add Dstack, 32 L2: inc word32 [Dframe+i] L4: mov A0, [Dframe+i] cmp A0, 100 jl L5 (The proper ASM changes 'iwiden' to 'movsx', and the local variable names to offsets.) If I code the equivalent in my language [where ints are 64 bits], and turn on its very modest optimiser, it produces this (with simplified local names specific for debugging purposes): mov R.i, 0 L5: mov D0, R.c imul2 D0, R.d mov D1, R.b add D1, D0 mov [Dframe+R.i*8+start.a], D1 mov D10, D1 call t.fn L6: inc R.i cmp R.i, 9 jle L5 So which one gets the prize? To me, both gcc-produced listings look like a nightmare. TBF that can probably be cleaned up; the listing produced via godbolt.org looks like this for -O0 in Intel format: mov DWORD PTR [rbp-4], 0 jmp .L2 .L3: mov eax, DWORD PTR [rbp-8] imul eax, DWORD PTR [rbp-12] mov edx, eax mov eax, DWORD PTR [rbp-16] add edx, eax mov eax, DWORD PTR [rbp-4] cdqe mov DWORD PTR [rbp-64+rax*4], edx mov eax, DWORD PTR [rbp-4] cdqe mov eax, DWORD PTR [rbp-64+rax*4] mov edi, eax call fn add DWORD PTR [rbp-4], 1 .L2: cmp DWORD PTR [rbp-4], 99 jle .L3 mov eax, 0 But it's still not as easy to follow as either of mine. So, yes, decent tools are important... >> Or more importantly, how does the debugger do so? > The compiler generates lots of debug information, and the debugger reads > it. How else would it work? Have a look at my first example above; would the a[i]=b+c*d be associated with anything more meaningful than those two lines of assembly? > Sometimes the issue is on the lines of "Why is this taking so long? I > had expected less than 0.1µs, but it is taking nearly 0.2µs." You need > to look at the assembly for that. That's the kind of thing that the unit tests Ian is always on about don't really work. > I don't have a problem with compile speed. Then just scale up the size of the project; you will hit a point where it /is/ a problem! Or change the threshold at which any hanging about becomes incredibly annoying; mine is about half a second. Just evading the issue by, insteading of getting a tool to work more quickly, making it try to avoid compiling things as much as possible, isn't a satisfactory solution IMO. It's like avoiding spending too long driving your car, due to its only managing to do 3 mph, by cutting down on your trips as much as possible. It's a slow car - /that's/ the problem. > mileage. But I don't see its petrol usage as a problem - and I > certainly would not swap it for a moped just because the moped uses much > less petrol. There are probably other reasons why deliveries are often done by moped. Being cheap to run is one, that it's small and nippy and can go anywhere might be others. You might also be able to afford to have a dozen on the go at any one time. |
| Manfred <noname@add.invalid>: Sep 12 01:09AM +0200 On 9/9/2021 10:17 PM, David Brown wrote: > c.d c.o: c.c a.h b.h > Now "make" knows that the dependency file is also dependent on the C > file and headers. What you are describing is substantially: https://www.gnu.org/software/make/manual/html_node/Automatic-Prerequisites.html with the addition of the -MT gcc option, which removes the need for the nasty 'sed' command in the "%.d: %.c" rule - which is the kind of thing that tends to keep people away. Thanks for pointing this out. I guess in that rule one can use the single command: %.d: %.c $(CC) $(CPPFLAGS) -MM -MT '$*.o $@' -MF $@ $< (As a side note, it wouldn't hurt if the GCC people updated their docs from time to time...) |
| David Brown <david.brown@hesbynett.no>: Sep 11 08:03PM +0200 On 11/09/2021 17:09, Alf P. Steinbach wrote: > any other actual effect of this UB than infinite recursion. > But of course there can be: formal UB means that the compiler is free to > insert checking that does whatever the compiler writers want. It is quite easy to see how you could get a deadlock from recursive use, but not concurrent use. A typical initialisation sequence could be roughly : if (!initialised_flag) { // atomic flag get_lock(); if (!initialised_flag) { do_initialisation(); initialised_flag = true; } release_lock(); } Calling this concurrently when the lock is taken, the second thread will block on "get_lock()" until the first thread is finished, then see the initialisation was done and go on its way. But calling it recursively, the block at "get_lock()" will never finish because it is the same thread that was supposed to be doing the initialisation. On the other hand, the sequence could be: if (!initialised_flag) { initialise_temporary_object(); compare_and_swap(static_object, temporary_object, null); initialised_flag = true; } That would be fine if there is no problem in accidentally creating extra objects and discarding them. The compare and swap would avoid overriding a previous initialisation. This would work fine with both concurrent and recursive use. So the standard says the behaviour is undefined on recursive use, leaving the implementation freedom to pick either tactic (or other tactics). |
| Bonita Montero <Bonita.Montero@gmail.com>: Sep 11 09:19PM +0200 Something I wondered about is whether it is possible to get a C function pointer from a local static lambda _with_ captures like this: #include <iostream> int main() { int i; static auto lambda = [&i]() { ++i; }; void (*pLambda)() = lambda; } In theory this should be possible since the lambda doesn't need its own context-pointer (pseudo-"this") since the capture-context is global storage. This seems something the designers of C++ didn't consider, although it is of little use. |
| "Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Sep 11 10:05PM +0200 On 11 Sep 2021 21:19, Bonita Montero wrote: > }; > void (*pLambda)() = lambda; > } This particular example — the lines of code up to the hypothetical declaration of `pLambda` — is flawed because if you put this code in any function other than `main` it will then use a dangling reference on second call of that function. --- > In theory this should be possible since the lambda doesn't need > its own context-pointer (pseudo-"this") since the capture-context > is global storage. If you make `i` static then you have something that a smart enough compiler can rewrite to a capture-less lambda. Don't know if they do though. And conversion to C function pointer would need to be a language extension. --- > This seems something the designers of C++ didn't > consider, although it is of little use. You can get a freestanding function that references any C++ object simply by storing a reference or pointer to (a copy of) that object indirectly or directly in a static variable that the function uses. For example, the type of the static variable can be `std::function<void()>`. And that scheme can be elaborated to provide a function that you can use like `as_c_callback( [&]{ ++i; } )`. To make that safe against multiple overlapping uses of `as_c_callback` let the static variable be a collection, e.g. with the return type of `as_c_callback` as a RAII object with implicit conversion to C function pointer. To make it thread safe use thread local storage. Uhm, not sure how to make it fool proof. But if such elaborations are adopted it can probably be a good idea to separate the elaborations from the basic simple scheme. - Alf |
| David Brown <david.brown@hesbynett.no>: Sep 11 08:10PM +0200 On 11/09/2021 13:55, Alf P. Steinbach wrote: > On 11 Sep 2021 12:35, Bonita Montero wrote: >> Is it specified that extern "C" functions never throw exceptions. > No, I don't think so, but that's a common and reasonable assumption. extern "C" marks the function as having C naming conventions and C calling conventions (which may, in theory, differ from C++ conventions - though I have never heard of that in practice). But there is nothing to stop a C++ function being marked extern "C" - I have done that in my own code, in connection with overriding weak alias functions defined in C or when I needed a non-mangled name for the function. If we are talking about external C functions - functions compiled in a C unit by a C compiler - then it is reasonable to assume that the function itself will not throw any exceptions. You'd need C compiler extensions to support that. However, the C function could call C++ functions that in turn throw exceptions - the external C function would then be passing on exceptions, even though it did not throw any itself. |
| You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment