I'm coming close to completing my first big project (yay!), and I need some advice. I'm getting ready to do some optimizations, but since I'm not intimately familiar with ASM, I don't really know what to do. I only vaguely remember a couple of things that were mentioned on the TICT forum. So, here are my questions:
Right now, I have all my variables as local variables in _main(). I'm considering moving them to be global so that I don't have to pass so much to my functions. Which is better in terms of speed? Which is better in terms of size?
Also, in several cases, I pass a structure (of roughly 22 bytes) to several functions. Would it be better to pass it as a pointer?
Finally, I'm assuming declaring functions called only once as "static inline" has no drawbacks. Is this so?
P. S. I'm also thinking of slowing down the game in the early levels so that it appears to work at the same speed on all levels. I remember something about one of the ports associated with the programmable timer not working on VTI. (Since I don't have any way of transferring to my calculator, all my testing is done on VTI.) What parts of the programmable timer can I expect to work?
As he said, you should use pointers instead of passing-by-value, which is both slower and larger, especially since GCC doesn't know how to generate inline copy loops (unlike the compiler in TIFS, this is one of the very few advantages the TIFS compiler has over GCC).
> Right now, I have all my variables as local variables in _main(). I'm considering moving them to be global so that I don't have to pass so much to my functions. Which is better in terms of speed? Which is better in terms of size?
Globals, probably. The worst example of misuse of locals (auto) variables I know of is Pinball. I told the author how terrible his code was, but it's true that improving the code is much work.
The next step is a global register variable: put all your globals into a structure, and keep a pointer to that structure into a register (usually a4, this is why I'm not happy with the current version of Kevin's patch for the tilemap engine). It doesn't always work well with interrupt handlers, though.
> Finally, I'm assuming declaring functions called only once as "static inline" has no drawbacks. Is this so?
I'd say "yes", because I saw that if the compiler figures out that inlining not-too-small "static inline" functions would turn into worse code, it doesn't.
"static inline" is also usually the way to go for very simple functions (functions that return the state of a single global, etc.). If you have a look at the kernel-based dynamic version of PolySnd, you will see that one third of all exported entry points are functions which *should* be macros for both speed and size. I told the author as well, I don't know if he turned those functions into macros in the static version.
As for optimized compilation options, check the latest software by TICT.
> will the address register contain always the address of the structure (even when it is unnecessary) ?
On the one hand, it improves all accesses to globals (d(an) ea mode, even for writes).
On the other hand, it eats up a register permanently (same for -freg-relative-an which effect is identical regarding variables).
In practice, -freg-relative-an or the global register variables I'm describing usually improve both compressed and uncompressed size of programs, thanks to improved writes to globals (d(an) instead of relocated xxx.l references).
#6: Interrupt handlers do not take global register variables or -freg-relative-an into account.
AFAIK, TIGCC handles global variables the normal way I described above, unless -mpcrel is used (it is then likely to use d(pc) reads and d(pc) writes after lea instructions). Note that -mpcrel and -freg-relative-an are mutually exclusive.
No, VTI's programmable timer is not reliable. It's a bug in VTI which has nothing to do with differences between HW1 and HW2/3.
-freg-relative-an or other compilation options (check TI-Timewaste for example) have to be added in the compiler options of your project's options.
Structures are never passed by value in C. When you do not use a pointer, they are passed by reference. Thus, using pointers or not changes nothing but the syntax you use. Whatever way you choose, the compiler will actually use the address of the structure without duplicating it.
Anyway, if you do not modify the structure, use the const attribute. It prevents errors and may allow the compiler to make a few more optimizations, based on the assumption that the members of the structure will not be modified.
no, they are definitely passed (and returned) by value, so you *have* to use pointers to be efficient. BTW, that's the same in C++, too ^^ (unless you use MyStruct& mystruct, in which case it is obviously passed by reference)
as for the const attribute, it might prevent errors, but often it's just going to be annoying (e.g. function mother() calls function son() with struc.value1 set appropriately, and son() wants to call grandson() with a special value of struc.value2; you just can't set the const attribute for struc in son() -- and mother() doesn't care about struc.value2 anyway, so it's OK for son() to modify it)
Err, maybe I confused structures and arrays. My mistake ^^
However, your example has nothing to do with the const attribute, since mother() has nothing against son() modifying some fields (here value2) of the structure.
What about when son() modifies something he shouldn't and mother() gets mad, can father() beat son() until he stops?
When I add -freg-relative-an to the list at Project->Options->Compilation->GCC Switches:, I get a large number of errors, starting with "Warning: Unknown register name: an", "Bad expression.", "Missing ')'.", "Parse error -- statement 'move.w #1,level-__relation(%an)' ignored.", etc. Any hints on how to discover what I need to fix?
Pollux> in C++ you can use the mutable attribute to solve a part of the problem. Well, in truly object-oriented programming, one would encapsulate data access anyway. I do not know, however, if the mutable keyword exists in C.
dmwit> That's because it's not -freg-relative-an but -freg-relative-a0 -freg-relative-a1, etc. You choose one. You want to avoid using a0, a1, a6 and a7, though. Because the first ones are overwritten by most functions, a6 is used to keep track of the stack frame (though you may use it if you disable stack frames and you are sure no function you call uses them), and a7 is the stack pointer.
The leaves you with one of -freg-relative-a2 -freg-relative-a3 -freg-relative-a4 or -freg-relative-a5
Relavant options are :
-fomit-frame-pointer : forces the compiler not to use stack frames. set by default
-freg-relative-an : causes your program to load its address at its very beginning, then use indirections relative to this address. The address being kept in a register, it should be faster and shorten the program a bit. This is, however, at the expense of having a register stuck all the time and may slow down complicated computations.
-mpcrel : a loose variant of -freg-relative-an. It does not use a register. It takes advantage of the pc-relative addressing mode of the 68k and uses indirections relative to current instruction.
I don't think there is much difference between -freg-relative-an and -mpcrel. Maybe this comes to play when using dynamically linked libraries, I do not know. I use -mpcrel.
I always experience problemes when I try to compile something with -mpcrel (such as approximately "could not emit relocation")
do you know why ?
It depends on what you are trying compile. Do you have a specific example?
i'lll try to give you one
Did you try -mpcrel instead of -freg-relative-an ?
It has much less side-effects.
Yes, but it doesn't compile. It says, "Operands mismatch -- statement 'cmp.w #1,(level:w,%pc)' ignored."
Is your code pure C or does the error happen in some assembly code you wrote yourself?
-mpcrel should only affect what the compiler generates, I guess it doesn't have anything to do with the assembler. As it's not a very used switch, there are chances that it's a bit buggy on plain 68000 (IIRC cmp.w #1,level(pc) does work on more recent 680x0 processors)
My code is pure C; I don't know any assembly. (Although I am using the Extgraph library, I doubt the problem is there.)
Strange, I don't usually experience trouble when using -freg-relative-a4 or -mpcrel (mutually exclusive).
Do either of them conflict with any other options? I have the default options still set:
-Os -Wall -W -Wwrite-strings