simple question about inter modular acces

B

Ben Bacarisse

Rosario1903 said:
for me
it is possible compiler 'optimize' recursion with a loop
so a never ending loop

Ding! We have a winner! :)
i say that the compiler not have to optimize what the coder or the
programmer wrote
i'm not agree compiler optimizations...
this is because i think programmer has to see result
of his own routines
the result of each instruction he/her wrote and
not something different

Oh, we *had* a winner. :-(
 
J

Johannes Bauer

i say that the compiler not have to optimize what the coder or the
programmer wrote
i'm not agree compiler optimizations...
this is because i think programmer has to see result
of his own routines

You demonstrate a profound lack of understanding of what C is. Please
state then the official(tm) way a non-optimizing compiler should
translate some idioms in your opinion. You'll be surprised that there's
more than one way.
the result of each instruction he/her wrote and
not something different

In C, you don't write "instructions". You write statements. Those
statements are translated to instructions. Which instructions are chosen
is up to the compiler, period.

Johannes

--
Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa <[email protected]>
 
J

Johann Klammer

Ben said:
Ding! We have a winner! :)

I doubt that this has happened..
The compilation takes place before linking.
The compiler can not know how you are going to use the functions in each
modules. The compiler cannot know what a function in one module does
while compiling another module.
That means, the compiler has to generate the usual entry points.
You are using intel, so it's likely it'll push 4 (or 8) bytes return
address onto the stack. How could it possibly optimize away the function
call itself? AFAIK The linker would have to do that...

Just tried myself.. it segfaulted immediately.
Did you perhaps just call functions inside one module?
gdb session:

Program received signal SIGSEGV, Segmentation fault.
a4 () at a.c:22
22 main();
(gdb) backtrace
#0 a4 () at a.c:22
#1 0x080483fe in main () at b.c:22
(gdb) disass
Dump of assembler code for function a4:
0x08048427 <+0>: push %ebp
0x08048428 <+1>: mov %esp,%ebp
0x0804842a <+3>: sub $0x8,%esp
=> 0x0804842d <+6>: call 0x80483f3 <main>
0x08048432 <+11>: leave
0x08048433 <+12>: ret
End of assembler dump.
(gdb) info regi
eax 0x1 1
ecx 0xbffff614 -1073744364
edx 0x80483f3 134513651
ebx 0xb7fb7ff4 -1208254476
esp 0xbf800000 0xbf800000
ebp 0xbf800008 0xbf800008
esi 0x0 0
edi 0x0 0
eip 0x804842d 0x804842d <a4+6>
eflags 0x10286 [ PF SF IF RF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51

smaps shows stack page is at:
bf801000-c0000000 rw-p 00000000 00:00 0 [stack]
Size: 8192 kB
Rss: 8192 kB
Pss: 8192 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 8192 kB
Referenced: 8192 kB
Anonymous: 8192 kB
AnonHugePages: 0 kB
Swap: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB
VmFlags: rd wr mr mw me gd ac.
 
J

Johannes Bauer

I doubt that this has happened..
Wrong.

The compilation takes place before linking.
Right.

The compiler can not know how you are going to use the functions in each
modules. The compiler cannot know what a function in one module does
while compiling another module.

Also right.
That means, the compiler has to generate the usual entry points.

Again right.
You are using intel, so it's likely it'll push 4 (or 8) bytes return
address onto the stack. How could it possibly optimize away the function
call itself? AFAIK The linker would have to do that...

Wrong!

The compiler DOES generate the appropriate function calls.

However, since the call and the function is in the SAME module, it
inlines the call and therefore optimizes it away in that same module. In
that module, the entry points are therefore never used. If you were to
call it from a different module, it'd have to segfault (unless you were
using LTO, which is still rather uncommon).

Regards,
Joe

--
Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa <[email protected]>
 
J

Johannes Bauer

Wrong!

The compiler DOES generate the appropriate function calls.

