Getting the size of a C function

B

bartc

Mark Borgerson said:
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();

}

If you're going to add a special parameter (and assume the return type is
compatible with a return address), it might be possible to use gcc's feature
of obtaining the address of a label.

Then findend can return the address of a label placed near the closing brace
of the function (which possibly may be less likely to be rearranged than a
function call).

int MoveMe(...., bool findend){
if(findend) return (int)&&endoffunction;

// do all the stuff the function is supposed to do

endoffunction:
return 0;
}
 
B

Ben Pfaff

Mark Borgerson said:
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.
 
M

Mark Borgerson

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.


Mark Borgerson
 
W

Willem

Mark Borgerson wrote:
) In article <[email protected]>, (e-mail address removed)
) says...
)>
)> > In article <[email protected]>, (e-mail address removed)
)> > says...
)> >> 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.

More specifically, it could generate code like this:
(example in pseudocode)

(begin MoveMe)
TEST var
SKIP NEXT on zero
JUMP Markend
.... ; the rest of the code
RETURN
(end MoveMe)


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
J

Jon Kirwan

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.

Ben quite correctly brought you up short on the right point.
Your example was, just to refresh ourselves:
: int MoveMe ( ...., bool findend ) {
: if ( !findend ) {
: // do normal function stuff
: } else
: Markend();
: }

Let's divert from this for a moment and take the case of a
for-loop in c. It looks like:
: for ( init-block; condition; iterate-block )
: body-block;

A compiler will often translate this into this form:
: init-block;
: goto A;
: B: body-block;
: C: iterate-block;
: A: if ( condition ) goto B;
: D:

(The reason for the C label is to support the continue-
statement and the reason for the D label is to support a
break-statement, of course.)

The straight interpretation would have been more like this:
: init-block;
: A: if ( !condition ) goto D;
: B: body-block;
: C: iterate-block;
: goto A;
: D:

But note that the execution of the for-loop's main body,
presumed by the compiler to have "many iterations" as a
reasonable guess, includes execution for the "goto A"
statement in each and every iteration. But so is, in effect,
the conditional test, too. In other words, it takes longer
to execute the body, even if that only means the execution of
one jump instruction. It's more efficient to redesign the
model used by the compiler to the first example I gave,
merely because the c compiler takes the position that the
added one-time execution of the first "goto A" will be the
lower cost approach (which it almost always will be.)

Now let's assume that the compiler takes the position that
the first case of an if-statement section is the more
frequently travelled one. In other words, when the
conditional case is executed, it will more often be "true"
than "false." The model used might very well then be to
convert:
: if ( condition )
: s1-block;
: else
: s2-block;
into:

: if ( !condition ) goto A;
: s2-block;
: goto B;
: A: s1-block;
: B:

This provides s1-block execution with one less jump and
therefore lets it execute slightly faster with the idea that
it is the preferred path.

So let's revisit your example again in this light:
: int MoveMe ( ...., bool findend ) {
: if ( !findend ) {
: // do normal function stuff
: } else
: Markend();
: }

This _may_ be taken by a c compiler to be:
: int MoveMe ( ...., bool findend ) {
: if ( findend ) goto A;
: Markend();
: goto B;
: A: // do normal function stuff
: B:
: }

Leaving your function call to Markend not exactly where you'd
have liked to see it occur.

An old book you can pick up talking about a method used to
_explicitly_ inform the compiler about statistics of branch
likelihoods is the Ph.D. thesis by John Ellis:

http://mitpress.mit.edu/catalog/item/default.asp?tid=5098&ttype=2

Worth a read, some snowy day.

Jon
 
M

Mark Borgerson

Mark Borgerson wrote:
) In article <[email protected]>, (e-mail address removed)
) says...
)>
)> > In article <[email protected]>, (e-mail address removed)
)> > says...
)> >> 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.

More specifically, it could generate code like this:
(example in pseudocode)

(begin MoveMe)
TEST var
SKIP NEXT on zero
JUMP Markend
... ; the rest of the code
RETURN
(end MoveMe)

