From: Julian Seward Date: Sat, 12 Nov 2005 23:10:48 +0000 (+0000) Subject: Tidy up the machinery for detecting the CPU type and capabilities. X-Git-Tag: svn/VALGRIND_3_1_0~128 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=10ee793424ffecc2688368647c78d2f7da9ba6c9;p=thirdparty%2Fvalgrind.git Tidy up the machinery for detecting the CPU type and capabilities. Move it from m_translate to m_machine, which is a more appropriate place for it. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5104 --- diff --git a/coregrind/m_dispatch/dispatch-x86-linux.S b/coregrind/m_dispatch/dispatch-x86-linux.S index e743a8c132..f357ad7174 100644 --- a/coregrind/m_dispatch/dispatch-x86-linux.S +++ b/coregrind/m_dispatch/dispatch-x86-linux.S @@ -71,7 +71,7 @@ VG_(run_innerloop): /* set host SSE control word to the default mode expected by VEX-generated code. */ - cmpl $0, VG_(have_mxcsr_x86) + cmpl $0, VG_(machine_x86_have_mxcsr) jz L1 pushl $0x1F80 ldmxcsr (%esp) @@ -134,15 +134,15 @@ run_innerloop_exit: %mxcsr or %fpucw. We can't mess with %eax here as it holds the tentative return value, but any other is OK. */ /* This fails for self-hosting, so skip in that case */ -#ifndef ENABLE_INNER +#if !defined(ENABLE_INNER) pushl $0 fstcw (%esp) cmpl $0x027F, (%esp) popl %esi /* get rid of the word without trashing %eflags */ jnz invariant_violation - cmpl $0, VG_(have_mxcsr_x86) - jz L2 #endif + cmpl $0, VG_(machine_x86_have_mxcsr) + jz L2 pushl $0 stmxcsr (%esp) andl $0xFFFFFFC0, (%esp) /* mask out status flags */ diff --git a/coregrind/m_machine.c b/coregrind/m_machine.c index f143f00c50..c0342533f4 100644 --- a/coregrind/m_machine.c +++ b/coregrind/m_machine.c @@ -33,6 +33,7 @@ #include "pub_core_libcassert.h" #include "pub_core_libcbase.h" #include "pub_core_machine.h" +#include "pub_core_cpuid.h" #define INSTR_PTR(regs) ((regs).vex.VG_INSTR_PTR) #define STACK_PTR(regs) ((regs).vex.VG_STACK_PTR) @@ -211,36 +212,131 @@ Bool VG_(thread_stack_next)(ThreadId* tid, Addr* stack_min, Addr* stack_max) return False; } -////////////////////////////////////////////////////////////////// -// Architecture specifics +//------------------------------------------------------------- +/* Details about the capabilities of the underlying (host) CPU. These + details are acquired by (1) enquiring with the CPU at startup, or + (2) from the AT_SYSINFO entries the kernel gave us (ppc32 cache + line size). It's a bit nasty in the sense that there's no obvious + way to stop uses of some of this info before it's ready to go. -#if defined(VGA_ppc32) -/* PPC: what is the cache line size (for dcbz etc) ? This info is - harvested on Linux at startup from the AT_SYSINFO entries. 0 means - not-yet-set. */ -Int VG_(cache_line_size_ppc32) = 0; + Current dependencies are: + + x86: initially: call VG_(machine_get_hwcaps) + + then safe to use VG_(machine_get_VexArchInfo) + and VG_(machine_x86_have_mxcsr) + ------------- + amd64: initially: call VG_(machine_get_hwcaps) + + then safe to use VG_(machine_get_VexArchInfo) + ------------- + ppc32: initially: call VG_(machine_get_hwcaps) + call VG_(machine_ppc32_set_clszB) + + then safe to use VG_(machine_get_VexArchInfo) + and VG_(machine_ppc32_has_FPU) + and VG_(machine_ppc32_has_VMX) + + VG_(machine_get_hwcaps) may use signals (although it attempts to + leave signal state unchanged) and therefore should only be + called before m_main sets up the client's signal state. +*/ -/* Altivec enabled? Harvested on startup from the AT_HWCAP entry. */ -Int VG_(have_altivec_ppc32) = 0; +/* --------- State --------- */ +static Bool hwcaps_done = False; + +/* --- all archs --- */ +static VexArch va; +static VexArchInfo vai; + +#if defined(VGA_x86) +UInt VG_(machine_x86_have_mxcsr) = 0; #endif +#if defined(VGA_ppc32) +UInt VG_(machine_ppc32_has_FPU) = 0; +UInt VG_(machine_ppc32_has_VMX) = 0; +#endif + + +/* Determine what insn set and insn set variant the host has, and + record it. To be called once at system startup. Returns False if + this a CPU incapable of running Valgrind. */ +Bool VG_(machine_get_hwcaps)( void ) +{ + vg_assert(hwcaps_done == False); + hwcaps_done = True; + + // Whack default settings into vai, so that we only need to fill in + // any interesting bits. + LibVEX_default_VexArchInfo(&vai); #if defined(VGA_x86) -/* X86: set to 1 if the host is able to do {ld,st}mxcsr (load/store - the SSE control/status register. For most modern CPUs this will be - 1. It is set to 1, if possible, by m_translate.getArchAndArchInfo. - The value is read by m_dispatch.dispatch-x86.S, which is why it is - an Int rather than a Bool. - - Ugly hack: this has to start as 0 and be set to 1 in the normal - case, rather than the other way round, because the dispatch loop - needs it, and it runs before the first translation is made. Yet it - is the act of making that first translation which causes - getArchAndArchInfo to set this value to its final value. So it is - necessary to start this value off at 0 as only that guarantees that - the dispatch loop will not SIGILL on its first attempt. */ -Int VG_(have_mxcsr_x86) = 0; + { Bool have_sse1, have_sse2; + UInt eax, ebx, ecx, edx; + + if (!VG_(has_cpuid)()) + /* we can't do cpuid at all. Give up. */ + return False; + + VG_(cpuid)(0, &eax, &ebx, &ecx, &edx); + if (eax < 1) + /* we can't ask for cpuid(x) for x > 0. Give up. */ + return False; + + /* get capabilities bits into edx */ + VG_(cpuid)(1, &eax, &ebx, &ecx, &edx); + + have_sse1 = (edx & (1<<25)) != 0; /* True => have sse insns */ + have_sse2 = (edx & (1<<26)) != 0; /* True => have sse2 insns */ + + if (have_sse2 && have_sse1) { + va = VexArchX86; + vai.subarch = VexSubArchX86_sse2; + VG_(machine_x86_have_mxcsr) = 1; + return True; + } + + if (have_sse1) { + va = VexArchX86; + vai.subarch = VexSubArchX86_sse1; + VG_(machine_x86_have_mxcsr) = 1; + return True; + } + + va = VexArchX86; + vai.subarch = VexSubArchX86_sse0; + VG_(machine_x86_have_mxcsr) = 0; + return True; + } + +#elif defined(VGA_amd64) + vg_assert(VG_(has_cpuid)()); + va = VexArchAMD64; + vai.subarch = VexSubArch_NONE; + return True; + +#elif defined(VGA_ppc32) + va = VexArchPPC32; + vai.subarch = VexSubArchPPC32_AV; + /* But we're not done yet: VG_(machine_ppc32_set_clszB) must be + called before we're ready to go. */ + return True; + +#else +# error "Unknown arch" #endif +} + + +/* Fetch host cpu info, once established. */ +void VG_(machine_get_VexArchInfo)( /*OUT*/VexArch* pVa, + /*OUT*/VexArchInfo* pVai ) +{ + vg_assert(hwcaps_done); + *pVa = va; + *pVai = vai; +} /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_main.c b/coregrind/m_main.c index 05503f2430..5423ce2561 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -2033,6 +2033,30 @@ Int main(Int argc, HChar **argv, HChar **envp) // Get the current process stack rlimit. VG_(getrlimit)(VKI_RLIMIT_STACK, &VG_(client_rlimit_stack)); + //-------------------------------------------------------------- + // Figure out what sort of CPU we're on, and whether it is + // able to run V. + VG_(debugLog)(1, "main", "Get hardware capabilities ...\n"); + { VexArch vex_arch; + VexArchInfo vex_archinfo; + Bool ok = VG_(machine_get_hwcaps)(); + if (!ok) { + VG_(printf)("\n"); + VG_(printf)("valgrind: fatal error: unsupported CPU.\n"); + VG_(printf)(" Supported CPUs are:\n"); + VG_(printf)(" * x86 (practically any; Pentium-I or above), " + "AMD Athlon or above)\n"); + VG_(printf)(" * AMD Athlon64/Opteron\n"); + VG_(printf)(" * PowerPC (most; ppc405 and above)\n"); + VG_(printf)("\n"); + VG_(exit)(1); + } + VG_(machine_get_VexArchInfo)( &vex_arch, &vex_archinfo ); + VG_(debugLog)(1, "main", "... arch = %s, subarch = %s\n", + LibVEX_ppVexArch ( vex_arch ), + LibVEX_ppVexSubArch( vex_archinfo.subarch ) ); + } + //============================================================ // Command line argument handling order: // * If --help/--help-debug are present, show usage message diff --git a/coregrind/m_translate.c b/coregrind/m_translate.c index ad109f5b5c..f6558a7a86 100644 --- a/coregrind/m_translate.c +++ b/coregrind/m_translate.c @@ -31,11 +31,9 @@ #include "pub_core_basics.h" #include "pub_core_aspacemgr.h" -#include "pub_core_cpuid.h" -#include "pub_core_machine.h" // For VG_(cache_line_size_ppc32) - // and VG_(have_altivec_ppc) + +#include "pub_core_machine.h" // For VG_(machine_get_VexArchInfo) // and VG_(get_SP) - // and VG_(have_mxcsr_x86) #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_libcprint.h" @@ -52,81 +50,6 @@ #include "pub_core_transtab.h" -/*------------------------------------------------------------*/ -/*--- Determining arch/subarch. ---*/ -/*------------------------------------------------------------*/ - -// Returns the architecture and auxiliary information, or indicates -// that this subarchitecture is unable to run Valgrind. Returns False -// to indicate we cannot proceed further. - -static Bool getArchAndArchInfo( /*OUT*/VexArch* vex_arch, - /*OUT*/VexArchInfo* vai ) -{ - // Whack default settings into vai, so that we only need to fill in - // any interesting bits. - LibVEX_default_VexArchInfo(vai); - -#if defined(VGA_x86) - { Bool have_sse1, have_sse2; - UInt eax, ebx, ecx, edx; - - if (!VG_(has_cpuid)()) - /* we can't do cpuid at all. Give up. */ - return False; - - VG_(cpuid)(0, &eax, &ebx, &ecx, &edx); - if (eax < 1) - /* we can't ask for cpuid(x) for x > 0. Give up. */ - return False; - - /* get capabilities bits into edx */ - VG_(cpuid)(1, &eax, &ebx, &ecx, &edx); - - have_sse1 = (edx & (1<<25)) != 0; /* True => have sse insns */ - have_sse2 = (edx & (1<<26)) != 0; /* True => have sse2 insns */ - - VG_(have_mxcsr_x86) = 1; - - if (have_sse2 && have_sse1) { - *vex_arch = VexArchX86; - vai->subarch = VexSubArchX86_sse2; - return True; - } - - if (have_sse1) { - *vex_arch = VexArchX86; - vai->subarch = VexSubArchX86_sse1; - return True; - } - - { - *vex_arch = VexArchX86; - vai->subarch = VexSubArchX86_sse0; - VG_(have_mxcsr_x86) = 0; - return True; - } - } - -#elif defined(VGA_amd64) - vg_assert(VG_(has_cpuid)()); - *vex_arch = VexArchAMD64; - vai->subarch = VexSubArch_NONE; - return True; - -#elif defined(VGA_ppc32) - *vex_arch = VexArchPPC32; - vai->subarch = VG_(have_altivec_ppc32) ? VexSubArchPPC32_AV - : VexSubArchPPC32_noAV; - vai->ppc32_cache_line_szB = VG_(cache_line_size_ppc32); - return True; - -#else -# error Unknown architecture -#endif -} - - /*------------------------------------------------------------*/ /*--- %SP-update pass ---*/ /*------------------------------------------------------------*/ @@ -524,42 +447,21 @@ Bool VG_(translate) ( ThreadId tid, Int debugging_verbosity, ULong bbs_done ) { - Addr64 redir, orig_addr_noredir = orig_addr; - Int tmpbuf_used, verbosity, i; - Bool notrace_until_done, do_self_check; - UInt notrace_until_limit = 0; - NSegment* seg; - VexGuestExtents vge; - - /* Indicates what arch we are running on, and other important info - (subarch variant, cache line size). */ - static VexArchInfo vex_archinfo; - static VexArch vex_arch = VexArch_INVALID; + Addr64 redir, orig_addr_noredir = orig_addr; + Int tmpbuf_used, verbosity, i; + Bool notrace_until_done, do_self_check; + UInt notrace_until_limit = 0; + NSegment* seg; + VexArch vex_arch; + VexArchInfo vex_archinfo; + VexGuestExtents vge; + VexTranslateResult tres; /* Make sure Vex is initialised right. */ - VexTranslateResult tres; + static Bool vex_init_done = False; if (!vex_init_done) { - Bool ok = getArchAndArchInfo( &vex_arch, &vex_archinfo ); - if (!ok) { - VG_(printf)("\n"); - VG_(printf)("valgrind: fatal error: unsupported CPU.\n"); - VG_(printf)(" Supported CPUs are:\n"); - VG_(printf)(" * x86 (practically any; Pentium-I or above), " - "AMD Athlon or above)\n"); - VG_(printf)(" * AMD Athlon64/Opteron\n"); - VG_(printf)(" * PowerPC with Altivec\n"); - VG_(printf)("\n"); - VG_(exit)(1); - } - if (VG_(clo_verbosity) > 2) { - VG_(message)(Vg_DebugMsg, - "Host CPU: arch = %s, subarch = %s", - LibVEX_ppVexArch ( vex_arch ), - LibVEX_ppVexSubArch( vex_archinfo.subarch ) ); - } - LibVEX_Init ( &failure_exit, &log_bytes, 1, /* debug_paranoia */ False, /* valgrind support */ @@ -664,10 +566,13 @@ Bool VG_(translate) ( ThreadId tid, VGP_PUSHCC(VgpVexTime); - /* Actually do the translation. */ + /* ------ Actually do the translation. ------ */ tl_assert2(VG_(tdict).tool_instrument, "you forgot to set VgToolInterface function 'tool_instrument'"); + /* Get the CPU info established at startup. */ + VG_(machine_get_VexArchInfo)( &vex_arch, &vex_archinfo ); + /* Set up closure arg for "chase_into_ok" */ chase_into_ok__CLOSURE_tid = tid; diff --git a/coregrind/pub_core_machine.h b/coregrind/pub_core_machine.h index 2046fe6768..f22bfcc080 100644 --- a/coregrind/pub_core_machine.h +++ b/coregrind/pub_core_machine.h @@ -76,23 +76,70 @@ #define VG_O_STACK_PTR (offsetof(VexGuestArchState, VG_STACK_PTR)) -// Architecture specifics +//------------------------------------------------------------- +/* Details about the capabilities of the underlying (host) CPU. These + details are acquired by (1) enquiring with the CPU at startup, or + (2) from the AT_SYSINFO entries the kernel gave us (ppc32 cache + line size). It's a bit nasty in the sense that there's no obvious + way to stop uses of some of this info before it's ready to go. + + Current dependencies are: + + x86: initially: call VG_(machine_get_hwcaps) + + then safe to use VG_(machine_get_VexArchInfo) + and VG_(machine_x86_have_mxcsr) + ------------- + amd64: initially: call VG_(machine_get_hwcaps) + + then safe to use VG_(machine_get_VexArchInfo) + ------------- + ppc32: initially: call VG_(machine_get_hwcaps) + call VG_(machine_ppc32_set_clszB) + + then safe to use VG_(machine_get_VexArchInfo) + and VG_(machine_ppc32_has_FPU) + and VG_(machine_ppc32_has_VMX) + + VG_(machine_get_hwcaps) may use signals (although it attempts to + leave signal state unchanged) and therefore should only be + called before m_main sets up the client's signal state. +*/ + +/* Determine what insn set and insn set variant the host has, and + record it. To be called once at system startup. Returns False if + this a CPU incapable of running Valgrind. */ +extern Bool VG_(machine_get_hwcaps)( void ); +/* Fetch host cpu info, as per above comment. */ +extern void VG_(machine_get_VexArchInfo)( /*OUT*/VexArch*, + /*OUT*/VexArchInfo* ); + +/* Notify host cpu cache line size, as per above comment. */ #if defined(VGA_ppc32) -// PPC: what is the cache line size (for dcbz etc) ? -// This info is harvested on Linux at startup from the AT_SYSINFO -// entries. -extern Int VG_(cache_line_size_ppc32); -// Altivec enabled? Harvested on startup from the AT_HWCAP entry -extern Int VG_(have_altivec_ppc32); +extern void VG_(machine_ppc32_set_clszB)( Int ); #endif -// X86: set to 1 if the host is able to do {ld,st}mxcsr (load/store -// the SSE control/status register. +/* X86: set to 1 if the host is able to do {ld,st}mxcsr (load/store + the SSE control/status register), else zero. Is referenced from + assembly code, so do not change from a 32-bit int. */ #if defined(VGA_x86) -extern Int VG_(have_mxcsr_x86); +extern UInt VG_(machine_x86_have_mxcsr); +#endif + +/* PPC32: set to 1 if FP instructions are supported in user-space, + else 0. Is referenced from assembly code, so do not change from a + 32-bit int. */ +#if defined(VGA_ppc32) +extern UInt VG_(machine_ppc32_has_FPU); #endif +/* PPC32: set to 1 if Altivec instructions are supported in + user-space, else 0. Is referenced from assembly code, so do not + change from a 32-bit int. */ +#if defined(VGA_ppc32) +extern UInt VG_(machine_ppc32_has_VMX); +#endif #endif // __PUB_CORE_MACHINE_H