However, since the call and the function is in the SAME module, it
inlines the call and therefore optimizes it away in that same module. In
that module, the entry points are therefore never used. If you were to
call it from a different module, it'd have to segfault (unless you were
using LTO, which is still rather uncommon).

Also, thinking about that the example was using more than one module
(reading helps), you're still wrong :)

Calls at the end of functions are also optimized away and replaced by
jumps, reusing the stack frame (if there is one). This is called "tail
call optimization" and explains why it also works across modules.

Regards,
Joe


--
Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa <[email protected]>
 
B

Ben Bacarisse

Johann Klammer said:
I doubt that this has happened..

Doubt is good, but I'm pretty sure I know what happened.
The compilation takes place before linking.
The compiler can not know how you are going to use the functions in
each modules. The compiler cannot know what a function in one module
does while compiling another module.
That means, the compiler has to generate the usual entry points.

Yes, but it knows (or can know) that a1 need do no more than jump to
b1. Compiling with -S confirms that's what it does.
You are using intel, so it's likely it'll push 4 (or 8) bytes return
address onto the stack. How could it possibly optimize away the
function call itself? AFAIK The linker would have to do that...

Actually, gcc's "usual" linker can also do optimisations, but this isn't
one of them.
Just tried myself.. it segfaulted immediately.

But that it *can* go wrong does not mean that it will. I, too, can get
an almost immediate segfault. I am afraid that by being rather
tangential I've failed to make the point I wanted to. All I was
objecting to was the common miss-conception that recursion *must* use
the stack (or some memory resource), and thus unbounded recursion *must*
blow it eventually.
Did you perhaps just call functions inside one module?

Nope.

<snip>
 
E

Eric Sosman

[...]
People have a few different ideas about how to handle this. I have my
own rules that I stick to very rigidly:

For any "module" you have a ".c" file and a ".h" file. Every function
(and file-scope variable) that the module defines is either local to the
module, or exported from the module. If it is local, it is declared as
"static", and then only at its definition. (Occasionally it is useful
with an extra "forward" declaration, but I don't use these unless they
are needed.) If it is to be exported, there is an "extern" declaration
in the module's header file. Modules /always/ #include their own
headers. There should normally never be an "extern" declaration in a
".c" file - keep it strictly in the appropriate header.

Thus here you will have:

a.h
===
#ifndef _a_h_

This choice of macro name is ill-advised, because "All
identifiers that begin with an underscore are always reserved
for use as identifiers with file scope [...]" and "Each [library]
header [...] optionally declares or defines identifiers [...]
which are always reserved either for any use or for use as file
scope identifiers" (both from 7.1.3p1). That is, the name may
already be in use by a standard library header, and using it for
any other purpose could cause trouble.

Some people form inclusion guards by capitalizing the name
of the header file (because macros are usually all-caps) and
appending _H, so the macro for "a.h" would be A_H. Unfortunately,
that practice breaks down when you get to "elevator.h" because
the description of <errno.h> (7.5p4) says "Additional macro
definitions, beginning with E and a digit or E and an uppercase
letter may also be specified by the implementation," so
ELEVATOR_H could be an implementation-defined error macro.

My own practice is to stick the H at the beginning, so the
inclusion guards would be H_A and H_ELEVATOR; these identifiers
are firmly in the programmer's name space. One place I worked
went further, encoding the header file's entire path:

/* graphics/utils/canvas.h */
#ifndef H_GRAPHICS_UTILS_CANVAS

/* nautical/tallship/supplies/canvas.h */
#ifndef H_NAUTICAL_TALLSHIP_SUPPLIES_CANVAS

.... so the two "canvas.h" headers could be used together.
A particularly important check you have here is that the "extern"
declaration is guaranteed to match the definition of the function,
because the same declaration is #include'd while compiling the body of
the function - any mismatches will cause an error.

The same place that used full-path inclusion guards also had
a rule that the .h should be the *first* thing included in the
corresponding .c, so the compiler would detect any failure on
the .h's part to include its own dependencies. That is,

/* path/a.h */
#ifndef H_PATH_A
#define H_PATH_A
extern void oldMill(FILE *stream);
#endif