I've actually seen constructs like that intentionally
coded in assembly language, since it saves the
address push and pop you would need need in a branch
to a subroutine. I haven't seen it recently
in compiler output, but that may be because I
limit optimization to make debugging easier. Since
I do limited numbers of systems in a niche market,
I save money by spending a few extra dollars on
more memory and CPU cycles if it saves me a few
hours of debugging time.


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. ;-)




Mark Borgerson
 
M

Mark Borgerson

Ben quite correctly brought you up short on the right point.
Your example was, just to refresh ourselves:


Let's divert from this for a moment and take the case of a
for-loop in c. It looks like:


A compiler will often translate this into this form:


(The reason for the C label is to support the continue-
statement and the reason for the D label is to support a
break-statement, of course.)

The straight interpretation would have been more like this:


But note that the execution of the for-loop's main body,
presumed by the compiler to have "many iterations" as a
reasonable guess, includes execution for the "goto A"
statement in each and every iteration. But so is, in effect,
the conditional test, too. In other words, it takes longer
to execute the body, even if that only means the execution of
one jump instruction. It's more efficient to redesign the
model used by the compiler to the first example I gave,
merely because the c compiler takes the position that the
added one-time execution of the first "goto A" will be the
lower cost approach (which it almost always will be.)

I've also run across main processing loops such as

void MainLoop(void)
while(1){
get user input
execute commands
}
MarkEnd();
}

where MarkEnd doesn't appear in the generated machine
code, because the compiler, even at lowest optimization
setting, recognizes that the code after the loop
will never get executed.

Now let's assume that the compiler takes the position that
the first case of an if-statement section is the more
frequently travelled one. In other words, when the
conditional case is executed, it will more often be "true"
than "false." The model used might very well then be to
convert:


This provides s1-block execution with one less jump and
therefore lets it execute slightly faster with the idea that
it is the preferred path.

So let's revisit your example again in this light:


This _may_ be taken by a c compiler to be:


Leaving your function call to Markend not exactly where you'd
have liked to see it occur.

That could certainly occur. I would be interested in the logic
that could come to the conclusion that one or the other
of the branches would be more likely to occur. I guess the
compiler could check all the calls to MoveMe and compare the
number of times the findend parameter was true and false. However that
might be pretty difficult if a variable was used.

Still, a good reason, as I've said in other posts, to look
at the resulting assembly langauage. I did it for one
MSP430 compiler, and it worked the way I wanted. YMMV.

I wonder how many compilers would make that kind of optimization
and under which optimization settings.
An old book you can pick up talking about a method used to
_explicitly_ inform the compiler about statistics of branch
likelihoods is the Ph.D. thesis by John Ellis:

http://mitpress.mit.edu/catalog/item/default.asp?tid=5098&ttype=2

Worth a read, some snowy day.

Those are pretty rare in Corvallis. Only one or two so far this winter.
Now, rainy days----those I get in plentitude!Mark Borgerson
 
J

Jon Kirwan

I've also run across main processing loops such as

void MainLoop(void)
while(1){
get user input
execute commands
}
MarkEnd();
}

where MarkEnd doesn't appear in the generated machine
code, because the compiler, even at lowest optimization
setting, recognizes that the code after the loop
will never get executed.

Yes, of course. That is another possibility. The intended
function may be essentially the "main loop" of the code and
as such never returns. However, whether or not MarkEnd()
were optimized out, it wouldn't ever get executed anyway. So
you'd never get the address stuffed into something useful...
and so it doesn't even matter were it that the compiler kept
the function call. So it makes a good case against your
approach for an entirely different reason than optimization
itself.
That could certainly occur. I would be interested in the logic
that could come to the conclusion that one or the other
of the branches would be more likely to occur.

I wasn't suggesting that the optimizer includes a feature
where it "tries" to adduce the likelihood. I was suggesting
the idea that the compiler writer makes the 'a priori'
decision that it is.

Think of it this way. Ignorant of application specific
information, the compiler writer has two options to take when
considering the if..else case's approach. Regardless of
which way the compiler author chooses, one of the two blocks
will get a run-time preference. So, does the compiler author
_choose_ to prefer the if-case or the else-case? Without
knowledge, which way would _you_ decide to weigh in on?
Either way you go, you are making a choice. No escaping that
fact.

