diff --git a/source/graphics/Color.cpp b/source/graphics/Color.cpp index bb5efff4a7..b42ad5afff 100644 --- a/source/graphics/Color.cpp +++ b/source/graphics/Color.cpp @@ -4,6 +4,7 @@ #include "maths/MathUtil.h" #include "graphics/SColor.h" +#include "lib/sysdep/ia32/ia32.h" static u32 fallback_ConvertRGBColorTo4ub(const RGBColor& src) { diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp index 67ff1ca33e..5d872fac48 100644 --- a/source/gui/CGUI.cpp +++ b/source/gui/CGUI.cpp @@ -39,6 +39,7 @@ gee@pyro.nu #include "ps/Hotkey.h" #include "ps/Globals.h" #include "lib/timer.h" +#include "lib/sysdep/sysdep.h" // namespaces used using namespace std; diff --git a/source/gui/CInput.cpp b/source/gui/CInput.cpp index de5d99ad40..b6729a9fda 100644 --- a/source/gui/CInput.cpp +++ b/source/gui/CInput.cpp @@ -12,6 +12,7 @@ gee@pyro.nu #include "lib/ogl.h" #include "lib/res/graphics/unifont.h" +#include "lib/sysdep/sysdep.h" #include "ps/Hotkey.h" #include "ps/CLogger.h" diff --git a/source/lib/app_hooks.cpp b/source/lib/app_hooks.cpp index 34f102ed5e..04a69ab32a 100644 --- a/source/lib/app_hooks.cpp +++ b/source/lib/app_hooks.cpp @@ -24,6 +24,7 @@ #include "app_hooks.h" #include "lib/path_util.h" +#include "lib/sysdep/sysdep.h" //----------------------------------------------------------------------------- diff --git a/source/lib/config.h b/source/lib/config.h index 1c52551899..a5068551b3 100644 --- a/source/lib/config.h +++ b/source/lib/config.h @@ -305,12 +305,6 @@ # endif #endif -// snprintf, swprintf, etc. -#define HAVE_NPRINTF 0 -#if HAVE_C99 -# undef HAVE_NPRINTF -# define HAVE_NPRINTF 1 -#endif // strdup, wcsdup #define HAVE_STRDUP 1 diff --git a/source/lib/debug.cpp b/source/lib/debug.cpp index feeb2615de..e78dbaea40 100644 --- a/source/lib/debug.cpp +++ b/source/lib/debug.cpp @@ -32,6 +32,7 @@ #include "path_util.h" #include "debug_stl.h" #include "lib/sysdep/cpu.h" // CAS +#include "lib/sysdep/sysdep.h" #include "lib/res/file/file.h" // FILE_ACCESS // some functions here are called from within mmgr; disable its hooks // so that our allocations don't cause infinite recursion. @@ -178,6 +179,7 @@ bool debug_filter_allows(const char* text) } +#undef debug_printf // allowing #defining it out void debug_printf(const char* fmt, ...) { char buf[MAX_CHARS]; buf[ARRAY_SIZE(buf)-1] = '\0'; @@ -191,6 +193,7 @@ void debug_printf(const char* fmt, ...) debug_puts(buf); } +#undef debug_wprintf // allowing #defining it out void debug_wprintf(const wchar_t* fmt, ...) { wchar_t wcs_buf[MAX_CHARS]; wcs_buf[ARRAY_SIZE(wcs_buf)-1] = '\0'; @@ -698,7 +701,7 @@ ErrorReaction debug_display_error(const wchar_t* description, ErrorMessageMem emm; emm.alloca_buf_size = 50000; - emm.alloca_buf = alloca(emm.alloca_buf_size); + emm.alloca_buf = sys_alloca(emm.alloca_buf_size); const wchar_t* text = debug_error_message_build(description, fn_only, line, func, skip, context, &emm); diff --git a/source/lib/lib.h b/source/lib/lib.h index 8807e578ae..d2531f8696 100644 --- a/source/lib/lib.h +++ b/source/lib/lib.h @@ -8,7 +8,7 @@ * ========================================================================= */ -/* +/*b * Copyright (c) 2003-2005 Jan Wassenberg * * Redistribution and/or modification are also permitted under the @@ -261,7 +261,7 @@ checking, but does not cause any compiler warnings. // 1) final build: optimize assuming this location cannot be reached. // may crash if that turns out to be untrue, but removes checking overhead. #if CONFIG_FINAL -# define UNREACHABLE SYS_UNREACHABLE +# define UNREACHABLE ASSUME_UNREACHABLE // 2) normal build: #else // a) normal implementation: includes "abort", which is declared with @@ -275,7 +275,7 @@ checking, but does not cause any compiler warnings. ) // b) VC only: don't generate any code; squelch the warning and optimize. # else -# define UNREACHABLE SYS_UNREACHABLE +# define UNREACHABLE ASSUME_UNREACHABLE # endif #endif diff --git a/source/lib/lockfree.cpp b/source/lib/lockfree.cpp index 9627b9b2f3..2a6393c64f 100644 --- a/source/lib/lockfree.cpp +++ b/source/lib/lockfree.cpp @@ -29,6 +29,7 @@ #include "lib/posix/posix_pthread.h" #include "lib.h" #include "lib/sysdep/cpu.h" +#include "lib/sysdep/sysdep.h" #include "timer.h" /* @@ -283,7 +284,7 @@ static void smr_release_unreferenced_nodes(TLS* tls) retry: const size_t max_hps = (active_threads+3) * NUM_HPS; // allow for creating a few additional threads during the loop - void** hps = (void**)alloca(max_hps * sizeof(void*)); + void** hps = (void**)sys_alloca(max_hps * sizeof(void*)); size_t num_hps = 0; // for each participating thread: for(TLS* t = tls_list; t; t = t->next) @@ -560,7 +561,7 @@ retry: // or 0 if not found in the list. void* lfl_find(LFList* list, uintptr_t key) { - ListPos* pos = (ListPos*)alloca(sizeof(ListPos)); + ListPos* pos = (ListPos*)sys_alloca(sizeof(ListPos)); if(!list_lookup(list, key, pos)) return 0; return node_user_data(pos->cur); @@ -578,7 +579,7 @@ void* lfl_insert(LFList* list, uintptr_t key, size_t additional_bytes, int* was_ // if this triggers, tls_alloc called from lfl_init failed due to // lack of memory and the caller didn't check its return value. - ListPos* pos = (ListPos*)alloca(sizeof(ListPos)); + ListPos* pos = (ListPos*)sys_alloca(sizeof(ListPos)); Node* node = 0; if(was_inserted) @@ -631,7 +632,7 @@ LibError lfl_erase(LFList* list, uintptr_t key) // if this triggers, tls_alloc called from lfl_init failed due to // lack of memory and the caller didn't check its return value. - ListPos* pos = (ListPos*)alloca(sizeof(ListPos)); + ListPos* pos = (ListPos*)sys_alloca(sizeof(ListPos)); retry: // not found in list - abort. diff --git a/source/lib/posix/posix.cpp b/source/lib/posix/posix.cpp index bbc3ef58e2..800b2e4314 100644 --- a/source/lib/posix/posix.cpp +++ b/source/lib/posix/posix.cpp @@ -1,3 +1,64 @@ #include "precompiled.h" #include "posix.h" +#if CPU_IA32 +# include "lib/sysdep/ia32/ia32.h" +#endif + + +#if !HAVE_C99_MATH + +float rintf(float f) +{ +#if CPU_IA32 + return ia32_asm_rintf(f); +#else + return (float)(int)f; +#endif +} + +double rint(double d) +{ +#if CPU_IA32 + return ia32_asm_rint(d); +#else + return (double)(int)d; +#endif +} + + +float fminf(float a, float b) +{ +#if CPU_IA32 + return ia32_asm_fminf(a, b); +#else + return (a < b)? a : b; +#endif +} + +float fmaxf(float a, float b) +{ +#if CPU_IA32 + return ia32_asm_fmaxf(a, b); +#else + return (a > b)? a : b; +#endif +} + + +uint fpclassifyd(double d) +{ + // really sucky stub implementation; doesn't attempt to cover all cases. + + if(d != d) + return FP_NAN; + else + return FP_NORMAL; +} + +uint fpclassifyf(float f) +{ + return fpclassify((double)f); +} + +#endif // #if !HAVE_C99_MATH diff --git a/source/lib/posix/posix.h b/source/lib/posix/posix.h index d4d4941767..61da410f76 100644 --- a/source/lib/posix/posix.h +++ b/source/lib/posix/posix.h @@ -56,6 +56,8 @@ need only be renamed (e.g. _open, _stat). #ifndef INCLUDED_POSIX #define INCLUDED_POSIX +#include // see isfinite comment below + #if OS_WIN # include "lib/sysdep/win/wposix/wposix.h" # include "lib/sysdep/win/win.h" @@ -73,9 +75,60 @@ need only be renamed (e.g. _open, _stat). #include "posix_utsname.h" -#if OS_MACOSX +// provide C99 *snprintf functions if compiler doesn't already +// (MinGW does, VC7.1 doesn't). +#if MSC_VERSION +# define snprintf _snprintf +# define swprintf _snwprintf +# define vsnprintf _vsnprintf +# define vswprintf _vsnwprintf +#endif + + +#if !HAVE_STRDUP extern char* strdup(const char* str); extern wchar_t* wcsdup(const wchar_t* str); -#endif // #if OS_MACOSX +#endif // #if !HAVE_STRDUP + + +#if !HAVE_C99_MATH +// round float to nearest integral value, according to +// current rounding mode. +extern float rintf(float f); +extern double rint(double d); +// return minimum/maximum of two floats. +extern float fminf(float a, float b); +extern float fmaxf(float a, float b); + +extern uint fpclassifyf(float f); +extern uint fpclassifyd(double d); +#define fpclassify(x) ( (sizeof(x) == sizeof(float))? fpclassifyf(x) : fpclassifyd(x) ) + +// these definitions "happen" to match IA32_FP_* and allow using +// ia32_fp_classify without having to translate the return value. +// we don't #define to IA32_FP_* to avoid dependency. +# define FP_NAN 0x0100 +# define FP_NORMAL 0x0400 +# define FP_INFINITE (FP_NAN | FP_NORMAL) +# define FP_ZERO 0x4000 +# define FP_SUBNORMAL (FP_NORMAL | FP_ZERO) + +# define isnan(d) (fpclassify(d) == FP_NAN) +# define isfinite(d) ((fpclassify(d) & (FP_NORMAL|FP_ZERO)) != 0) +# define isinf(d) (fpclassify(d) == FP_NAN|FP_NORMAL) +# define isnormal(d) (fpclassify(d) == FP_NORMAL) +//# define signbit +#else // HAVE_C99_MATH +// Some systems have C99 support but in C++ they provide only std::isfinite +// and not isfinite. C99 specifies that isfinite is a macro, so we can use +// #ifndef and define it if it's not there already. +// We've included above to make sure it defines that macro. +# ifndef isfinite +# define isfinite std::isfinite +# define isnan std::isnan +# define isinf std::isinf +# define isnormal std::isnormal +# endif +#endif // HAVE_C99_MATH #endif // #ifndef INCLUDED_POSIX diff --git a/source/lib/precompiled.h b/source/lib/precompiled.h index 1278021ef7..050dad82ee 100644 --- a/source/lib/precompiled.h +++ b/source/lib/precompiled.h @@ -58,8 +58,10 @@ #include "lib/types.h" #include "lib/lib_errors.h" #include "lib/string_s.h" // CRT secure string -#include "lib/sysdep/sysdep.h" -#include "lib/debug.h" // (sysdep.h pulls in debug.h) +#include "lib/debug.h" +#include "lib/sysdep/compiler.h" +#include "lib/sysdep/stl.h" +#include "lib/posix/posix.h" // diff --git a/source/lib/res/file/path.cpp b/source/lib/res/file/path.cpp index c9666717a3..5aa6998256 100644 --- a/source/lib/res/file/path.cpp +++ b/source/lib/res/file/path.cpp @@ -29,6 +29,7 @@ #include "lib/lib.h" #include "lib/adts.h" #include "lib/allocators.h" +#include "lib/sysdep/sysdep.h" #include "file_internal.h" diff --git a/source/lib/res/file/vfs_optimizer.cpp b/source/lib/res/file/vfs_optimizer.cpp index 7685ceca58..a8cf7f1672 100644 --- a/source/lib/res/file/vfs_optimizer.cpp +++ b/source/lib/res/file/vfs_optimizer.cpp @@ -664,6 +664,7 @@ static LibError build_mini_archive(const char* mini_archive_fn_fmt) if(!should_build_mini_archive(mini_archive_fn_fmt)) return INFO::SKIPPED; +#if AB_COUNT_LOOSE_FILES Filenames V_fns = (Filenames)malloc((loose_files.size()+1) * sizeof(const char*)); if(!V_fns) WARN_RETURN(ERR::NO_MEM); @@ -678,6 +679,9 @@ static LibError build_mini_archive(const char* mini_archive_fn_fmt) RETURN_ERR(archive_build(mini_archive_fn, V_fns)); return INFO::OK; +#else + return ERR::NOT_IMPLEMENTED; +#endif } diff --git a/source/lib/sysdep/compiler.h b/source/lib/sysdep/compiler.h new file mode 100644 index 0000000000..df26b6b0fb --- /dev/null +++ b/source/lib/sysdep/compiler.h @@ -0,0 +1,98 @@ +#ifndef INCLUDED_COMPILER +#define INCLUDED_COMPILER + + +// pass "omit frame pointer" setting on to the compiler +#if MSC_VERSION +# if CONFIG_OMIT_FP +# pragma optimize("y", on) +# else +# pragma optimize("y", off) +# endif +#elif GCC_VERSION +// TODO +#endif + + +// try to define _W64, if not already done +// (this is useful for catching pointer size bugs) +#ifndef _W64 +# if MSC_VERSION +# define _W64 __w64 +# elif GCC_VERSION +# define _W64 __attribute__((mode (__pointer__))) +# else +# define _W64 +# endif +#endif + + +// C99-like restrict (non-standard in C++, but widely supported in various forms). +// +// May be used on pointers. May also be used on member functions to indicate +// that 'this' is unaliased (e.g. "void C::m() RESTRICT { ... }"). +// Must not be used on references - GCC supports that but VC doesn't. +// +// We call this "RESTRICT" to avoid conflicts with VC's __declspec(restrict), +// and because it's not really the same as C99's restrict. +// +// To be safe and satisfy the compilers' stated requirements: an object accessed +// by a restricted pointer must not be accessed by any other pointer within the +// lifetime of the restricted pointer, if the object is modified. +// To maximise the chance of optimisation, any pointers that could potentially +// alias with the restricted one should be marked as restricted too. +// +// It would probably be a good idea to write test cases for any code that uses +// this in an even very slightly unclear way, in case it causes obscure problems +// in a rare compiler due to differing semantics. +// +// .. we made g++ claim to support C99 by defining __STDC_VERSION__ in the +// makefiles, but that's wrong and it doesn't have the restrict keyword. +// use the extension __restrict__ instead. +#if GCC_VERSION +# define RESTRICT __restrict__ +// .. already available in C99 as 'restrict' +#elif HAVE_C99 +# define RESTRICT restrict +// .. VC8 provides __restrict +#elif MSC_VERSION >= 1400 +# define RESTRICT __restrict +// .. ICC supports the keyword 'restrict' when run with the /Qrestrict option, +// but it always also supports __restrict__ or __restrict to be compatible +// with GCC/MSVC, so we'll use the underscored version. One of {GCC,MSC}_VERSION +// should have been defined in addition to ICC_VERSION, so we should be using +// one of the above cases (unless it's an old VS7.1-emulating ICC). +#elif ICC_VERSION +# error ICC_VERSION defined without either GCC_VERSION or an adequate MSC_VERSION +// .. unsupported; remove it from code +#else +# define RESTRICT +#endif + + +// C99 __func__ +// .. already available; need do nothing +#if HAVE_C99 +// .. VC supports something similar +#elif MSC_VERSION +# define __func__ __FUNCTION__ +// .. unsupported; remove it from code +#else +# define __func__ "(unknown)" +#endif + + +// tell the compiler that the code at/following this macro invocation is +// unreachable. this can improve optimization and avoid warnings. +// +// this macro should not generate any fallback code; it is merely the +// compiler-specific backend for lib.h's UNREACHABLE. +// #define it to nothing if the compiler doesn't support such a hint. +#if MSC_VERSION +# define ASSUME_UNREACHABLE __assume(0) +#else +# define ASSUME_UNREACHABLE +#endif + + +#endif // #ifndef INCLUDED_COMPILER diff --git a/source/lib/sysdep/cpu.cpp b/source/lib/sysdep/cpu.cpp index 10bcba6e05..78e18813bf 100644 --- a/source/lib/sysdep/cpu.cpp +++ b/source/lib/sysdep/cpu.cpp @@ -86,10 +86,10 @@ int cpu_coresPerPackage() #endif } -int cpu_logicalPerPackage() +int cpu_logicalPerCore() { #if CPU_IA32 - return ia32_logicalPerPackage(); + return ia32_logicalPerCore(); #endif } diff --git a/source/lib/sysdep/cpu.h b/source/lib/sysdep/cpu.h index c7e923fb11..d652c95a77 100644 --- a/source/lib/sysdep/cpu.h +++ b/source/lib/sysdep/cpu.h @@ -46,7 +46,7 @@ extern const char* cpu_identifierString(); extern double cpu_clockFrequency(); extern int cpu_numPackages(); // i.e. sockets extern int cpu_coresPerPackage(); -extern int cpu_logicalPerPackage(); +extern int cpu_logicalPerCore(); extern bool cpu_isThrottlingPossible(); diff --git a/source/lib/sysdep/ia32/ia32.cpp b/source/lib/sysdep/ia32/ia32.cpp index 844930e67e..2b39224574 100644 --- a/source/lib/sysdep/ia32/ia32.cpp +++ b/source/lib/sysdep/ia32/ia32.cpp @@ -386,38 +386,69 @@ double ia32_clockFrequency() } +//----------------------------------------------------------------------------- +// detect processor types / topology +//----------------------------------------------------------------------------- -// (these are uniform over all packages) - - -static uint logicalPerPackage() -{ - // not Hyperthreading capable - if(!ia32_cap(IA32_CAP_HT)) - return 1; - - u32 regs[4]; - if(!ia32_asm_cpuid(1, regs)) - DEBUG_WARN_ERR(ERR::CPU_FEATURE_MISSING); - const uint logical_per_package = bits(regs[EBX], 16, 23); - return logical_per_package; -} +// OSes report hyperthreading units and cores as "processors". we need to +// drill down and find out the exact counts (for thread pool dimensioning +// and cache sharing considerations). +// note: Intel Appnote 485 (CPUID) assures uniformity of coresPerPackage and +// logicalPerCore. static uint coresPerPackage() { - // single core - u32 regs[4]; - if(!ia32_asm_cpuid(4, regs)) - return 1; + static uint cores_per_package = 0; + if(cores_per_package == 0) + { + u32 regs[4]; + if(ia32_asm_cpuid(4, regs)) + cores_per_package = bits(regs[EAX], 26, 31)+1; + else + cores_per_package = 1; // single-core + } - const uint cores_per_package = bits(regs[EAX], 26, 31)+1; return cores_per_package; } +static uint logicalPerCore() +{ + static uint logical_per_core = 0; + if(logical_per_core == 0) + { + if(ia32_cap(IA32_CAP_HT)) + { + u32 regs[4]; + if(!ia32_asm_cpuid(1, regs)) + DEBUG_WARN_ERR(ERR::CPU_FEATURE_MISSING); + const uint logical_per_package = bits(regs[EBX], 16, 23); + // cores ought to be uniform WRT # logical processors + debug_assert(logical_per_package % coresPerPackage() == 0); + logical_per_core = logical_per_package / coresPerPackage(); + } + else + logical_per_core = 1; // not Hyperthreading capable + } + + return logical_per_core; +} + +// the above two functions give the maximum number of cores/logical units. +// however, some of them may actually be disabled by the BIOS! +// what we can do is to analyze the APIC IDs. they are allocated sequentially +// for all "processors". treating the IDs as variable-width bitfields +// (according to the number of cores/logical units present) allows +// determining the exact topology as well as number of packages. + +// these are set by detectProcessorTopology, called from ia32_init. +static uint num_packages = 0; // i.e. sockets; > 1 => true SMP system +static uint enabled_cores_per_package = 0; +static uint enabled_logical_per_core = 0; // hyperthreading units typedef std::vector Ids; typedef std::set IdSet; +// add the currently running processor's APIC ID to a list of IDs. static void storeApicId(void* param) { u32 regs[4]; @@ -429,9 +460,14 @@ static void storeApicId(void* param) apic_ids->push_back(apic_id); } + +// field := a range of bits sufficient to represent integers. +// for each id in apic_ids: extract the value of the field at offset bit_pos +// and insert it into ids. afterwards, adjust bit_pos to the next field. +// used to gather e.g. all core IDs from all APIC IDs. static void extractFieldsIntoSet(const Ids& apic_ids, uint& bit_pos, uint num_values, IdSet& ids) { - const uint id_bits = log2(num_values); + const uint id_bits = log2(num_values); // (rounded up) if(id_bits == 0) return; @@ -447,65 +483,62 @@ static void extractFieldsIntoSet(const Ids& apic_ids, uint& bit_pos, uint num_va bit_pos += id_bits; } -static int num_packages = -1; // i.e. sockets; > 1 => true SMP system -static int cores_per_package = -1; -static int logical_per_package = -1; // hyperthreading units -// note: even though APIC IDs are assigned sequentially, we can't make any -// assumptions about the values/ordering because we get them according to -// the CPU affinity mask, which is unknown. - -// HT and dual cores may be disabled, so see how many are actually enabled. -// (examine each APIC ID to determine the kind of CPU) -static void detectActualTopology() +// determine how many coresPerPackage and logicalPerCore are +// actually enabled and also count numPackages. +// (scans the APIC IDs, which requires OS support for thread affinity) +static void detectProcessorTopology() { Ids apic_ids; if(cpu_callByEachCPU(storeApicId, &apic_ids) != INFO::OK) return; + // .. if they're not unique, cpu_callByEachCPU is broken. std::sort(apic_ids.begin(), apic_ids.end()); debug_assert(std::unique(apic_ids.begin(), apic_ids.end()) == apic_ids.end()); - const uint max_cores_per_package = coresPerPackage(); - const uint logical_per_core = logicalPerPackage() / max_cores_per_package; - - // extract values from all 3 fields into separate sets - // (we want to know how many logical CPUs per core, cores per package and packages exist) + // extract values from all 3 ID bitfields into separate sets uint bit_pos = 0; IdSet logical_ids; - extractFieldsIntoSet(apic_ids, bit_pos, logical_per_core, logical_ids); + extractFieldsIntoSet(apic_ids, bit_pos, logicalPerCore(), logical_ids); IdSet core_ids; - extractFieldsIntoSet(apic_ids, bit_pos, max_cores_per_package, core_ids); + extractFieldsIntoSet(apic_ids, bit_pos, coresPerPackage(), core_ids); IdSet package_ids; - extractFieldsIntoSet(apic_ids, bit_pos, 0xFF, core_ids); + extractFieldsIntoSet(apic_ids, bit_pos, 0xFF, package_ids); - num_packages = std::max((int)package_ids.size(), 1); - cores_per_package = std::max((int)core_ids.size(), 1); - logical_per_package = std::max((int)logical_ids.size(), 1) * cores_per_package; + // (the set cardinality is representative of all packages/cores since + // they are uniform.) + num_packages = std::max((uint)package_ids.size(), 1u); + enabled_cores_per_package = std::max((uint)core_ids .size(), 1u); + enabled_logical_per_core = std::max((uint)logical_ids.size(), 1u); + + // note: even though APIC IDs are assigned sequentially, we can't make any + // assumptions about the values/ordering because we get them according to + // the CPU affinity mask, which is unknown. } -uint ia32_logicalPerPackage() +uint ia32_numPackages() { #ifndef NDEBUG - debug_assert(logical_per_package != -1); + debug_assert(num_packages != 0); #endif - return (uint)logical_per_package; + return (uint)num_packages; } uint ia32_coresPerPackage() { #ifndef NDEBUG - debug_assert(cores_per_package != -1); + debug_assert(enabled_cores_per_package != 0); #endif - return (uint)cores_per_package; + return (uint)enabled_cores_per_package; } -uint ia32_numPackages() +uint ia32_logicalPerCore() { #ifndef NDEBUG - debug_assert(num_packages != -1); + debug_assert(enabled_logical_per_core != 0); #endif - return (uint)num_packages; + return (uint)enabled_logical_per_core; } @@ -585,5 +618,5 @@ void ia32_init() detectVendor(); - detectActualTopology(); + detectProcessorTopology(); } diff --git a/source/lib/sysdep/ia32/ia32.h b/source/lib/sysdep/ia32/ia32.h index ad8c48296b..f0b26a090f 100644 --- a/source/lib/sysdep/ia32/ia32.h +++ b/source/lib/sysdep/ia32/ia32.h @@ -130,8 +130,8 @@ extern const char* ia32_identifierString(); extern int ia32_isThrottlingPossible(); extern double ia32_clockFrequency(); -extern uint ia32_logicalPerPackage(); -extern uint ia32_coresPerPackage(); extern uint ia32_numPackages(); +extern uint ia32_coresPerPackage(); +extern uint ia32_logicalPerCore(); #endif // #ifndef IA32_H diff --git a/source/lib/sysdep/ia32/ia32_memcpy.h b/source/lib/sysdep/ia32/ia32_memcpy.h index cfa79329af..9f1fd2ca5b 100644 --- a/source/lib/sysdep/ia32/ia32_memcpy.h +++ b/source/lib/sysdep/ia32/ia32_memcpy.h @@ -1,13 +1,10 @@ #ifndef INCLUDED_IA32_MEMCPY #define INCLUDED_IA32_MEMCPY -#include "lib/sysdep/sysdep.h" - extern "C" { extern void ia32_memcpy_init(); -#define RESTRICT __restrict extern void* ia32_memcpy(void* RESTRICT dst, const void* RESTRICT src, size_t nbytes); } diff --git a/source/lib/sysdep/stl.h b/source/lib/sysdep/stl.h new file mode 100644 index 0000000000..0263f646fd --- /dev/null +++ b/source/lib/sysdep/stl.h @@ -0,0 +1,91 @@ +#ifndef INCLUDED_STL +#define INCLUDED_STL + +// compiling without exceptions (usually for performance reasons); +// tell STL not to generate any. +#if CONFIG_DISABLE_EXCEPTIONS +# if OS_WIN +# define _HAS_EXCEPTIONS 0 +# else +# define STL_NO_EXCEPTIONS +# endif +#endif + + +// STL_HASH_MAP, STL_HASH_MULTIMAP, STL_HASH_SET +// these containers are useful but not part of C++98. most STL vendors +// provide them in some form; we hide their differences behind macros. + +#if GCC_VERSION +# include +# include + +# define STL_HASH_MAP __gnu_cxx::hash_map +# define STL_HASH_MULTIMAP __gnu_cxx::hash_multimap +# define STL_HASH_SET __gnu_cxx::hash_set +# define STL_HASH_MULTISET __gnu_cxx::hash_multiset +# define STL_SLIST __gnu_cxx::slist + +template +size_t STL_HASH_VALUE(T v) +{ + return __gnu_cxx::hash()(v); +} + +// Hack: GCC Doesn't have a hash instance for std::string included (and it looks +// like they won't add it - marked resolved/wontfix in the gcc bugzilla) +namespace __gnu_cxx +{ + template<> struct hash + { + size_t operator()(const std::string& __x) const + { + return __stl_hash_string(__x.c_str()); + } + }; + + template<> struct hash + { + // Nemesi hash function (see: http://mail-index.netbsd.org/tech-kern/2001/11/30/0001.html) + // treating the pointer as an array of bytes that is hashed. + size_t operator()(const void* __x) const + { + union { + const void* ptr; + unsigned char bytes[sizeof(void*)]; + } val; + size_t h = 5381; + + val.ptr = __x; + + for(uint i = 0; i < sizeof(val); ++i) + h = 257*h + val.bytes[i]; + + return 257*h; + } + }; +} + +#else // !__GNUC__ + +# include +# include +// VC7 or above +# if MSC_VERSION >= 1300 +# define STL_HASH_MAP stdext::hash_map +# define STL_HASH_MULTIMAP stdext::hash_multimap +# define STL_HASH_SET stdext::hash_set +# define STL_HASH_MULTISET stdext::hash_multiset +# define STL_HASH_VALUE stdext::hash_value +// VC6 and anything else (most likely name) +# else +# define STL_HASH_MAP std::hash_map +# define STL_HASH_MULTIMAP std::hash_multimap +# define STL_HASH_SET std::hash_set +# define STL_HASH_MULTISET std::hash_multiset +# define STL_HASH_VALUE std::hash_value +# endif // MSC_VERSION >= 1300 + +#endif // !__GNUC__ + +#endif // #ifndef INCLUDED_STL diff --git a/source/lib/sysdep/sysdep.cpp b/source/lib/sysdep/sysdep.cpp index 3c653578e9..36e004b760 100644 --- a/source/lib/sysdep/sysdep.cpp +++ b/source/lib/sysdep/sysdep.cpp @@ -23,48 +23,3 @@ #include "precompiled.h" #include "sysdep.h" - -// emulate C99 functionality -#if !HAVE_C99_MATH - -// fallback versions in case ia32 optimized versions are unavailable. -#if !HAVE_MS_ASM - -inline float rintf(float f) -{ - return (float)(int)f; -} - -inline double rint(double d) -{ - return (double)(int)d; -} - -float fminf(float a, float b) -{ - return (a < b)? a : b; -} - -float fmaxf(float a, float b) -{ - return (a > b)? a : b; -} - -uint fpclassify(double d) -{ - // really sucky stub implementation; doesn't attempt to cover all cases. - - if(d != d) - return FP_NAN; - else - return FP_NORMAL; -} - -uint fpclassifyf(float f) -{ - return fpclassify((double)f); -} - -#endif - -#endif // #if !HAVE_C99_MATH diff --git a/source/lib/sysdep/sysdep.h b/source/lib/sysdep/sysdep.h index cd493a5b78..20196e6774 100644 --- a/source/lib/sysdep/sysdep.h +++ b/source/lib/sysdep/sysdep.h @@ -9,7 +9,7 @@ */ /* - * Copyright (c) 2003-2005 Jan Wassenberg + * Copyright (c) 2003-2007 Jan Wassenberg * * Redistribution and/or modification are also permitted under the * terms of the GNU General Public License as published by the @@ -20,64 +20,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef SYSDEP_H_INCLUDED -#define SYSDEP_H_INCLUDED +#ifndef INCLUDED_SYSDEP +#define INCLUDED_SYSDEP #include "lib/config.h" #include "lib/debug.h" // ErrorReaction -#include // see comments below about isfinite #include // needed for vsnprintf2 -// some functions among the sysdep API are implemented as macros -// that redirect to the platform-dependent version. this is done where -// the cost of a trampoline function would be too great; VC7 does not -// always inline them. -// we therefore need to include those headers. -#if OS_WIN -# include "lib/sysdep/win/win.h" -#elif OS_UNIX -# include "lib/sysdep/unix/unix.h" -#endif -#if CPU_IA32 -#include "ia32/ia32.h" -#endif - - -// pass "omit frame pointer" setting on to the compiler -#if MSC_VERSION -# if CONFIG_OMIT_FP -# pragma optimize("y", on) -# else -# pragma optimize("y", off) -# endif -#elif GCC_VERSION -// TODO -#endif - -// compiling without exceptions (usually for performance reasons); -// tell STL not to generate any. -#if CONFIG_DISABLE_EXCEPTIONS -# if OS_WIN -# define _HAS_EXCEPTIONS 0 -# else -# define STL_NO_EXCEPTIONS -# endif -#endif - - -// try to define _W64, if not already done -// (this is useful for catching pointer size bugs) -#ifndef _W64 -# if MSC_VERSION -# define _W64 __w64 -# elif GCC_VERSION -# define _W64 __attribute__((mode (__pointer__))) -# else -# define _W64 -# endif -#endif - //----------------------------------------------------------------------------- // C99 / SUSv3 emulation where needed @@ -95,118 +45,6 @@ extern int vsnprintf2(char* buffer, size_t count, const char* format, va_list ar #define strnicmp strncasecmp #endif -// alloca: allocate on stack, automatically free, return 0 if out of mem. -// already available on *nix, emulated on Win32. -#if OS_WIN -#undef alloca // from malloc.h -extern void* alloca(size_t size); -#endif - -// emulate some C99 functions if not already available: -// rint: round float to nearest integral value, according to -// current rounding mode. -// fminf/fmaxf: return minimum/maximum of two floats. -#if !HAVE_C99_MATH -// .. fast IA-32 versions -# if CPU_IA32 -# define rintf ia32_asm_rintf -# define rint ia32_asm_rint -# define fminf ia32_asm_fminf -# define fmaxf ia32_asm_fmaxf - -# define FP_NAN IA32_FP_NAN -# define FP_NORMAL IA32_FP_NORMAL -# define FP_INFINITE (FP_NAN | FP_NORMAL) -# define FP_ZERO IA32_FP_ZERO -# define FP_SUBNORMAL (FP_NORMAL | FP_ZERO) -# define fpclassify(x) ( (sizeof(x) == sizeof(float))? ia32_asm_fpclassifyf(x) : ia32_asm_fpclassify(x) ) -// .. portable C emulation -# else - extern float rintf(float f); - extern double rint(double d); - extern float fminf(float a, float b); - extern float fmaxf(float a, float b); - -# define FP_NAN 1 -# define FP_NORMAL 2 -# define FP_INFINITE (FP_NAN | FP_NORMAL) -# define FP_ZERO 4 -# define FP_SUBNORMAL (FP_NORMAL | FP_ZERO) - extern uint fpclassify(double d); -# endif - -# define isnan(d) (fpclassify(d) == FP_NAN) -# define isfinite(d) ((fpclassify(d) & (FP_NORMAL|FP_ZERO)) != 0) -# define isinf(d) (fpclassify(d) == FP_NAN|FP_NORMAL) -# define isnormal(d) (fpclassify(d) == FP_NORMAL) -//# define signbit -#else // HAVE_C99_MATH -// Some systems have C99 support but in C++ they provide only std::isfinite -// and not isfinite. C99 specifies that isfinite is a macro, so we can use -// #ifndef and define it if it's not there already. -// We've included above to make sure it defines that macro. -# ifndef isfinite -# define isfinite std::isfinite -# define isnan std::isnan -# define isinf std::isinf -# define isnormal std::isnormal -# endif -#endif // HAVE_C99_MATH - - -// C99-like restrict (non-standard in C++, but widely supported in various forms). -// -// May be used on pointers. May also be used on member functions to indicate -// that 'this' is unaliased (e.g. "void C::m() RESTRICT { ... }"). -// Must not be used on references - GCC supports that but VC doesn't. -// -// We call this "RESTRICT" to avoid conflicts with VC's __declspec(restrict), -// and because it's not really the same as C99's restrict. -// -// To be safe and satisfy the compilers' stated requirements: an object accessed -// by a restricted pointer must not be accessed by any other pointer within the -// lifetime of the restricted pointer, if the object is modified. -// To maximise the chance of optimisation, any pointers that could potentially -// alias with the restricted one should be marked as restricted too. -// -// It would probably be a good idea to write test cases for any code that uses -// this in an even very slightly unclear way, in case it causes obscure problems -// in a rare compiler due to differing semantics. -// -// .. we made g++ claim to support C99 by defining __STDC_VERSION__ in the -// makefiles, but that's wrong and it doesn't have the restrict keyword. -// use the extension __restrict__ instead. -#if GCC_VERSION -# define RESTRICT __restrict__ -// .. already available in C99 as 'restrict' -#elif HAVE_C99 -# define RESTRICT restrict -// .. VC8 provides __restrict -#elif MSC_VERSION >= 1400 -# define RESTRICT __restrict -// .. ICC supports the keyword 'restrict' when run with the /Qrestrict option, -// but it always also supports __restrict__ or __restrict to be compatible -// with GCC/MSVC, so we'll use the underscored version. One of {GCC,MSC}_VERSION -// should have been defined in addition to ICC_VERSION, so we should be using -// one of the above cases (unless it's an old VS7.1-emulating ICC). -#elif ICC_VERSION -# error ICC_VERSION defined without either GCC_VERSION or an adequate MSC_VERSION -// .. unsupported; remove it from code -#else -# define RESTRICT -#endif - - -// C99 __func__ -// .. already available; need do nothing -#if HAVE_C99 -// .. VC supports something similar -#elif MSC_VERSION -# define __func__ __FUNCTION__ -// .. unsupported; remove it from code -#else -# define __func__ "(unknown)" -#endif //----------------------------------------------------------------------------- @@ -282,6 +120,11 @@ extern LibError sys_cursor_free(void* cursor); // misc // +/** + * allocate on stack, automatically free when current function returns. + **/ +extern void* sys_alloca(size_t size); + // describe the current OS error state. // // err: if not 0, use that as the error code to translate; otherwise, @@ -337,96 +180,4 @@ extern size_t sys_max_sector_size(); # define SYS_DIR_SEP '/' #endif -// tell the compiler that the code at/following this macro invocation is -// unreachable. this can improve optimization and avoid warnings. -// -// this macro should not generate any fallback code; it is merely the -// compiler-specific backend for lib.h's UNREACHABLE. -// #define it to nothing if the compiler doesn't support such a hint. -#if MSC_VERSION -# define SYS_UNREACHABLE __assume(0) -#else -# define SYS_UNREACHABLE -#endif - - -//----------------------------------------------------------------------------- -// STL_HASH_MAP, STL_HASH_MULTIMAP, STL_HASH_SET -//----------------------------------------------------------------------------- - -// these containers are useful but not part of C++98. most STL vendors -// provide them in some form; we hide their differences behind macros. - -#if GCC_VERSION -# include -# include // Probably? - -# define STL_HASH_MAP __gnu_cxx::hash_map -# define STL_HASH_MULTIMAP __gnu_cxx::hash_multimap -# define STL_HASH_SET __gnu_cxx::hash_set -# define STL_HASH_MULTISET __gnu_cxx::hash_multiset -# define STL_SLIST __gnu_cxx::slist - -template -size_t STL_HASH_VALUE(T v) -{ - return __gnu_cxx::hash()(v); -} - -// Hack: GCC Doesn't have a hash instance for std::string included (and it looks -// like they won't add it - marked resolved/wontfix in the gcc bugzilla) -namespace __gnu_cxx -{ - template<> struct hash - { - size_t operator()(const std::string& __x) const - { - return __stl_hash_string(__x.c_str()); - } - }; - - template<> struct hash - { - // Nemesi hash function (see: http://mail-index.netbsd.org/tech-kern/2001/11/30/0001.html) - // treating the pointer as an array of bytes that is hashed. - size_t operator()(const void* __x) const - { - union { - const void* ptr; - unsigned char bytes[sizeof(void*)]; - } val; - size_t h = 5381; - - val.ptr = __x; - - for(uint i = 0; i < sizeof(val); ++i) - h = 257*h + val.bytes[i]; - - return 257*h; - } - }; -} - -#else // !__GNUC__ - -# include -# include -// VC7 or above -# if MSC_VERSION >= 1300 -# define STL_HASH_MAP stdext::hash_map -# define STL_HASH_MULTIMAP stdext::hash_multimap -# define STL_HASH_SET stdext::hash_set -# define STL_HASH_MULTISET stdext::hash_multiset -# define STL_HASH_VALUE stdext::hash_value -// VC6 and anything else (most likely name) -# else -# define STL_HASH_MAP std::hash_map -# define STL_HASH_MULTIMAP std::hash_multimap -# define STL_HASH_SET std::hash_set -# define STL_HASH_MULTISET std::hash_multiset -# define STL_HASH_VALUE std::hash_value -# endif // MSC_VERSION >= 1300 - -#endif // !__GNUC__ - -#endif // #ifndef SYSDEP_H_INCLUDED +#endif // #ifndef INCLUDED_SYSDEP diff --git a/source/lib/sysdep/tests/test_printf.h b/source/lib/sysdep/tests/test_printf.h index 77806a310a..a20c33906a 100644 --- a/source/lib/sysdep/tests/test_printf.h +++ b/source/lib/sysdep/tests/test_printf.h @@ -1,6 +1,8 @@ #include "lib/lib.h" #include "lib/self_test.h" +#include "lib/sysdep/sysdep.h" + class TestPrintf : public CxxTest::TestSuite { // Split some bits into separate functions, so we can get diff --git a/source/lib/sysdep/win/win.h b/source/lib/sysdep/win/win.h index e8e012c5dc..1d4c487841 100644 --- a/source/lib/sysdep/win/win.h +++ b/source/lib/sysdep/win/win.h @@ -20,8 +20,8 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef WIN_H__ -#define WIN_H__ +#ifndef INCLUDED_WIN +#define INCLUDED_WIN #include "lib/config.h" @@ -29,19 +29,6 @@ #error "win.h: do not include if not compiling for Windows" #endif -#include - -// provide C99 *snprintf functions if compiler doesn't already -// (MinGW does, VC7.1 doesn't). -#if !HAVE_NPRINTF -# define snprintf _snprintf -# define swprintf _snwprintf -# define vsnprintf _vsnprintf -# define vswprintf _vsnwprintf -#endif - -#include // wchar_t - // libpng.h -> zlib.h -> zconf.h includes , which causes conflicts. // prevent that, and define what they actually need from windows.h. // incidentally, this requires all dependents of windows.h to include @@ -75,4 +62,4 @@ // remembering to call the init function, but it can't be helped. extern void win_pre_main_init(); -#endif // #ifndef WIN_H__ +#endif // #ifndef INCLUDED_WIN diff --git a/source/lib/sysdep/win/wsysdep.cpp b/source/lib/sysdep/win/wsysdep.cpp index b66c3a3523..24bdf8c32f 100644 --- a/source/lib/sysdep/win/wsysdep.cpp +++ b/source/lib/sysdep/win/wsysdep.cpp @@ -46,13 +46,7 @@ void sys_display_msgw(const wchar_t* caption, const wchar_t* msg) } - -// workaround: apparently PCH contents may be included after the headers. -// malloc.h is defining alloca to _alloca and then VC complains about us -// redefining an intrinsic. so, stomp on the macro here. -#undef alloca - -void* alloca(size_t size) +void* sys_alloca(size_t size) { void* ret; __try diff --git a/source/lib/sysdep/win/wsysdep.h b/source/lib/sysdep/win/wsysdep.h new file mode 100644 index 0000000000..e22dfdc195 --- /dev/null +++ b/source/lib/sysdep/win/wsysdep.h @@ -0,0 +1,4 @@ +#ifndef INCLUDED_WSYSDEP +#define INCLUDED_WSYSDEP + +#endif // #ifndef INCLUDED_WSYSDEP diff --git a/source/lib/timer.cpp b/source/lib/timer.cpp index 9ac37784ae..e615d4d99b 100644 --- a/source/lib/timer.cpp +++ b/source/lib/timer.cpp @@ -33,6 +33,10 @@ #include "adts.h" #include "lib/sysdep/cpu.h" +#if TIMER_USE_RDTSC +# include "lib/sysdep/ia32/ia32.h" +#endif + // rationale for wrapping gettimeofday and clock_gettime, instead of emulating // them where not available: allows returning higher-resolution timer values // than their us / ns interface, via double [seconds]. they're also not @@ -328,3 +332,13 @@ void timer_display_client_totals() debug_printf("-----------------------------------------------------\n"); } + + +#if TIMER_USE_RDTSC + +TimerRdtsc::unit TimerRdtsc::get_timestamp() const +{ + return ia32_rdtsc(); +} + +#endif diff --git a/source/lib/timer.h b/source/lib/timer.h index 3ff480df09..86f44d191a 100644 --- a/source/lib/timer.h +++ b/source/lib/timer.h @@ -200,10 +200,7 @@ class TimerRdtsc { public: typedef i64 unit; - unit get_timestamp() const - { - return ia32_rdtsc(); - } + unit get_timestamp() const; }; #else diff --git a/source/ps/CLogger.cpp b/source/ps/CLogger.cpp index 8aa0b7150a..cbd2d6a3ae 100644 --- a/source/ps/CLogger.cpp +++ b/source/ps/CLogger.cpp @@ -6,6 +6,7 @@ #include "lib/lib.h" #include "lib/path_util.h" #include "lib/res/file/file.h" +#include "lib/sysdep/sysdep.h" #include #include diff --git a/source/ps/CLogger.h b/source/ps/CLogger.h index aa98b00364..1f2e3b5a64 100644 --- a/source/ps/CLogger.h +++ b/source/ps/CLogger.h @@ -69,7 +69,7 @@ private: * Helper class for unit tests - captures all log output while it is in scope, * and returns it as a single string. */ -class TestLogger +class TestLogger : boost::noncopyable { public: TestLogger(); diff --git a/source/ps/Pyrogenesis.cpp b/source/ps/Pyrogenesis.cpp index 251dfc4904..a525fec36f 100644 --- a/source/ps/Pyrogenesis.cpp +++ b/source/ps/Pyrogenesis.cpp @@ -5,6 +5,7 @@ #include "lib/path_util.h" #include "lib/res/file/file.h" +#include "lib/sysdep/sysdep.h" DEFINE_ERROR(PS_OK, "OK"); DEFINE_ERROR(PS_FAIL, "Fail"); diff --git a/source/ps/Util.cpp b/source/ps/Util.cpp index d6ac025451..4b8ce7bc0a 100644 --- a/source/ps/Util.cpp +++ b/source/ps/Util.cpp @@ -62,7 +62,7 @@ void WriteSystemInfo() fprintf(f, "OS : %s %s (%s)\n", un.sysname, un.release, un.version); // .. CPU - fprintf(f, "CPU : %s, %s (%dx%dx%d)", un.machine, cpu_identifierString(), cpu_numPackages(), cpu_coresPerPackage(), cpu_logicalPerPackage()/cpu_coresPerPackage()); + fprintf(f, "CPU : %s, %s (%dx%dx%d)", un.machine, cpu_identifierString(), cpu_numPackages(), cpu_coresPerPackage(), cpu_logicalPerCore()); const double cpu_freq = cpu_clockFrequency(); if(cpu_freq != 0.0f) {