/* path/a.c, right at the top: */
#include "a.h" // BZZZT! <stdio.h> not included yet

(Some people disapprove of nested inclusions; others feel that
telling a header's user to include four other headers first is
an imposition. I'm in the latter camp.)
 
J

James Kuyper

On 09/24/2013 09:49 AM, David Brown wrote:
....
But anyone who writes code that calls main() (except for at the end of
pre-main startup code) is just asking for trouble. They might not /get/
trouble, but they are asking for it!

Calling main() from within my program has behavior that is well-defined
on any hosted conforming implementation of C (assuming that there's no
problem with any other aspect of your program). It is a legitimate
though rarely used technique for manipulating the command line
arguments. If I make use of that technique, I'm not asking for trouble,
I'm asking for the standard-defined behavior, and if I don't get it,
it's the implementor who's asking for trouble - and I will give it to him.
 
K

Keith Thompson

Johannes Bauer said:
You demonstrate a profound lack of understanding of what C is. Please
state then the official(tm) way a non-optimizing compiler should
translate some idioms in your opinion. You'll be surprised that there's
more than one way.


In C, you don't write "instructions". You write statements. Those
statements are translated to instructions. Which instructions are chosen
is up to the compiler, period.

That's the major difference between assembly languages on the one hand,
and C and other higher-level languages on the other.

Assembly language programs specify instruction sequences.

C programs specify behavior.
 
T

Tim Rentsch

David Brown said:
In a freestanding program, main *may or may not* be special. The
program entry point is implementation-defined; it may well be called
"main".

N1570 5.1.2.1:

In a freestanding environment (in which C program execution
may take place without any benefit of an operating system),
the name and type of the function called at program startup are
implementation-defined. Any library facilities available to a
freestanding program, other than the minimal set required by
clause 4, are implementation-defined.

The effect of program termination in a freestanding environment
is implementation-defined.

It is not uncommon in embedded systems for main to be treated
specially. Some compilers inject extra code at the beginning of
main() as an alternative to doing it in the startup code (typical
"run before main" code includes basic hardware initialisation,
memory setup, stack setup, clearing bss, copying .data from flash,
etc.). [snip]

Any C implementation that puts extra code at the beginning of
main is non-conforming, no matter hosted or freestanding. Any
such code must run before calling the designated entry function
(whether that function is main or some other function).
 
T

Tim Rentsch

Ben Bacarisse said:
I believe you actually can call main from your hosted program in C
(not in C++), although I can't imagine a case where that would
actually be a good idea.

int main(int argc, char *argv[])
{
return argc > 1 ? !process(argv[1]) || main(argc-1, argv+1) : 0;
}

Wouldn't you rather write it in a way that is more likely to
be recognized as an optimizable tail recursion?
? <insert your smiley of choice here>

Ditto. /:
 
J

James Kuyper

On 09/25/2013 04:13 AM, David Brown wrote:
....
Few, if any, embedded C compilers conform /fully/ to any C standard.
For many, there are clear and important differences (as distinct from
mostly irrelevant non-conformities, such as missing parts of the
standard library). Treating "main" as special and injecting code into
it is just one such case - others include treating "const" to mean "this
data is in flash, and must be accessed using flash-space instructions
rather than ram-space instructions", 32-bit doubles, and skipping the
zeroing of uninitialised data (that caused me a lot of "fun" until I
found out the problem).

Those aren't really C compilers, but compilers for C-like languages.
From what you've said, I get the impression that there's very little in
the way of useful statements that can be made about all such compilers;
in order to be useful, statements about embedded C have to include
phrases restricting their applicability to a specific compiler or a
small set of compilers. Such statements are more usefully discussed in
compiler-specific forums.
 
J

James Kuyper

That is true in a very strict sense - ...

It's true in the sense that's relevant to the context of my comment -
it's not particularly useful to discuss the details of how they fail to
conform in a forum that's not dedicated to such compilers.
... but in such a strict sense, there
are very few C compilers for /any/ targets - and even fewer if you want
C99 compilers.

Compilers that have a mode that's fully compliant with C90 are quite
commonplace, at least outside the embedded systems world.
Non-conformities of the severity you describe are quite rare outside
that world.
 
K

Keith Thompson

Tim Rentsch said:
It is not uncommon in embedded systems for main to be treated
specially. Some compilers inject extra code at the beginning of
main() as an alternative to doing it in the startup code (typical
"run before main" code includes basic hardware initialisation,
memory setup, stack setup, clearing bss, copying .data from flash,
etc.). [snip]

Any C implementation that puts extra code at the beginning of
main is non-conforming, no matter hosted or freestanding. Any
such code must run before calling the designated entry function
(whether that function is main or some other function).

How is it non-conforming?

If your concern is the possibility of the code being executed
again if main is called recursively, the injected code could be
something like:

static int __initialized = 0;
if (!__initialized) {
/* startup code */
__initialized = 1;
}

Or the check could be optimized out if the compiler/linker detects
that main isn't called recursively.
 
G

glen herrmannsfeldt

(snip on non-conforming compilers)
I agree that details of such non-conformaties are best handled in
dedicated forums of some sort. But it is certainly fine to discuss them
in general terms here. "Assume main() is special, and don't call it
from other functions - you might get trouble with an odd compiler" is
perfectly good advice, and is entirely on-topic to this group.
OK.

On the other hand, saying "don't cast your non-const pointers to const
pointers in case the compiler treats "const" in a non-standard way"
would be very bad advice except in a forum specifically for such
compilers. In this group, it is enough to say that such compilers /do/
exist, and are in use today - making them more relevant than many of the
odd (but often compliant) compilers with things like 36-bit integers or
ones-compliment arithmetic. If someone asks, I will of course give
details about such awkward non-compliant compilers. Otherwise I'll
leave it at that.

Well, many features are up to the compiler designer to choose, but
in most cases word size of number representation are properties
of the hardware, and not the compiler.

I suppose one could write a compiler using 36 bit int, with a
target of 32 bit hardware, but that doesn't seem likely.

Also, ones complement on twos complement hardware also doesn't seem
so likely, though maybe isn't so hard to do.

It could be interesting to have compilers for emulated hardware,
including emulated 36 bit (PDP-10 and 7090), ones complement
(many CDC machines) or sign magnitude (704, 704, 7090, 7094).

-- glen
 
K

Kenny McCormack

David Brown said:
with PDP mainframes. Therefore I think that these sorts of
non-conforming compliers are relevant [*] in this group, especially in light
of how often odd but conforming architectures get drawn in.

Well, that's where you are just simply wrong, and no amount of arguing or
pleading will ever change that fact.

Lord Kiki and his minions have declared things to be as they are, and those
realities will never change.

[*] Assuming that by the word "relevant", we can read it as "on topic" (the
Holy Grail of this newsgroup).

--
Modern Conservative: Someone who can take time out
from demanding more flag burning laws, more abortion
laws, more drug laws, more obscenity laws, and more
police authority to make warrantless arrests to remind
us that we need to "get the government off our backs".
 
J

James Kuyper

On 09/26/2013 03:20 AM, David Brown wrote:
....
I agree that details of such non-conformaties are best handled in
dedicated forums of some sort. But it is certainly fine to discuss them
in general terms here. "Assume main() is special, and don't call it
from other functions - you might get trouble with an odd compiler" is
perfectly good advice, and is entirely on-topic to this group.

No - the on-topic version of that advice would be "It's perfectly safe
to call main() from other functions, and you should feel free to do so
on those rare occasions when it might be useful. However, be aware that
there's many non-conforming implementations where it might not work as
required by the standard."
 
J

James Kuyper

On 09/26/2013 07:44 AM, David Brown wrote:
....
Just because C allows something, does not make it a good idea! Even if
you don't cause trouble with the compiler, you /will/ cause confusion
for other people who read the code. There are /no/ occasions when
calling main() recursively from other functions is a good idea.

I've seen reasonable uses of the technique, and the resulting code was
no more confusing than any other example I've seen of recursive code. In
fact, it was simpler than any alternative I could come up with at the
time that didn't use a recursive call to main(). I wish I could remember
where I saw it, so I could give you an example. I can understand if
you're unwilling to accept my naked assertion that such code exists.

I think your negative opinion of the technique may have been influenced
by the fact that most of the examples you've ever seen were probably
IOCCC entries.
 
J

James Kuyper

On 09/26/2013 09:48 AM, David Brown wrote:
....
Maybe part of the reason why I react so badly to this sort of thing is
that I work in embedded programming, including high reliability and
sometimes safety-critical systems. I don't do medical or aeronautical
stuff, but even for "normal" industrial use there are much higher
expectations for software quality. This means you do not write code
that uses "tricks" or unusual programming techniques - code must be
clear and it must stick to a subset of the language that makes it easy
to see that it is correct. Readability and maintainability are critical

I don't think that it's legitimately a readability or maintainability
issue. Code that directly or indirectly calls main() is not
intrinsically any less readable or maintainable than any other recursive
code. If you allow recursion at all (and I have heard arguments against
allowing recursion), there's no reason for excluding main() from the
list of functions that can be called recursively.

At least, there's no such reason when using a fully conforming
implementation of C - you've given good reason to avoid it in contexts
where certain specific non-conforming embedded implementations must be
used. Therefore, I do agree that it's probably your background in
embedded programming that colors your opinion on this matter - but not
because of the "high reliability" or "safety critical" issues.
 
B

Ben Bacarisse

David Brown said:
Without seeing the code example in question, I can't be categorical
about readability. It is simply a matter of how people expect C
programs to behave. Recursion is bad enough (in C - it's a different
matter for languages that use it more) - you need to make a particular
effort for clarity when using recursion.

Whilst calling main is, inevitably, recursion, is does not always mean
that you are writing some obscure recursive algorithm. For example, I
once needed to alter a program so that when it was not given some
acceptable arguments, it used some default set. Now I could have gone
though main and added defaults, but the trouble with that was that the
default setting consisted of a collections of arguments, not simple
default for various settings (I don't recall the details). The simplest
and clearest solution was to replace the error message "unsuitable or
conflicting arguments" with a call to main that has the defaults wired
in.
When you write code that is to be clear, accurate, maintainable and
understandable by others, it is not enough to write code that is allowed
by the standards. It must also be allowed by what an appropriately
skilled C programmer /thinks/ is allowed, and it must not require them
to ask other questions or look up standards documents. So if the reader
of the code is asking questions such as "are you /allowed/ to call
main() like that? What happens to static data - does it get initialised
again?", then you have dropped a level in readability.

I don't like this view. For one thing, how on earth do you determine
this lowest common denominator language? But from a personal point of
view, when I read some code I hope and expect that it will use the
language to it's full potential. That way I can learn stuff. I don't
want it to do so just for the sake of it, but if it makes sense, go for
it.
Clearly you can expect different types of experience and different
levels of abilities for programmers working on different types of code -
you don't write all your code with an aim of making things obvious to
the freshest of newbies. But every time you use a technique that /does/
require a certain level of understanding or thought, you are raising the
bar. That is appropriate if it makes the code better in some way, such
as being clearer for programmers of the level you are targeting. My gut
feeling - without having seen the program in question - is that
recursive main() raises this bar without adding anything of benefit, and
almost certainly adds confusion if main() does anything before calling
the recursive functions. So I /do/ think this is a readability and
maintainability issue.

This is an example of the problem of determining the "bar". I would not
have though that calling main would raise any issues at all, but you've
used non-confirming systems where it does (or you are importing C++
knowledge into C -- I'm not sure which but I don't think it matters).
How can I possibly guess what's going to raise a question for you or for
the next programmer to read the code? Everyone is going to have their
own ideas of what the "clearest" version of C is, but the only universal
one is what the language standard permits.

<snip>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,165
Latest member
JavierBrak
Top