Now, the Bulldog compiler provides a way for the author of
the code to supply known information _or_ to use run-time
profiling to provide that information, automatically. But
I'm not talking about this case. That's for another
discussion. I only pointed that out for leisure reading. Not
as a point in this specific discussion.
I guess the
compiler could check all the calls to MoveMe and compare the
number of times the findend parameter was true and false. However that
might be pretty difficult if a variable was used.

Run-time profiling could provide that information. But that
wasn't anything I wanted you worrying over in this talk. It
distracts from the central point -- which is that a compiler
writer, sans application knowledge and sans anything in the
compiler or compiler syntax provided to the application coder
to better inform him/her about which way to go, must choose.
Either prefer the if-case or prefer the else-case. There is
no other option. So which way would you go?
Still, a good reason, as I've said in other posts, to look
at the resulting assembly langauage. I did it for one
MSP430 compiler, and it worked the way I wanted. YMMV.

Indeed. I think the point here is that one is left entirely
to the vagaries of the compiler author. And on that point,
they may decide to go either way. There is NOTHING in the c
language itself to help them decide which is better.
I wonder how many compilers would make that kind of optimization
and under which optimization settings.

I think this question is moot. The point I was making
remains even _without_ optimizations that may help inform the
compiler about frequency of execution. So there is no need
to argue this point.
Those are pretty rare in Corvallis. Only one or two so far this winter.
Now, rainy days----those I get in plentitude!

Mark Borgerson

Hehe. I live near Mt. Hood at an elevation of about 1000'
ASL. So I get three feet of snow and ice, from time to time.
I've had to use my JD 4320 tractor on more than one occasion!
:)

Jon
 
M

Mark Borgerson

On Sat, 23 Jan 2010 15:18:25 -0800, Mark Borgerson


Yes, of course. That is another possibility. The intended
function may be essentially the "main loop" of the code and
as such never returns. However, whether or not MarkEnd()
were optimized out, it wouldn't ever get executed anyway. So
you'd never get the address stuffed into something useful...
and so it doesn't even matter were it that the compiler kept
the function call. So it makes a good case against your
approach for an entirely different reason than optimization
itself.

Well, I wuould not dream of using this approach on a function
that never returns. OTOH a flash-update routine had better
return, or it won't be particularly useful (unless your goal
is to test the write-endurance of the flash) ;-)
Hehe. I live near Mt. Hood at an elevation of about 1000'
ASL. So I get three feet of snow and ice, from time to time.
I've had to use my JD 4320 tractor on more than one occasion!
:)
Mark Borgerson
 
G

Grant Edwards

this is a little closer to the second option, of having a
secondary image file embedded as data...

Yup, it's pretty much exactly that.
this is, assuming the linker or image format actually supports
the "separate section" idea...

Every C compiler/toolchain I've used for embedded systems
development for the past 25 years supported things like that.
If his tools don't support multiple sections, then the first
order of business is to find a decent toolchain.
dunno about ELF,

ELF supports multile sections, and I've done exactly such
things with ELF-based toolchains (Gnu binutils and GCC) when
working on stuff like bootloaders where the memory map changes
completely part-way through the program as the memory
controller gets configured.n
but PE/COFF would not support this, since it would require
breaking some of the internal assumptions of the file format
(for example, that the image is continuous from ImageBase to
ImageBase+ImageSize, ...).

