#147282 - bpoint - Mon Dec 17, 2007 10:55 pm
Hello all,
Is it possible to select and use both ARM and Thumb in a single source file with gcc? Googling turned up nothing promising, and considering the way the -m flag works, I presume the answer is "no".
It would be nice to be able to default to say, Thumb mode, by specifing -mthumb -mthumb-interwork, and then tag a specific function or functions with an __attribute__ that would allow those functions to be compiled as ARM code, while the rest is compiled into Thumb.
Is there anything like this, supported by gcc now?
#147387 - wintermute - Wed Dec 19, 2007 1:47 pm
Nope, gcc also assumes that functions in the same translation unit can be reached with a standard branch so I doubt if it will ever be possible.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog
#147428 - bpoint - Thu Dec 20, 2007 3:14 pm
Urgh, oh well. This makes it a bit difficult to manage source files for code that is shared across multiple platforms...
Thanks anyway!
#147435 - Dwedit - Thu Dec 20, 2007 6:17 pm
Maybe you just need clever use of IFDEFS, and compile the file twice to two different output files.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."
#147437 - Miked0801 - Thu Dec 20, 2007 7:04 pm
Or just break the file into an ARM and Thumb equivilent. Why is that so hard?
#147443 - bpoint - Thu Dec 20, 2007 8:31 pm
Dwedit wrote: |
Maybe you just need clever use of IFDEFS, and compile the file twice to two different output files. |
Hmm... that's not such a bad idea. I could probably come up with some Makefile magic to have that happen.
Miked0801 wrote: |
Or just break the file into an ARM and Thumb equivilent. Why is that so hard? |
You missed the point about "multiple platforms".
Given that a single source file is typically a certain module of some sort (typically an entire class's implementation), splitting the source file between ARM and Thumb causes problems for other platforms, since the code is shared among all of them. In other words, I'd have to compile both the standard .cpp and the .arm.cpp for all non-GBA platforms. What if some other platform requires this kind of separation with a different CPU? What do you do then?
Another option is to duplicate the actual function code between sources, but if you change something on one platform you can easily forget to change it on another, considering it's in a different source file.
I like Dwedit's idea of using #ifdefs though. Thanks!
#147450 - Miked0801 - Thu Dec 20, 2007 10:53 pm
You do what has always been done: Make it up as you go and pray that you aren't the one who has to maintain it later ;)
#147452 - keldon - Thu Dec 20, 2007 11:01 pm
If you want identical code in two files can't you use includes!
#147472 - bpoint - Fri Dec 21, 2007 6:17 am
Miked0801 wrote: |
You do what has always been done: Make it up as you go and pray that you aren't the one who has to maintain it later ;) |
Considering adding GBA support was my idea, I'm probably going to have to be the one to maintain it... :)
keldon wrote: |
If you want identical code in two files can't you use includes! |
Aside from the fact that #including .cpp files is a big no-no, doing this scatters the class's implementation across multiple source files, rather than just one, where it should be. Again, code maintenance becomes difficult if done this way.
#147480 - keldon - Fri Dec 21, 2007 11:01 am
bpoint wrote: |
Aside from the fact that #including .cpp files is a big no-no, doing this scatters the class's implementation across multiple source files, rather than just one, where it should be. Again, code maintenance becomes difficult if done this way. |
I know not to include source files, I would never do that; but your task sounds like it would make sense - i.e. the exception to the rule. And when you think about it it doesn't quite scatter the classes implementation across multiple source files any more than writing multiple files would since the implementation is in one source file as opposed to two.
Here's how I imagined it to work; you have the two "original" class implementations and declaration
Code: |
// original_class_implementation.inc
#ifdef ORIGINAL_CLASS
void ORIGINAL_CLASS::func ( void )
{
OS_Printf ("called");
} // func (void)
#endif |
Code: |
// original_class_declaration.inc
/*
class ORIGINAL_CLASS
{
*/
public:
void func ( void );
/*
};
*/ |
... then you include them into the header and source file like so (note that this is not anything like typical amateur including source files because they don't know how to build) ...
Code: |
// arm_class.cpp
#include "ARM_CLASS.h"
#define ORIGINAL_CLASS ARM_CLASS
#include "original.cpp"
#undef ORIGINAL_CLASS |
Code: |
// arm_class.h
class ARM_CLASS
{
#include "original.h"
}; |
Code: |
// thumb_class.cpp
#include "thumb_class.h"
#define ORIGINAL_CLASS THUMB_CLASS
#include "original.cpp"
#undef ORIGINAL_CLASS |
Code: |
// thumb_class.h
class THUMB_CLASS
{
#include "original.h"
}; |
#147531 - bpoint - Sat Dec 22, 2007 9:07 am
Unless if I'm mistaken, your method would generate two different classes with identical code (although one would be compiled for ARM and one would be compiled for Thumb).
I want to have a single class implementation with most of the functions compiled as Thumb, and the few which are performance-intensive as ARM. I'm not quite sure how your idea would work given what I want to do.
#147533 - keldon - Sat Dec 22, 2007 11:04 am
Ahh, well if the compiler allows it you could forget my last post and maybe have ...
Code: |
// arm_thumb_class.h
#pragma once // (replace with ifdef)
ARM_THUMB_CLASS
{
public:
void method (void);
}; |
Code: |
// arm_class.h
#pragma once // (replace with ifdef)
#include "arm_class.h"
void method ( ARM_THUMB_CLASS* this )
{
// code
} |
Code: |
// arm_thumb_class.cpp
#include "arm_thumb_class.h"
#include "arm_class.h"
void ARM_THUMB_CLASS::method ( void )
{
method (this);
} |
#147808 - bpoint - Fri Dec 28, 2007 8:16 am
That is a step in the right direction, but dropping back to C from C++ is, well, a bit unacceptable. Once you remove yourself from the class, you no longer have access to any protected or private members, and you cannot call functions from the classes you have derived from.
After a bit of work though, I have been able to find an acceptable solution to this problem. As Dwedit pointed out, compiling the file twice and using #ifdefs to create function-specific code guards works nicely. With a bit of preprocessor magic, I have this in my master header:
Code: |
#ifdef GBA
#if defined(__thumb__) || defined(__THUMBEL__)
#define BUILD_THUMB
#elif defined(__arm__) || defined(__ARMEL__)
#define BUILD_ARM
#endif
#else
#define BUILD_ARM
#define BUILD_THUMB
#endif
// default to disabling of functions
#define FUNC_ARM(...)
#define FUNC_THUMB(...)
#ifdef BUILD_ARM
// allow arm functions
#undef FUNC_ARM
#define FUNC_ARM(...) __VA_ARGS__
#endif
#ifdef BUILD_THUMB
// allow thumb functions
#undef FUNC_THUMB
#define FUNC_THUMB(...) __VA_ARGS__
#endif |
And in my source, I can do:
Code: |
FUNC_THUMB( void MyClass::thumbFunc(void)
{
} )
FUNC_ARM( void MyClass::armFunc(void)
{
} )
|
When the file is compiled using -mthumb, gcc internally defines __thumb__, so BUILD_THUMB is #defined. This lets FUNC_THUMB() be #defined to just pass-through, while FUNC_ARM() is #defined to nothing (the function is removed). Likewise when compiling with -marm, BUILD_ARM is #defined, so code inside FUNC_ARM() is compiled while FUNC_THUMB() is removed.
On platforms other than GBA (where GBA is not #defined), both BUILD_THUMB and BUILD_ARM are #defined, so all functions are included in the output.
The only downside to this is I cannot use #ifdefs within a FUNC_ARM() or FUNC_THUMB() code block, because Microsoft's compiler doesn't support it. I was actually suprised to find that gcc compiled #ifdefs insde without a problem, though, so some of you out there who only use gcc might find this code snippet useful.