We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hi,
This post is about the classic problem creating pointers to function using the overlay procedure.
The reference in the manual suggest the OVERLAY directive to modify the call tree that is automatically generated by the linker. But what happeneds if we don't know the name of the indirect function the pointer calls? Yes this is true.
Consider the possibility of someone who try to build a lib. And this lib provide a function(funA) that calls an afterwards user defined procedure unknown by the lib function(funA). Here an example
//lib_file.h // ... typedef void (* Tpfun)(void); void module_init (Tpfun pf); void module (void); // ... //lib_file.c static Tpfun pfun; //-------------------------------- void module_init (Tpfun pf) { pfun = pf; } //---------------------- void module (void) { //code // ... pfun(); // code }
In this simple program there is no call to module_init(&..) and it is impossible to know the address/name of the indirect function and so it is also impossible to use the OVERLAY directive(i think). We know only the pointer to function "pfun". We also don't know if the indirect function call other functions. The only way to know the name of the indirect function is when the lib user decide to use the module and initializes the module using the module_init(&..);
But how can we build the lib so the user uses it???
The question is:
1)Is there a way to modify the call tree that is automatically generated by the linker in order to include the indirect function? And if there is no way. 2)How can someone enable the normal way of function call using the stack?
I'm using the eval version C51 8.08
The only thing in question is: how should the library gain access to this user-provided function? The 2 choices are: 1. By name; 2. By a pointer.
Since the pointer is a Really Bad Idea for an 8051, that really only leaves 1 option...
If the library is written for '51 purposes (no function pointers but calleable by name) the call tree is not the end users job but the linkers.
Andy, we DO agree. even if "you say tomahtos and I say tomatoes"
Erik
... but i believe that a library must be complete for the end user. How can the user modify the call tree if he doesn't know how the library works. He is just the user.
When i say "complete" i mean that if you are the end user "you don't need to add or modify anything". Not even the call tree. It's a bad idea to ask for the user to know how your library works. And even if you know how to trick the call tree, probably one year later you will not. That's why i choose to use the name of the calleable function and not a pointer to it.
But what is so difficult about pointer to functions and x51. Is it that hard for the linker? In contrast with KEIL i see it like a flaw...
It has NOTHING to do with Keil (well in a way it does - if they had chosen a horrible implementation, there would be no call tree)
It has EVERYTHING to do with the '51 architecture. The '51 is extremely limited re stack space (and fetches via the stack pointer are very processor heavy), and if the call tree had not been implemented you would have a bunch of stack overflows and a very sluggish performance.
Thus, because of the ARCHITECTURE of the '51 you "In contrast with KEIL i see it like a flaw" that Keil did the most effieient inplementation possible.
So, as often said before if you do not want to be concerned with processor architecture, stay away from the '51
When i say "complete" i mean that if you are the end user "you don't need to add or modify anything". Not even the call tree.
I'm quite sure we all understand your wish. It's just that we know better than to expect such wishes to come true.
As I said before, not all those nice design patterns lend themselves well to the way a 8051 works. Arbitrary use of function pointers is not a good idea on an 8051.
In my experience libraries aren't used all that much in C51. You've just come across one of the reasons. Another is that 8051 programs are usually small enough that pre-compiled libraries don't offer significant advantage over re-compilation.
In my experience libraries aren't used all that much in C51 I use them a lot HOWEVER, In my experience libraries for which you do not have access to the source aren't used all that much in C51.
"Is it that hard for the linker?"
If you need to ask that question, then you haven't understood the problem!
It is not just "hard" - it is impossible!
The reason the Linker needs to know the call tree is so that it can know what data can be overlaid.
The Linker needs to do this at the time the project is built - therefore it cannot possibly know what functions might end-up getting called via a pointer assigned at run time!
"with KEIL i see it like a flaw..."
The problem is that the 8051 architecture is inherently unsuited to putting parameters & automatic variables on the stack - which is what "normal" compilers do. Keil's "workaround" for this is to put parameters & automatic variables into fixed memory locations. Of course, this has some drawbacks - which is why "normal" compilers don't do it when they can use the stack!
Overlaying then recovers some of this fixed memory usage, by allowing mutually-exclusive functions to share memory locations - effectively, this does at link time what a real stack does at runtime.
See: http://www.keil.com/appnotes/docs/apnt_129.asp
effectively, this does at link time what a real stack does at runtime.
Which means that recursive function call chains - or unknown call chains (pointers) - are not suited for a '51 processor.
I don't know about any other architecture where the linker knows about the internals of all functions. A linker normally have to fixup addresses depending on order and size of individual modules.
What the Keil linker does is a hack. But it is the only known method to make the '51 processor at least reasonably compatible with any high och medium level language. Keil is basically continuing something that was started with the PLM51 language from Intel.
"What the Keil linker does is a hack. But it is the only known method to make the '51 processor at least reasonably compatible with any high or medium level language."
Exactly!
It brings its problems - like all this call-tree analysis stuff - but can the OP suggest a better way?
I wouldn't call it a hack, so much as a fairly powerful optimization. Most C compilers generate code that spends a lot of run time pushing and popping onto a stack, and uses indirect references to access commonly used variables. The call tree analysis allows you to eliminate all that code and directly reference locals, temps, and parameters. Sometimes it would be nice to have this option on other architectures, even those that are much better suited to maintaining a stack and indirect references. For a great many programs, you don't actually need the features that all that run-time stack manipulation provides.
The cost of eliminating the stack, however, is fairly severe -- no recursion, no reentrancy, and limitations on the use of function pointers.
If you want, though, you can just declare all your functions reentrant and get the behavior you'd expect of a "normal" C compiler without the "hack". I don't think you'll like the results on the 8051 so much.
Most C compilers generate code that spends a lot of run time pushing and popping onto a stack, and uses indirect references to access commonly used variables.
No, they don't.
Most processors have a suitable set of instructions for implementing a stack, and suitable base pointer registers for accessing parameters and auto variables relative the current stack frame.
At function entry, a typical processor has to capture the current stack pointer as a base address for accessing parameters and auto variables. It then as to adjust the stack pointer to allocate room for auto variables. This can often be done with two instructions - neither instruction requiring any memory reference.
At function exit, a typical processor has to restore the current stack pointer, which can normally be done with a single register assign.
Standard pipelining can normally perform multiple instructions concurrently (especially instructions that doesn't require explicit memory accesses).
The net result is that for most programs, a very small part of the processor capacity is used for the stack manipulation.
The exception is when a large number of calls are to miniature functions with very few instructions in them. But in such cases, more processor capacity is normally lost in cache misses than in the stack manipulation. But then the normal - and trivially simple - way to handle such cases (if it is important) is to inline tiny functions. Either automatically, or by use of an inline keyword. The removal of the call statement will in many cases allow inlining without increasing the code size. And good code optimizers will perform the optimization after the inlining, allowing prefetches and optimum use of registers etc.
I wouldn't call it a hack, so much as a fairly powerful optimization.
The reason I called the Keil linker work a hack is because it is a very heavy-duty work-around to make the C51 processor do tricks it was specifically not inteded to do. It is a hack because it isn't a general solution to the problem. It can only perform it's workaround magic for a specific limited set of problems, and in some situations the user must be knowledgeable enough to help the compiler/linker. If it was a general solution and not a hack, you would never need to specify if a function is reentrant or not.
For other processor architectures, it is normally enough to specify if you want the compiler to optimize for speed, size or "debugability".
and in some situations the user must be knowledgeable enough to help the compiler/linker. If it was a general solution and not a hack, you would never need to specify if a function is reentrant or not.
oh WOW, I did not know that. I always thought you were supposed to know the tools you worked with.