ELF may have similar restrictions (actually, I think most ELF
images are position independent anyways,

That depends on the compiler options and linker command file.
In my experience, "executable" ELF files on embedded systems
(images that are ready to load into RAM and run) are generally
not relocatable.
can't say so much about other file formats though...

The COFF-based toolchains I've used all seem to support
multiple sections, but that may have been due to
vendor-specific extensions.
 
B

BGB / cr88192

Grant Edwards said:
Yup, it's pretty much exactly that.

ok.



Every C compiler/toolchain I've used for embedded systems
development for the past 25 years supported things like that.
If his tools don't support multiple sections, then the first
order of business is to find a decent toolchain.

well, I haven't personally had much experience with embedded systems, so I
am not sure here.

ELF supports multile sections, and I've done exactly such
things with ELF-based toolchains (Gnu binutils and GCC) when
working on stuff like bootloaders where the memory map changes
completely part-way through the program as the memory
controller gets configured.n

yes, I know it has multiple sections, but AFAIK it is generally assumed that
the final image is in a continuous region of memory (with the sections
generally packed end-to-end), at least in the cases I have seen. granted, in
cases I have seen, ELF has usually been x86 and PIC as well (the default
build for Linux).

That depends on the compiler options and linker command file.
In my experience, "executable" ELF files on embedded systems
(images that are ready to load into RAM and run) are generally
not relocatable.

interesting...

well, I have really only seen ELF on Linux on x86, and there it is almost
invariably position-independent.

granted, I don't know what other systems do...

The COFF-based toolchains I've used all seem to support
multiple sections, but that may have been due to
vendor-specific extensions.

COFF has multiple sections, but PE/COFF (in particular) also has ImageBase
and ImageSize fields (I forget their exact official names right off), which
are located in the optional header, which is mandatory in PE/COFF, and also
contains things like the subsystem (Console, GUI, ...), and references to
the import and export tables (related to DLL's), ...

AFAIK, PE/COFF also tends to assume that the image is continuous between
these addresses, and also that all loadable sections be between them (doing
otherwise could break the DLL / EXE loader). however, they may support
additional "non-loadable" sections, which AFAIK need not obey this (but are
usually ignored by the loader).

granted, to really know, I would have to dig around more closely in the
PE/COFF spec (and Microsoft's sometimes confusing writing style, which
caused great fun in a few cases when trying to debug my custom EXE/DLL
loader...).

however, I can't say much about how much of this is common with other
variants of COFF (IOW: the ones which don't necessarily begin with an MS-DOS
stub, ...).

nevermind the added layer of hackery needed for .NET ...



I guess it all depends then on whether the particular linker for the
particular target supports non-continuous images then, or if alternative
means would be needed instead...

or such...
 
F

Flash Gordon

well, I haven't personally had much experience with embedded systems, so I
am not sure here.

Then believe people who have...
yes, I know it has multiple sections, but AFAIK it is generally assumed that
the final image is in a continuous region of memory (with the sections
generally packed end-to-end), at least in the cases I have seen. granted, in
cases I have seen, ELF has usually been x86 and PIC as well (the default
build for Linux).

It simply isn't true for embedded systems.
interesting...

well, I have really only seen ELF on Linux on x86, and there it is almost
invariably position-independent.

granted, I don't know what other systems do...

Then believe people who do...

I guess it all depends then on whether the particular linker for the
particular target supports non-continuous images then, or if alternative
means would be needed instead...

or such...

Or you could believe people with experience on embedded systems. It is a
common requirement to have non-contiguous sections, and sections which
are loaded in to one location but run from another, and all sorts of
funky things. Sometimes you can execute code faster from RAM than ROM,
so you move the code at run-time, having boot loaders which get code
from one place (sometimes on a different processor) and put it in
another is common, and I've even had gaps in the ROM where there was
RAM! All of which means having separate sections which are not adjacent.

It's all specific to the given tool-chain as to the best way to achieve
it though.
 
F

Flash Gordon

Mark said:
Well, I wuould not dream of using this approach on a function
that never returns. OTOH a flash-update routine had better
return, or it won't be particularly useful (unless your goal
is to test the write-endurance of the flash) ;-)

<snip>

Some times you *do* write such functions so they never return. Whilst
reprogramming the flash it keeps kicking the watchdog, but it stops when
it's finished and the watchdog resets the system thus booting it in to
the new code. Or it might branch to the reset (or power-up) vector
rather than return. In fact, returning could easily be impossible
because the code from which the function was called is no longer there!
 
M

Mark Borgerson

<snip>

Some times you *do* write such functions so they never return. Whilst
reprogramming the flash it keeps kicking the watchdog, but it stops when
it's finished and the watchdog resets the system thus booting it in to
the new code. Or it might branch to the reset (or power-up) vector
rather than return. In fact, returning could easily be impossible
because the code from which the function was called is no longer there!
Hmmmm. I hadn't thought of that watchdog idea, since TI recommends
shutting off the watchdog and disabling interrupts while programming
flash.

I also agree about the return not being normal---there's probably not
much chance returning to the address on the stack is going to
work out, so a reset is probably the best idea after a firmware
update.

