EmbeddedRelated.com
Forums

Getting the size of a C function

Started by john January 22, 2010
In article <RdadncEft4Y4IsHWnZ2dnUVZ8mOdnZ2d@lyse.net>, 
david.brown@hesbynett.removethisbit.no says...
> Mark Borgerson wrote: > > In article <pan.2010.01.23.05.08.12.672000@nowhere.com>, > > nobody@nowhere.com says... > >> On Fri, 22 Jan 2010 22:53:18 +0000, john wrote: > >> > >>> I need to know the size of a function or module because I need to > >>> temporarily relocate the function or module from flash into sram to > >>> do firmware updates. > >> Do you need to be able to run it from RAM? If so, simply memcpy()ing it > >> may not work. And you would also need to copy anything which the function > >> calls (just because there aren't any explicit function calls in the source > >> code, that doesn't mean that there aren't any in the resulting object code). > >> > >> > > At the expense of a few words of code and a parameter, you could do > > > > > > int MoveMe(...., bool findend){ > > if(!findend){ > > > > // do all the stuff the function is supposed to do > > > > } else Markend(); > > > > } > > > > > > Where Markend is a function that pulls the return > > address off the stack and stashes it somewhere > > convenient. Markend may have to have some > > assembly code. External code can then > > subtract the function address from the address > > stashed by Markend(), add a safety margin, and > > know how many bytes to move to RAM. > > > > > > Mark Borgerson > > > > Anything that relies on the compiler being stupid, or deliberately > crippled ("disable all optimisations") or other such nonsense is a bad > solution. It is conceivable that it might happen to work - /if/ you can > get the compiler in question to generate bad enough code. But it is > highly dependent on the tools in question, and needs to be carefully > checked at the disassembly level after any changes. > > In this particular example of a highly risky solution, what happens when > the compiler generates proper code? The compiler is likely to generate > the equivalent of : > > int MoveMe(..., bool findend) { > if (findend) "jump" Markend(); > // do all the stuff > } > > Or perhaps it will inline Markend, MoveMe, or both. Or maybe it will > figure out that MoveMe is never called with "findend" set, and thus > optimise away that branch. All you can be sure of, is that there is no > way you can demand that a compiler produces directly the code you > apparently want it to produce - C is not assembly. >
That's true. But it is also true that you can verify that a particular compiler DOES produce the desired code an use that code effectively. For embedded programming, it doesn't particularly matter if 50 other compilers don't produce what you want, as long as the compiler your are using does. Mark Borgerson
In article <fbCdncswcJUMXMHWnZ2dnUVZ8vednZ2d@lyse.net>, 
david.brown@hesbynett.removethisbit.no says...
> Mark Borgerson wrote: > > In article <slrnhlmqth.1evp.willem@turtle.stack.nl>, willem@stack.nl > > says... > >> Mark Borgerson wrote: > >> ) In article <87tyucpp7x.fsf@blp.benpfaff.org>, blp@cs.stanford.edu > >> ) says... > >> )> Mark Borgerson <mborgerson@comcast.net> writes: > >> )> > >> )> > In article <87y6jopsl9.fsf@blp.benpfaff.org>, blp@cs.stanford.edu > >> )> > says... > >> )> >> Mark Borgerson <mborgerson@comcast.net> writes: > >> )> >> You seem to be assuming that the compiler emits machine code that > >> )> >> is in the same order as the corresponding C code, i.e. that the > >> )> >> call to Markend() will occur at the end of MoveMe(). This is not > >> )> >> a good assumption. > >> )> > > >> )> > This would certainly be a dangerous technique on a processor > >> )> > with multi-threading and possible out-of-order execution. > >> )> > I think it will work OK on the MSP430 that is the CPU where > >> )> > I am working on a flash-burning routine. > >> )> > >> )> Threading and out-of-order execution has little if anything to do > >> )> with it. The issue is the order of the code emitted by compiler, > >> )> not the order of the code's execution. > >> )> > >> ) But woudn't an optimizing compiler generating code for a > >> ) complex processor be more likely to compile optimize in > >> ) a way that changed the order of operations? I think > >> ) that might apply particularly to a call to a function > >> ) that returns no result to be used in a specific > >> ) place inside the outer function. > >> > > You get good and bad compilers for all sorts of processors, and even a > half-decent one will be able to move code around if it improves the > speed or size of the target - something that can apply on any size of > processor. > > <snip> > > > > > In any of these instances, I would certainly review > > the assembly code to make sure the compiler was doing > > what I intended in the order I wanted. Maybe programmers > > in comp.lang.c don't do that as often as programmers > > in comp.arch.embedded. ;-) > > > > I don't know about typical "comp.lang.c" programmers, but typical > "comp.arch.embedded" programmers use compilers that generate tight code, > and they let the compiler do its job without trying to force the tools > into their way of thinking. At least, that's the case for good embedded > programmers - small and fast code means cheap and reliable > microcontrollers in this line of work. And code that has to be > disassembled and manually checked at every change is not reliable or > quality code. >
None of that is at odds with writing a flash update routine once, verifying that the end of the code is properly marked and using the code. If you are worried about changes in optimization levels for future compiles, you can generate a binary library for the flash update function and link that in future applications. AFAIK, linking a library does not generally result in any change in the binary code of the library if the library was generated from position-independent code. (And, if your going to copy a function to a differnt location for execution, it had better be position-independent.) That said, I will have to look very carefully at some of the MSP430 code that I have generated---the compiler may access I/O locations using PC-relative addressing. That would totally mess up code that got simply copied to RAM. However, that's an altogether different problem than simply finding the length of the function. Mark Borgerson
On 25/01/2010 00:19, Jon Kirwan wrote:
> On Sun, 24 Jan 2010 22:53:01 +0100, David Brown > <david.brown@hesbynett.removethisbit.no> wrote: > >> Mark Borgerson wrote: >>> In article<slrnhlmqth.1evp.willem@turtle.stack.nl>, willem@stack.nl >>> says... >>>> Mark Borgerson wrote: >>>> ) In article<87tyucpp7x.fsf@blp.benpfaff.org>, blp@cs.stanford.edu >>>> ) says... >>>> )> Mark Borgerson<mborgerson@comcast.net> writes: >>>> )> >>>> )> > In article<87y6jopsl9.fsf@blp.benpfaff.org>, blp@cs.stanford.edu >>>> )> > says... >>>> )> >> Mark Borgerson<mborgerson@comcast.net> writes: >>>> )> >> You seem to be assuming that the compiler emits machine code that >>>> )> >> is in the same order as the corresponding C code, i.e. that the >>>> )> >> call to Markend() will occur at the end of MoveMe(). This is not >>>> )> >> a good assumption. >>>> )> > >>>> )> > This would certainly be a dangerous technique on a processor >>>> )> > with multi-threading and possible out-of-order execution. >>>> )> > I think it will work OK on the MSP430 that is the CPU where >>>> )> > I am working on a flash-burning routine. >>>> )> >>>> )> Threading and out-of-order execution has little if anything to do >>>> )> with it. The issue is the order of the code emitted by compiler, >>>> )> not the order of the code's execution. >>>> )> >>>> ) But woudn't an optimizing compiler generating code for a >>>> ) complex processor be more likely to compile optimize in >>>> ) a way that changed the order of operations? I think >>>> ) that might apply particularly to a call to a function >>>> ) that returns no result to be used in a specific >>>> ) place inside the outer function. >>>> >> >> You get good and bad compilers for all sorts of processors, and even a >> half-decent one will be able to move code around if it improves the >> speed or size of the target - something that can apply on any size of >> processor. >> >> <snip> >> >>> >>> In any of these instances, I would certainly review >>> the assembly code to make sure the compiler was doing >>> what I intended in the order I wanted. Maybe programmers >>> in comp.lang.c don't do that as often as programmers >>> in comp.arch.embedded. ;-) >>> >> >> I don't know about typical "comp.lang.c" programmers, but typical >> "comp.arch.embedded" programmers use compilers that generate tight code, >> and they let the compiler do its job without trying to force the tools >> into their way of thinking. At least, that's the case for good embedded >> programmers - small and fast code means cheap and reliable >> microcontrollers in this line of work. And code that has to be >> disassembled and manually checked at every change is not reliable or >> quality code. > > You give me a great way to segue into something. There are > cases where you simply have no other option than to do > exactly that. I'll provide one example. There are others. >
In embedded development, /every/ rule has an exception, except this one :-). There are definitely times when you have to manually check your outputs, or write code that only works with specific compiler options, or add assembly code hacks that rely on details of the compiler working. But you don't do it unless you have no better way - you certainly don't design in your hacks at the first step. Another rule for embedded development is always know your tools, and preferably pick /good/ tools. Microchip are known to be good for many things - the quality of their 16-bit PIC C compilers is definitely not one of them.
> I was working on a project using the PIC18F252 processor and, > at the time, the Microchip c compiler was in its roughly-v1.1 > incarnation. We'd spent about 4 months in development time > and the project was nearing completion when we discovered an > intermittent (very rarely occurred) problem in testing. Once > in a while, the program would emit strange outputs that we > simply couldn't understand when closely examining and walking > through the code that was supposed to generate that output. > It simply wasn't possible. Specific ASCII characters were > being generated that simply were not present in the code > constants. > > In digging through the problem, by closely examining the > generated assembly output, I discovered one remarkable fact > that led me to imagine a possibility that might explain > things. The Microchip c compiler was using static variables > for compiler temporaries. And it would _spill_ live > variables that might be destroyed across a function call into > them. They would be labelled something like __temp0 and the > like. > > There was _no_ problem when the c compiler was doing that for > calls made to functions within the same module, because they > had anticipated that there might be more than one compiler > temporary needed in nested calls and they added the extra > code in the c compiler to observe if a decendent function, > called by a parent, would also need to spill live variables > and would then construct more __temp1... variables to cover > that case. Not unlike what good 8051 compilers might do when > generating static variable slots for nested call parameters > for efficiency (counting spills all the way down, so to > speak.) > > However, when calling functions in _other_ modules, where the > c compiler had _no_ visibility about what it had already done > over there on a separate compilation, it had no means to do > that and, of course, there became a problem. What was > spilled into __temp0 in module-A was also spilled into > __temp0 in module-B and, naturally, I just happened to have a > case where that became a problem under the influence of > interrupt processing. I had completely saved _all_ registers > at the moment of the interrupt code before attempting to call > any c functions, of course. That goes without saying. But > I'd had _no_ idea that I might have to save some statics > which may, or may not, at the time be "live." > > Worse, besides the fact that there was no way I could know in > advance which naming the c compiler would use in any > circumstance, the c compiler chose these names in such a way > that they were NOT global or accessible either to c code or > to assembly. I had to actually _observe_ in the linker file > the memory location where they resided and make sure that the > interrupt routine protected them, as well. > > This required me to document a procedure where every time we > made a modification to the code that might _move_ the > location of these compiiler generated statics, we had to > update a #define constant to reflect it, and then recompile > again. > > Got us by. > > Whether it is _reliable_ or not would be another debate. The > resulting code was very reliable -- no problems at all. > However, the process/procedures we had to apply were not > reliable, of course, because we might forget to apply the > documented procedure before release. So on that score, sure. > > Life happens. Oh, well. > > Jon
On 25/01/2010 00:13, James Harris wrote:
> On 24 Jan, 21:44, David Brown<david.br...@hesbynett.removethisbit.no> > wrote: > ... >> Anything that relies on the compiler being stupid, or deliberately >> crippled ("disable all optimisations") or other such nonsense is a bad >> solution. > > I *think* Mark is aware of the limitations of his suggestion but there > seems to be no C way to solve the OP's problem. It does sound like the > problem only needs to be solved as a one-off in a particular > environment. >
You are correct that there is no standard C way to solve the problem. But for the majority of compilers used in embedded development, there are ways that will reliably solve this problem when working /with/ the compiler, rather than /against/ the compiler. We are not trying to get a highly portable solution here, but it is always better to find a design that could be reused if possible. And it is always better to work with the features of your toolset, especially when there is no standard C solution, rather than trying to find ways to limit your tools. For this problem, the best solution is generally to use a specific section for the functions in question. This can often be done using the gcc "__attribute__" syntax (even for non-gcc compilers), or by using compiler-specific pragmas. Any tools suitable for embedded development will support something to this effect, and give you control over the linking and placement of the function (this is assuming, of course, you are working with a microcontroller that supports execution from ram). The details of how you do this depend on the situation. For example, you may be happy to dedicate the required ram space to the function, or you may want to copy it into ram only when needed. The former case is the easiest, as you can arrange for the linker to put the code in flash, but linked as though it were in ram. There is no need for any position-independent code, and you can happily debug and step through the code in ram. You can often "cheat" and put the code in the ".data" section, then you don't even have to think about the linker file or copying over the function - the C startup code handles that (since it treats the function like initialised data). With gcc on the msp430, you have your function defined something like this: static void critical __attribute__ ((section(".data"))) progflash(...) Of course, you still have to ensure that the function doesn't call other functions - or that these are also in ram. And it is worth checking the disassembly here if you are not sure - it is easy to accidentally include library functions calls. But the difference is that you have a reliable and safe way to achieve the effect you want, that is independent of details such as the compiler flags or precise compiler version, and will continue to work even if the source is changed. Because you are working /with/ the tools, you can take full advantage of debugging and optimisation. And though the details may vary for different processors or toolchains, the principle can be re-used. As with all code that cannot be implemented in standard C, there is always the possibility of this solution failing with future compilers or different devices, and you must check the results carefully - but this is the best you can get.
> That said, what about taking function pointers for all functions and > sorting their values? It still wouldn't help with the size of the last > function. Can we assume the data area would follow the code? I guess > not. >
You can't make any assumptions about the ordering of code or data. You cannot practically speaking make function pointers for all functions without a great deal of effort, and making an unnecessary pointer to a function cripples the compiler's optimisations of that function and functions that call it.
On 25/01/2010 05:48, Mark Borgerson wrote:
> In article<RdadncEft4Y4IsHWnZ2dnUVZ8mOdnZ2d@lyse.net>, > david.brown@hesbynett.removethisbit.no says... >> Mark Borgerson wrote: >>> In article<pan.2010.01.23.05.08.12.672000@nowhere.com>, >>> nobody@nowhere.com says... >>>> On Fri, 22 Jan 2010 22:53:18 +0000, john wrote: >>>> >>>>> I need to know the size of a function or module because I need to >>>>> temporarily relocate the function or module from flash into sram to >>>>> do firmware updates. >>>> Do you need to be able to run it from RAM? If so, simply memcpy()ing it >>>> may not work. And you would also need to copy anything which the function >>>> calls (just because there aren't any explicit function calls in the source >>>> code, that doesn't mean that there aren't any in the resulting object code). >>>> >>>> >>> At the expense of a few words of code and a parameter, you could do >>> >>> >>> int MoveMe(...., bool findend){ >>> if(!findend){ >>> >>> // do all the stuff the function is supposed to do >>> >>> } else Markend(); >>> >>> } >>> >>> >>> Where Markend is a function that pulls the return >>> address off the stack and stashes it somewhere >>> convenient. Markend may have to have some >>> assembly code. External code can then >>> subtract the function address from the address >>> stashed by Markend(), add a safety margin, and >>> know how many bytes to move to RAM. >>> >>> >>> Mark Borgerson >>> >> >> Anything that relies on the compiler being stupid, or deliberately >> crippled ("disable all optimisations") or other such nonsense is a bad >> solution. It is conceivable that it might happen to work - /if/ you can >> get the compiler in question to generate bad enough code. But it is >> highly dependent on the tools in question, and needs to be carefully >> checked at the disassembly level after any changes. >> >> In this particular example of a highly risky solution, what happens when >> the compiler generates proper code? The compiler is likely to generate >> the equivalent of : >> >> int MoveMe(..., bool findend) { >> if (findend) "jump" Markend(); >> // do all the stuff >> } >> >> Or perhaps it will inline Markend, MoveMe, or both. Or maybe it will >> figure out that MoveMe is never called with "findend" set, and thus >> optimise away that branch. All you can be sure of, is that there is no >> way you can demand that a compiler produces directly the code you >> apparently want it to produce - C is not assembly. >> > That's true. But it is also true that you can verify that a particular > compiler DOES produce the desired code an use that code effectively. > For embedded programming, it doesn't particularly matter if 50 > other compilers don't produce what you want, as long as the compiler > your are using does. >
True enough - but it /does/ matter that the compiler you are using produces the code you want each of the 50 times you change and compile the program, or when you change the compiler flags and compile them, or when you update the compiler and recompile (I recommend keeping exactly the same compiler version for any given project, but sometimes that is not practical). If you have code that relies on working around the compiler, you need to check it /every/ time, and you are never able to take advantage of your tools to generate the best code.
On Mon, 25 Jan 2010 10:21:59 +0100, David Brown
<david@westcontrol.removethisbit.com> wrote:

>On 25/01/2010 00:19, Jon Kirwan wrote: >> <snip> >> You give me a great way to segue into something. There are >> cases where you simply have no other option than to do >> exactly that. I'll provide one example. There are others. > >In embedded development, /every/ rule has an exception, except this one :-).
:)
>There are definitely times when you have to manually check your outputs, >or write code that only works with specific compiler options, or add >assembly code hacks that rely on details of the compiler working. But >you don't do it unless you have no better way - you certainly don't >design in your hacks at the first step.
Just to be argumentative (no other good reason, really), one of my applications requires equal execution times across two code edges. In other words, the execution time must be constant regardless which branch is taken. c doesn't provide for that, quite simply. So the very first thing I do porting this application to a new processor is to ensure that I can achieve this well, or if not, exactly what the variability will be (because I must then relax the clocking rate to account for it.) It's one of those unknowns that must be locked down, immediately. So yes, I hack at the very first step in this case. But I'm just toying. In general, I take your point here. ..... As an aside, one of the first things I may do with a new c compiler and target is to explore methods to support process semantics. The c language doesn't provide quite a number of very useful semantics, this being one of them. (Another I enjoy the use of is named, link-time constants. They are not variable instances, in case you are confused about my wording here. Instead, they are much like #define in c except that these constants are link-time, not compile- time, and if you change them there is no need to recompile all the c code that uses them. You just change one file that creates those constants and re-link. The linker patches in the values, directly. Saves recompile time. Probably every assembler supports them, and every linker _must_ support them. But c does not provide syntax to access the semantic that is available in its own linker.) With cooperative switching (and I use that where possible, because it is much easier to implement and support) I may be able to write fairly simple routines in assembly to support it (a dozen lines, or two.) But there is no escaping the idea that whatever I do there relies on details about the compiler. Different compilers on the MSP430, for example, make different choices about register assignments, which must be preserved across calls, which are scratchable, and which are used to optionally pass parameters (and the conditions under which registers may be chosen to pass them.) With pre-emptive switching, it opens up a Pandora's box. Library routines that may use static memory, for example. But if pre-emptive switching is a part of the product, then I face the problems squarely and usually up front in the development. It's crucial to know exactly what works and how well it works, right away. I also enjoy the use of coroutine thunking, from time to time. This, and process semantics, make for clear, very readable code that works well and is able to be maintained by a broader range of programmers (so long as they don't try and rewrite the core o/s code, of course.) I still take your point. But I hope you don't mind a small moment of banter just to add to your suggestion that every rule has exceptions, including the rule of not hacking things at the outset. ;)
>Another rule for embedded development is always know your tools, and >preferably pick /good/ tools. Microchip are known to be good for many >things - the quality of their 16-bit PIC C compilers is definitely not >one of them. ><snip>
Well, there is that. I cannot defend their use of _static_ memory for compiler temporaries, as they chose to do. It's unconscionable. Their argument to me (one or two of those who actually _wrote_ its code) was that it led to faster emitted code -- in short, it appeared to show off their parts better. And they felt they "had it covered." Well, they were wrong and a false bargain was made. I'm sure they aren't the only ones guilty of choosing to sell the smell of sizzle over the quality of meat, though. Not by a long shot. Jon
On 25/01/2010 11:26, Jon Kirwan wrote:
> On Mon, 25 Jan 2010 10:21:59 +0100, David Brown > <david@westcontrol.removethisbit.com> wrote: > >> On 25/01/2010 00:19, Jon Kirwan wrote: >>> <snip> >>> You give me a great way to segue into something. There are >>> cases where you simply have no other option than to do >>> exactly that. I'll provide one example. There are others. >> >> In embedded development, /every/ rule has an exception, except this one :-). > > :) > >> There are definitely times when you have to manually check your outputs, >> or write code that only works with specific compiler options, or add >> assembly code hacks that rely on details of the compiler working. But >> you don't do it unless you have no better way - you certainly don't >> design in your hacks at the first step. > > Just to be argumentative (no other good reason, really), one
Being argumentative /is/ a good reason if it makes us think.
> of my applications requires equal execution times across two > code edges. In other words, the execution time must be > constant regardless which branch is taken. c doesn't provide > for that, quite simply. So the very first thing I do porting > this application to a new processor is to ensure that I can > achieve this well, or if not, exactly what the variability > will be (because I must then relax the clocking rate to > account for it.) It's one of those unknowns that must be > locked down, immediately. > > So yes, I hack at the very first step in this case. But I'm > just toying. In general, I take your point here. >
That's an example of when you need special consideration. My point is that you only do that sort of thing if you have no better way to implement the required functionality.
> ..... > > As an aside, one of the first things I may do with a new c > compiler and target is to explore methods to support process > semantics. The c language doesn't provide quite a number of > very useful semantics, this being one of them. > > (Another I enjoy the use of is named, link-time constants. > They are not variable instances, in case you are confused > about my wording here. Instead, they are much like #define > in c except that these constants are link-time, not compile- > time, and if you change them there is no need to recompile > all the c code that uses them. You just change one file that > creates those constants and re-link. The linker patches in > the values, directly. Saves recompile time. Probably every > assembler supports them, and every linker _must_ support > them. But c does not provide syntax to access the semantic > that is available in its own linker.) >
Are you talking about using constants in your code which are evaluated at link time, much in the way that static addresses are handled? Maybe I've misunderstood you, but that strikes me as a poor way to handle what are really compile-time constants - it's bad modularisation and structure (sometimes a single file is the best place to put these constants - but it should be because that's the best place, not because you want to fit some weird way of compiling). It is highly non-standard, potentially leading to confusion and maintenance issues. It also limits the compiler's options for optimising the code. And if re-compilation time is a serious issue these days, you need to consider getting better tools (PC and/or compiler), or making better use of them (better makefile setup, or use ccache). Of course, it is always fun getting your tools to do interesting things in unusual ways - but it's not always a good idea for real work.
> With cooperative switching (and I use that where possible, > because it is much easier to implement and support) I may be > able to write fairly simple routines in assembly to support > it (a dozen lines, or two.) But there is no escaping the > idea that whatever I do there relies on details about the > compiler. Different compilers on the MSP430, for example, > make different choices about register assignments, which must > be preserved across calls, which are scratchable, and which > are used to optionally pass parameters (and the conditions > under which registers may be chosen to pass them.) >
Yes, these are more examples of where you need to work with the compiler details.
> With pre-emptive switching, it opens up a Pandora's box. > Library routines that may use static memory, for example. But > if pre-emptive switching is a part of the product, then I > face the problems squarely and usually up front in the > development. It's crucial to know exactly what works and how > well it works, right away. > > I also enjoy the use of coroutine thunking, from time to > time. This, and process semantics, make for clear, very > readable code that works well and is able to be maintained by > a broader range of programmers (so long as they don't try and > rewrite the core o/s code, of course.) > > I still take your point. But I hope you don't mind a small > moment of banter just to add to your suggestion that every > rule has exceptions, including the rule of not hacking things > at the outset. ;) > >> Another rule for embedded development is always know your tools, and >> preferably pick /good/ tools. Microchip are known to be good for many >> things - the quality of their 16-bit PIC C compilers is definitely not >> one of them. >> <snip> > > Well, there is that. I cannot defend their use of _static_ > memory for compiler temporaries, as they chose to do. It's > unconscionable. Their argument to me (one or two of those > who actually _wrote_ its code) was that it led to faster > emitted code -- in short, it appeared to show off their parts > better. And they felt they "had it covered." >
It is certainly perfectly possible to use static memory for compiler temporaries, and it will certainly be faster than the normal alternative (temporaries on a stack) for many small processors. But it has to be implemented correctly!
> Well, they were wrong and a false bargain was made. > > I'm sure they aren't the only ones guilty of choosing to sell > the smell of sizzle over the quality of meat, though. Not by > a long shot. > > Jon
In article <lnzl4538ew.fsf@nuthaus.mib.org>, kst-u@mib.org says...
> WangoTango <Asgard24@mindspring.com> writes: > > In article <hjda8u$t4k$1@speranza.aioe.org>, john@nospam.com says... > >> I need to know the size of a function or module because I need to > >> temporarily relocate the function or module from flash into sram to > >> do firmware updates. > >> > >> How can I determine that at runtime? The > >> sizeof( myfunction) > >> generates an error: "size of function unknown". > >> > > Good question, and I would like to know if there is an easy way to do it > > during runtime, and a portable way would be nice too. I would probably > > look at the map file and use the size I calculated from there, but > > that's surely not runtime. > > > > You can get the starting address of the function pretty easy, but how > > about the end? Hmmm, gotta' think about that. > > You can't even portably assume that &func is the memory address of the > beginning of the function. I think there are systems (AS/400) where > function pointers are not just machine addresses. > > Given whatever it is you're doing, you're probably not too concerned > with portability, so that likely not to be an issue. But there's no > portable way in C to determine the size of a function, so you're more > likely to get help somewhere other than comp.lang.c.
First off, I think you might be confusing me with the OP, and he did cross post to comp.arch.embedded. Anyway, I agree that this is a lot trickier than just using the sizeof operator or doing some pointer math. That's why it is a head scratcher. 'I' think that this is a very target/compiler specific issue/problem, but I have been wrong before. That's why I left it open for someone that may have already cracked this nut. I don't have any experience doing what the OP wants to do, and haven't experimented in any way shape or form. I think I would write the function in assembly so I knew EXACTLY what was going on and I knew there were no external dependencies on library code or jumps to other functions, and go from there.
In article <4b5d6978$0$6274$8404b019@news.wineasy.se>, 
david@westcontrol.removethisbit.com says...
> On 25/01/2010 05:48, Mark Borgerson wrote: > > In article<RdadncEft4Y4IsHWnZ2dnUVZ8mOdnZ2d@lyse.net>, > > david.brown@hesbynett.removethisbit.no says... > >> Mark Borgerson wrote: > >>> In article<pan.2010.01.23.05.08.12.672000@nowhere.com>, > >>> nobody@nowhere.com says... > >>>> On Fri, 22 Jan 2010 22:53:18 +0000, john wrote: > >>>> > >>>>> I need to know the size of a function or module because I need to > >>>>> temporarily relocate the function or module from flash into sram to > >>>>> do firmware updates. > >>>> Do you need to be able to run it from RAM? If so, simply memcpy()ing it > >>>> may not work. And you would also need to copy anything which the function > >>>> calls (just because there aren't any explicit function calls in the source > >>>> code, that doesn't mean that there aren't any in the resulting object code). > >>>> > >>>> > >>> At the expense of a few words of code and a parameter, you could do > >>> > >>> > >>> int MoveMe(...., bool findend){ > >>> if(!findend){ > >>> > >>> // do all the stuff the function is supposed to do > >>> > >>> } else Markend(); > >>> > >>> } > >>> > >>> > >>> Where Markend is a function that pulls the return > >>> address off the stack and stashes it somewhere > >>> convenient. Markend may have to have some > >>> assembly code. External code can then > >>> subtract the function address from the address > >>> stashed by Markend(), add a safety margin, and > >>> know how many bytes to move to RAM. > >>> > >>> > >>> Mark Borgerson > >>> > >> > >> Anything that relies on the compiler being stupid, or deliberately > >> crippled ("disable all optimisations") or other such nonsense is a bad > >> solution. It is conceivable that it might happen to work - /if/ you can > >> get the compiler in question to generate bad enough code. But it is > >> highly dependent on the tools in question, and needs to be carefully > >> checked at the disassembly level after any changes. > >> > >> In this particular example of a highly risky solution, what happens when > >> the compiler generates proper code? The compiler is likely to generate > >> the equivalent of : > >> > >> int MoveMe(..., bool findend) { > >> if (findend) "jump" Markend(); > >> // do all the stuff > >> } > >> > >> Or perhaps it will inline Markend, MoveMe, or both. Or maybe it will > >> figure out that MoveMe is never called with "findend" set, and thus > >> optimise away that branch. All you can be sure of, is that there is no > >> way you can demand that a compiler produces directly the code you > >> apparently want it to produce - C is not assembly. > >> > > That's true. But it is also true that you can verify that a particular > > compiler DOES produce the desired code an use that code effectively. > > For embedded programming, it doesn't particularly matter if 50 > > other compilers don't produce what you want, as long as the compiler > > your are using does. > > > > True enough - but it /does/ matter that the compiler you are using > produces the code you want each of the 50 times you change and compile > the program, or when you change the compiler flags and compile them, or > when you update the compiler and recompile (I recommend keeping exactly > the same compiler version for any given project, but sometimes that is > not practical). If you have code that relies on working around the > compiler, you need to check it /every/ time, and you are never able to > take advantage of your tools to generate the best code. >
Unless, you put the function into a separately-compiled library to be linked in when you build the program the next 50 times. If you change compilers, you may have to rebuild and verify the library. Mark Borgerson
On Mon, 25 Jan 2010 13:13:37 +0100, David Brown
<david@westcontrol.removethisbit.com> wrote:

>On 25/01/2010 11:26, Jon Kirwan wrote: >> On Mon, 25 Jan 2010 10:21:59 +0100, David Brown >> <david@westcontrol.removethisbit.com> wrote: >> >>> On 25/01/2010 00:19, Jon Kirwan wrote: >>>> <snip> >>>> You give me a great way to segue into something. There are >>>> cases where you simply have no other option than to do >>>> exactly that. I'll provide one example. There are others. >>> >>> In embedded development, /every/ rule has an exception, except this one :-). >> >> :) >> >>> There are definitely times when you have to manually check your outputs, >>> or write code that only works with specific compiler options, or add >>> assembly code hacks that rely on details of the compiler working. But >>> you don't do it unless you have no better way - you certainly don't >>> design in your hacks at the first step. >> >> Just to be argumentative (no other good reason, really), one > >Being argumentative /is/ a good reason if it makes us think. > >> of my applications requires equal execution times across two >> code edges. In other words, the execution time must be >> constant regardless which branch is taken. c doesn't provide >> for that, quite simply. So the very first thing I do porting >> this application to a new processor is to ensure that I can >> achieve this well, or if not, exactly what the variability >> will be (because I must then relax the clocking rate to >> account for it.) It's one of those unknowns that must be >> locked down, immediately. >> >> So yes, I hack at the very first step in this case. But I'm >> just toying. In general, I take your point here. > >That's an example of when you need special consideration. My point is >that you only do that sort of thing if you have no better way to >implement the required functionality.
Understood, and agreed.
>> ..... >> >> As an aside, one of the first things I may do with a new c >> compiler and target is to explore methods to support process >> semantics. The c language doesn't provide quite a number of >> very useful semantics, this being one of them. >> >> (Another I enjoy the use of is named, link-time constants. >> They are not variable instances, in case you are confused >> about my wording here. Instead, they are much like #define >> in c except that these constants are link-time, not compile- >> time, and if you change them there is no need to recompile >> all the c code that uses them. You just change one file that >> creates those constants and re-link. The linker patches in >> the values, directly. Saves recompile time. Probably every >> assembler supports them, and every linker _must_ support >> them. But c does not provide syntax to access the semantic >> that is available in its own linker.) > >Are you talking about using constants in your code which are evaluated >at link time, much in the way that static addresses are handled? Maybe >I've misunderstood you, but that strikes me as a poor way to handle what >are really compile-time constants - it's bad modularisation and >structure (sometimes a single file is the best place to put these >constants - but it should be because that's the best place, not because >you want to fit some weird way of compiling). It is highly >non-standard, potentially leading to confusion and maintenance issues. >It also limits the compiler's options for optimising the code. And if >re-compilation time is a serious issue these days, you need to consider >getting better tools (PC and/or compiler), or making better use of them >(better makefile setup, or use ccache). > >Of course, it is always fun getting your tools to do interesting things >in unusual ways - but it's not always a good idea for real work.
Well, you are of course correct in the sense that a specific constant value shouldn't be scattered throughout a series of modules like casting dust to the winds. It's not a good idea. Your point is wisely made. However, you are also wrong in suggesting, once again, some absolute rule that _always_ applies. In this case, my point remains because there is _some_ need for the semantic. It doesn't matter if there are better ways for most things, if there are some times a need for this semantic. I think you understood me, correctly. Just in case there is any question at all, I'm talking about this semantic, if you are familiar with the Microsoft assembler: QUANTUM EQU 47 PUBLIC QUANTUM You can't do that in c. There is no syntax for it. In the above example, this constant might be the default number of timer ticks used per process quantum in a round robin arrangement. But as you say, you are correct to suggest that this kind of value usually only needs placement in a single module, so the advantage may arguably be reduced to a theoretical one, not a practical one. (Though I suppose I could always posit a specific case where this QUANTUM might be used in several reasonable places.) However, there are times where there are values which may be required in several modules. These may be field masks and init values, for example, of hardware registers or software control flags. It's not always the case that writing a specific subroutine to compose them for you is the better solution. Sometimes, it's better to expose the constants, broadly speaking, and use them in a simple, constant-folding, c language way. Libraries in c are riddled with these. In addition, these public link-time constants can be used to conditionally include or exclude code sections. In fact, almost every compiler uses this fact in one way or the other. CRT0, in particular, may take advantage of such features to conditionally include or exclude initialization code for libraries which may, or may not, have been linked in. And most linkers support the concept in some fashion -- because it is needed. And yes, I'd sometimes like c-level access to it.
>> With cooperative switching (and I use that where possible, >> because it is much easier to implement and support) I may be >> able to write fairly simple routines in assembly to support >> it (a dozen lines, or two.) But there is no escaping the >> idea that whatever I do there relies on details about the >> compiler. Different compilers on the MSP430, for example, >> make different choices about register assignments, which must >> be preserved across calls, which are scratchable, and which >> are used to optionally pass parameters (and the conditions >> under which registers may be chosen to pass them.) > >Yes, these are more examples of where you need to work with the compiler >details.
Yes. No question.
>> With pre-emptive switching, it opens up a Pandora's box. >> Library routines that may use static memory, for example. But >> if pre-emptive switching is a part of the product, then I >> face the problems squarely and usually up front in the >> development. It's crucial to know exactly what works and how >> well it works, right away. >> >> I also enjoy the use of coroutine thunking, from time to >> time. This, and process semantics, make for clear, very >> readable code that works well and is able to be maintained by >> a broader range of programmers (so long as they don't try and >> rewrite the core o/s code, of course.) >> >> I still take your point. But I hope you don't mind a small >> moment of banter just to add to your suggestion that every >> rule has exceptions, including the rule of not hacking things >> at the outset. ;) >> >>> Another rule for embedded development is always know your tools, and >>> preferably pick /good/ tools. Microchip are known to be good for many >>> things - the quality of their 16-bit PIC C compilers is definitely not >>> one of them. >>> <snip> >> >> Well, there is that. I cannot defend their use of _static_ >> memory for compiler temporaries, as they chose to do. It's >> unconscionable. Their argument to me (one or two of those >> who actually _wrote_ its code) was that it led to faster >> emitted code -- in short, it appeared to show off their parts >> better. And they felt they "had it covered." > >It is certainly perfectly possible to use static memory for compiler >temporaries, and it will certainly be faster than the normal alternative >(temporaries on a stack) for many small processors. But it has to be >implemented correctly!
Well, _if_ one is going to use statics _then_ of course it has to be implemented correctly! Who could argue otherwise? The problem is in the _doing_ of that. It requires (or at least I imagine so, right now, being ignorant of a better way) looking at the entire program block to achieve. And that is a bit of a step-change away from the usual c compiler mode of operation. It _might_ be implemented in the linker stage, I suppose. Though I'm struggling to imagine something a little less than a Rube Goldberg contraption to get there in the linker side.
>> Well, they were wrong and a false bargain was made. >> >> I'm sure they aren't the only ones guilty of choosing to sell >> the smell of sizzle over the quality of meat, though. Not by >> a long shot. >> >> Jon
As an aside, I have a lot of other things I'd like in c or c++ which just aren't there. For example, I dearly miss having access to thunking semantics in c or c++ (which does NOT break the c/c++ program model in any way, shape, or form and could easily be implemented as part of either language with no dire impacts at all. I might use this for efficient iterators (don't imagine that I'm talking about std library iterators here, which are somewhat similar in use but in no way similar in their implementation details -- they are much less efficient), so also do I miss this. There is no good reason I can think of not to have it and its utility is wonderful. (I'd be so happy to talk about it, at some point, as the examples are excellent and easily shown.) Jon