// Macros for correct module ordering. #ifndef _CL_MODULES_H #define _CL_MODULES_H // The order of initialization of different compilation units is not // specified in C++. AIX 4 has a linker which apparently does order // the modules according to dependencies, so that low-level modules // will be initialized earlier than the high-level modules which depend // on them. I have a patch for GNU ld that does the same thing. // // But for now, I take a half-automatic approach to the correct module // ordering problem: PROVIDE/REQUIRE, as in Common Lisp. // // CL_PROVIDE(module) must be the first code-generating entity in a module. // Inline function definitions can precede it, but global variable/function/ // class definitions may not precede it. // Afterwards, any number of CL_REQUIRE(othermodule) is allowed. // At the end of the module, there must be a corresponding // CL_PROVIDE_END(module). (Sorry for this, it's really needed.) // // These macros work only with g++, and only in optimizing mode. But who // wants to use CLN with other C++ compilers anyway... // How to apply these macros: // 1. Find out about variables which need to be initialized. // On Linux/ELF, you can use a command like // $ nm -o libcln.a | grep -v ' [UTtRrW] ' | sort +1 // A symbol of type "D" or "d" lies in the preinitialized DATA section, // a symbol of type "B" or "b" lies in the uninitialized BSS section. // All of them have to be checked. // - Those which contain POD (= plain old data, i.e. scalar values or // class instances without nontrivial constructors) are already fully // initialized by the linker and can be discarded from these considerations. // - Those which are static variables inside a function (you recognize // them: g++ appends a dot and a number to their name) are initialized // the first time the function is entered. They can be discarded from // our considerations as well. // 2. Find out which of these variables are publically exposed (to the user of // the library) through the library's include files, either directly or // through inline functions, or indirectly through normal function calls. // These variables can be referenced from any user module U, hence any // such module must CL_REQUIRE(M) the variable's definition module M. // Since there is no CL_REQUIRE_IF_NEEDED(M) macro (which is equivalent // to CL_REQUIRE(M) if the required module will be part of the executable // but does nothing if M is not used), we must preventively put the // CL_REQUIRE(M) into the header file. Hopefully M is either used anyway // or does not bring in too much code into the executable. // 3. Variables which are not publicly exposed but used internally by the // library can be handled by adding a CL_REQUIRE in all the library's // modules which directly or indirectly use the variable. // 4. Variables and functions which can be reasonably assumed to not be // accessed or executed during initialization need not be treated. // For example, I/O to external streams, exception handling facilities, // number theory stuff, etc. // OK, stop reading here, because it's getting obscene. #if defined(__GNUC__) && defined(__OPTIMIZE__) && !(defined(__hppa__) && (__GNUC__ == 2) && (__GNUC_MINOR__ < 8)) && !defined(NO_PROVIDE_REQUIRE) #ifdef ASM_UNDERSCORE #define ASM_UNDERSCORE_PREFIX "_" #else #define ASM_UNDERSCORE_PREFIX "" #endif // Globalize a label defined in the same translation unit. // See macro ASM_GLOBALIZE_LABEL in the gcc sources. #if defined(__i386__) || defined(__m68k__) || defined(__mips__) || defined(__mips64__) || defined(__alpha__) || defined(__rs6000__) || defined(__x86_64__) || defined(__s390__) // Some m68k systems use "xdef" or "global" or ".global"... #define CL_GLOBALIZE_LABEL(label) __asm__("\t.globl " label); #endif #if defined(__sparc__) || defined(__sparc64__) || defined(__arm__) || defined(__ia64__) // Some arm systems use "EXPORT" or ".globl"... #define CL_GLOBALIZE_LABEL(label) __asm__("\t.global " label); #endif #if defined(__hppa__) #define CL_GLOBALIZE_LABEL(label) __asm__("\t.EXPORT " label ",ENTRY,PRIV_LEV=3"); #endif #if defined(__m88k__) #define CL_GLOBALIZE_LABEL(label) __asm__("\tglobal " label); #endif #if defined(__convex__) #define CL_GLOBALIZE_LABEL(label) __asm__(".globl " label); #endif #ifndef CL_GLOBALIZE_LABEL #define CL_GLOBALIZE_LABEL(label) #endif #if defined(__rs6000__) || defined(_WIN32) #define CL_GLOBALIZE_JUMP_LABEL(label) CL_GLOBALIZE_LABEL(#label) #else #define CL_GLOBALIZE_JUMP_LABEL(label) #endif #ifdef CL_NEED_GLOBALIZE_CTORDTOR #define CL_GLOBALIZE_CTORDTOR_LABEL(label) CL_GLOBALIZE_LABEL(label) #else #define CL_GLOBALIZE_CTORDTOR_LABEL(label) #endif // Output a label inside a function. // See macro ASM_OUTPUT_LABEL in the gcc sources. #if defined(__hppa__) // Some hppa (Linux) systems want `label:', HPUX used to use just `label'. // I tried to find out, but was unable to find the assembler on my HPUX-11 // boxen so decided to potentially ditch the support (no joke). Please // send an email if you can explain to me what's going on! (-rbk. 07/2001) #define CL_OUTPUT_LABEL(label) ASM_VOLATILE ("\n" label ":") #else #define CL_OUTPUT_LABEL(label) ASM_VOLATILE ("\n" label ":") #endif // ASM_VOLATILE(string) is for asms without arguments only!! #if ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 91)) || (__GNUC__ >= 3) // avoid warning caused by the volatile keyword #define ASM_VOLATILE __asm__ #else // need volatile to avoid reordering #define ASM_VOLATILE __asm__ __volatile__ #endif // CL_JUMP_TO(addr) jumps to an address, like goto *(void*)(addr), // except that the latter inhibits inlining of the function containing it // in gcc-2.95. For new CPUs, look for "jump" and "indirect_jump" in gcc's // machine description. #if defined(__i386__) || defined(__x86_64__) #define CL_JUMP_TO(addr) ASM_VOLATILE("jmp %*%0" : : "rm" ((void*)(addr))) #endif #if defined(__m68k__) #define CL_JUMP_TO(addr) ASM_VOLATILE("jmp %0@" : : "a" ((void*)(addr))) #endif #if defined(__mips__) #define CL_JUMP_TO(addr) ASM_VOLATILE("%*j %0" : : "d" ((void*)(addr))) #endif #if defined(__sparc__) || defined(__sparc64__) #define CL_JUMP_TO(addr) ASM_VOLATILE("jmp %0\n\tnop" : : "r" ((void*)(addr))) #endif #if defined(__alpha__) #define CL_JUMP_TO(addr) ASM_VOLATILE("jmp $31,(%0),0" : : "r" ((void*)(addr))) #endif #if defined(__hppa__) //#define CL_JUMP_TO(addr) ASM_VOLATILE("bv,n 0(%0)" : : "r" ((void*)(addr))) #define CL_JUMP_TO(addr) ASM_VOLATILE("b " #addr "\n\tnop") #endif #if defined(__arm__) #define CL_JUMP_TO(addr) ASM_VOLATILE("mov pc,%0" : : "r" ((void*)(addr))) #endif #if defined(__rs6000__) || defined(__powerpc__) || defined(__ppc__) //#define CL_JUMP_TO(addr) ASM_VOLATILE("mtctr %0\n\tbctr" : : "r" ((void*)(addr))) #define CL_JUMP_TO(addr) ASM_VOLATILE("b " #addr) #endif #if defined(__m88k__) #define CL_JUMP_TO(addr) ASM_VOLATILE("jmp %0" : : "r" ((void*)(addr))) #endif #if defined(__convex__) #define CL_JUMP_TO(addr) ASM_VOLATILE("jmp (%0)" : : "r" ((void*)(addr))) #endif #if defined(__ia64__) #define CL_JUMP_TO(addr) ASM_VOLATILE("br " #addr) #endif #if defined(__s390__) #define CL_JUMP_TO(addr) ASM_VOLATILE("br %0" : : "a" ((void*)(addr))) #endif #ifdef CL_GLOBAL_DESTRUCTOR_PREFIX #define CL_PROVIDE(module) \ extern "C" void cl_module__##module##__firstglobalfun () {} \ extern "C" void cl_module__##module##__ctorend (void); \ extern "C" void cl_module__##module##__dtorend (void); \ CL_GLOBALIZE_JUMP_LABEL(cl_module__##module##__ctorend) \ CL_GLOBALIZE_JUMP_LABEL(cl_module__##module##__dtorend) \ CL_GLOBALIZE_CTORDTOR_LABEL( \ ASM_UNDERSCORE_PREFIX CL_GLOBAL_CONSTRUCTOR_PREFIX \ "cl_module__" #module "__firstglobalfun") \ CL_GLOBALIZE_CTORDTOR_LABEL( \ ASM_UNDERSCORE_PREFIX CL_GLOBAL_DESTRUCTOR_PREFIX \ "cl_module__" #module "__firstglobalfun") \ static int cl_module__##module##__counter; \ struct cl_module__##module##__controller { \ inline cl_module__##module##__controller () \ { if (cl_module__##module##__counter++) \ { CL_JUMP_TO(cl_module__##module##__ctorend); } \ } \ inline ~cl_module__##module##__controller () \ { CL_OUTPUT_LABEL (ASM_UNDERSCORE_PREFIX "cl_module__" #module "__dtorend"); } \ }; \ static cl_module__##module##__controller cl_module__##module##__ctordummy; #define CL_PROVIDE_END(module) \ struct cl_module__##module##__destroyer { \ inline cl_module__##module##__destroyer () \ { CL_OUTPUT_LABEL (ASM_UNDERSCORE_PREFIX "cl_module__" #module "__ctorend"); } \ inline ~cl_module__##module##__destroyer () \ { if (--cl_module__##module##__counter) \ { CL_JUMP_TO(cl_module__##module##__dtorend); } \ } \ }; \ static cl_module__##module##__destroyer cl_module__##module##__dtordummy; #define CL_REQUIRE(module) \ extern "C" void cl_module__##module##__ctor (void) \ __asm__ (ASM_UNDERSCORE_PREFIX CL_GLOBAL_CONSTRUCTOR_PREFIX \ "cl_module__" #module "__firstglobalfun"); \ extern "C" void cl_module__##module##__dtor (void) \ __asm__ (ASM_UNDERSCORE_PREFIX CL_GLOBAL_DESTRUCTOR_PREFIX \ "cl_module__" #module "__firstglobalfun"); \ struct _CL_REQUIRE_CLASSNAME(module,__LINE__) { \ inline _CL_REQUIRE_CLASSNAME(module,__LINE__) () \ { cl_module__##module##__ctor (); } \ inline ~_CL_REQUIRE_CLASSNAME(module,__LINE__) () \ { cl_module__##module##__dtor (); } \ }; \ static _CL_REQUIRE_CLASSNAME(module,__LINE__) \ _CL_REQUIRE_CLASSNAME(module##_requirer,__LINE__); #else // gcc-3.0 -fuse-cxa-atexit doesn't have a single per-module destructor // function anymore. Instead, for each object's static constructor it // executes, it pushes the corresponding object's destructor onto a list. // Thus we need to hack the constructors only. #define CL_PROVIDE(module) \ extern "C" void cl_module__##module##__firstglobalfun () {} \ extern "C" void cl_module__##module##__ctorend (void); \ CL_GLOBALIZE_JUMP_LABEL(cl_module__##module##__ctorend) \ CL_GLOBALIZE_CTORDTOR_LABEL( \ ASM_UNDERSCORE_PREFIX CL_GLOBAL_CONSTRUCTOR_PREFIX \ "cl_module__" #module "__firstglobalfun") \ static int cl_module__##module##__counter; \ struct cl_module__##module##__controller { \ inline cl_module__##module##__controller () \ { if (cl_module__##module##__counter++) \ { CL_JUMP_TO(cl_module__##module##__ctorend); } \ } \ }; \ static cl_module__##module##__controller cl_module__##module##__ctordummy; #define CL_PROVIDE_END(module) \ struct cl_module__##module##__destroyer { \ inline cl_module__##module##__destroyer () \ { CL_OUTPUT_LABEL (ASM_UNDERSCORE_PREFIX "cl_module__" #module "__ctorend"); } \ }; \ static cl_module__##module##__destroyer cl_module__##module##__dtordummy; #define CL_REQUIRE(module) \ extern "C" void cl_module__##module##__ctor (void) \ __asm__ (ASM_UNDERSCORE_PREFIX CL_GLOBAL_CONSTRUCTOR_PREFIX \ "cl_module__" #module "__firstglobalfun"); \ struct _CL_REQUIRE_CLASSNAME(module,__LINE__) { \ inline _CL_REQUIRE_CLASSNAME(module,__LINE__) () \ { cl_module__##module##__ctor (); } \ }; \ static _CL_REQUIRE_CLASSNAME(module,__LINE__) \ _CL_REQUIRE_CLASSNAME(module##_requirer,__LINE__); #endif #define _CL_REQUIRE_CLASSNAME(module,line) __CL_REQUIRE_CLASSNAME(module,line) #define __CL_REQUIRE_CLASSNAME(module,line) cl_module__##module##__##line #else #define CL_PROVIDE(module) #define CL_PROVIDE_END(module) #define CL_REQUIRE(module) #endif // Concatenation of macroexpanded tokens. // Equivalent to CL_CONCAT in src/base/cl_macros.h which we do not want // to expose, however. #define CL_CONCATENATE_(xxx,yyy) xxx##yyy #define CL_CONCATENATE(xxx,yyy) CL_CONCATENATE_(xxx,yyy) // Sometimes a link time dependency is needed, but without requirements // on initialization order. // // CL_FORCE_LINK(dummy,external_variable) // forces a link time reference to the external_variable. #include #if 0 // This definition does not work. It gets optimized away by g++ 3.1. #define CL_FORCE_LINK(dummy,external_variable) \ static const void* const dummy[] = { &dummy, &external_variable }; #else #define CL_FORCE_LINK(dummy,external_variable) \ static const \ struct dummy { \ inline dummy () { \ if ((void*) &external_variable == (void*) this) \ abort(); \ } \ } \ CL_CONCATENATE(dummy,_instance); #endif #endif /* _CL_MODULES_H */