I should have said that I wouldn't use this idea on a function
designed to run forever---or at least not one that the compiler
might think runs forever. I would also examine the
resulting code to make sure the compiler was doing what I
intended.


I think that the ideas I have described will work on some
processors and compilers for some functions, but not
on all compilers for all processors and functions. If you
do a lot of embedded systems programming, restrictions
like that are nothing new.


Mark Borgerson



Mark Borgerson
 
A

Albert van der Horst

Hi,

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".

Admit it, you do something that can't be done in C.
By far the simplest is to generate assembler code, and
add a small instrumentation to that.
Start by accessing the function through a pointer to subroutine.
Then you can store an sram address there when needed.
 
J

James Harris

On 24 Jan, 21:44, David Brown <[email protected]>
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.

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.

James
 
J

Jon Kirwan

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.



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.

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
 
B

bartc

James Harris said:
On 24 Jan, 21:44, David Brown <[email protected]>
wrote:
...

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.

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'd need to sort *all* the functions of an application (include
non-global functions), and there would still be the possibility that some
function or other stuff you don't know about resides between 'consecutive'
functions f() and g().

Reading f() might be alright but overwriting it would be tricky.
 
J

Jon Kirwan

On 24 Jan, 21:44, David Brown <[email protected]>
wrote:
...

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.

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.

In general, no universally "good" assumptions exist. Partly
also because the very idea itself of "moving a function" in
memory at run-time is itself not yet well-defined by those
talking about it here.

Any given function may have the following:

code --> Code is essentially strings of constants. It may
reside in a von-Neumann memory system or a Harvard one. It
therefore may be readable by other code, or not. Many of the
Harvard implementations include a special instruction or a
special pointer register, perhaps, to allow access to the
code space memory. But not all do. In general, it may not
even be possible to read and move code. Even in von-Neumann
memory systems where, in theory there is no problem, the code
may have been "distributed" in pieces. An example here would
be an implementation I saw with Metaware's c compiler where
they had extended it to support a type of co-routine called
an 'iterator.' In this case, the body-block of a for-loop
would be moved outside the function's code region into a
separate function so that their implementation could call the
for-loop body through their very excellently considered
support mechanism for iterators. You'd need to know where
that part was, as well, to meaningfully move things.

constants --> A function may include instanced constants
(which a smart compiler may "understand" from something like
'const int aa= 5;', if it also finds that some other code
takes an address to 'aa'.) These may also need to be moved.
Especially if one is trying to download an updated function
into ram before flashing it for permanence as a "code update"
procedure. These constants may also be placed either in
von-Neumann memory systems and be accessed via PC-relative or
absolute memory locations -- itself a potential bag of worms
-- or in Harvard code space if the processor supports
accessing it or in Harvard data space, otherwise, especially
if there is some of that which is non-volatile.

static initialized data --> A function may include instanced
locations that must be initialized prior to main(), but where
the actual values of these instances are located in some
general collection place used by who-knows-what code in the
crt0 library routine that does this job of pre-initing. Once
again, more issues to deal with and wonder about.

And that's just what trips off my tongue to start.

It's a tough problem to solve generally. To do it right, the
language semantics (and syntax, most likely, as well) itself
would need to be expanded to support it. That could be done,
I suppose. But I imagine a lot of gnashing of teeth along
the way.

Jon
 
J

James Harris

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".

....

....

....


You'd need to sort *all* the functions of an application (include
non-global functions), and there would still be the possibility that some
function or other stuff you don't know about resides between 'consecutive'
functions f() and g().

Reading f() might be alright but overwriting it would be tricky.

Since you've commented, Bart, do you have any thoughts on making
metadata about functions available in a programming language? Maybe
you already do this in one of your languages.

The thread got me thinking that if a function is a first-class object
perhaps some of its attributes should be transparent. Certainly its
code size and maybe its data size too; possibly its location, maybe a
signature for its input and output types. Then there are other
attributes such as whether it is in byte code or native code, whether
it is relocatable or not, what privilege it needs etc.

If portability is not needed a function object could also be
decomposed to individual instruction or subordinate function objects.
I'm not saying I like this idea - portability is a key goal for me -
but I'm just offering some ideas for comment.

Any thoughts on what's hot and what's not?

Followups set to only comp.lang.misc.

James
 

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,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top