summaryrefslogtreecommitdiff
path: root/coregrind/m_machine.c
diff options
context:
space:
mode:
authorStephane Marchesin <marchesin@icps.u-strasbg.fr>2009-05-04 19:05:59 +0200
committerStephane Marchesin <marchesin@icps.u-strasbg.fr>2009-05-04 19:05:59 +0200
commit6e410b3bb6ff51580897431105aae14591cbf7fb (patch)
treef8aeba9352710f10cd6b1d5138c8fc3ece91c8c3 /coregrind/m_machine.c
Initial import of fatgrind.HEADmaster
Diffstat (limited to 'coregrind/m_machine.c')
-rw-r--r--coregrind/m_machine.c676
1 files changed, 676 insertions, 0 deletions
diff --git a/coregrind/m_machine.c b/coregrind/m_machine.c
new file mode 100644
index 0000000..8d5901f
--- /dev/null
+++ b/coregrind/m_machine.c
@@ -0,0 +1,676 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Machine-related stuff. m_machine.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2000-2009 Julian Seward
+ jseward@acm.org
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_machine.h"
+#include "pub_core_cpuid.h"
+#include "pub_core_libcsignal.h" // for ppc32 messing with SIGILL and SIGFPE
+#include "pub_core_debuglog.h"
+
+
+#define INSTR_PTR(regs) ((regs).vex.VG_INSTR_PTR)
+#define STACK_PTR(regs) ((regs).vex.VG_STACK_PTR)
+#define FRAME_PTR(regs) ((regs).vex.VG_FRAME_PTR)
+
+Addr VG_(get_SP) ( ThreadId tid )
+{
+ return STACK_PTR( VG_(threads)[tid].arch );
+}
+
+Addr VG_(get_IP) ( ThreadId tid )
+{
+ return INSTR_PTR( VG_(threads)[tid].arch );
+}
+
+Addr VG_(get_FP) ( ThreadId tid )
+{
+ return FRAME_PTR( VG_(threads)[tid].arch );
+}
+
+Addr VG_(get_LR) ( ThreadId tid )
+{
+# if defined(VGA_ppc32) || defined(VGA_ppc64)
+ return VG_(threads)[tid].arch.vex.guest_LR;
+# elif defined(VGA_x86) || defined(VGA_amd64)
+ return 0;
+# else
+# error "Unknown arch"
+# endif
+}
+
+void VG_(set_SP) ( ThreadId tid, Addr sp )
+{
+ STACK_PTR( VG_(threads)[tid].arch ) = sp;
+}
+
+void VG_(set_IP) ( ThreadId tid, Addr ip )
+{
+ INSTR_PTR( VG_(threads)[tid].arch ) = ip;
+}
+
+void VG_(set_syscall_return_shadows) ( ThreadId tid,
+ /* shadow vals for the result */
+ UWord s1res, UWord s2res,
+ /* shadow vals for the error val */
+ UWord s1err, UWord s2err )
+{
+# if defined(VGP_x86_linux)
+ VG_(threads)[tid].arch.vex_shadow1.guest_EAX = s1res;
+ VG_(threads)[tid].arch.vex_shadow2.guest_EAX = s2res;
+# elif defined(VGP_amd64_linux)
+ VG_(threads)[tid].arch.vex_shadow1.guest_RAX = s1res;
+ VG_(threads)[tid].arch.vex_shadow2.guest_RAX = s2res;
+# elif defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
+ VG_(threads)[tid].arch.vex_shadow1.guest_GPR3 = s1res;
+ VG_(threads)[tid].arch.vex_shadow2.guest_GPR3 = s2res;
+# elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
+ VG_(threads)[tid].arch.vex_shadow1.guest_GPR3 = s1res;
+ VG_(threads)[tid].arch.vex_shadow2.guest_GPR3 = s2res;
+ VG_(threads)[tid].arch.vex_shadow1.guest_GPR4 = s1err;
+ VG_(threads)[tid].arch.vex_shadow2.guest_GPR4 = s2err;
+# else
+# error "Unknown plat"
+# endif
+}
+
+void
+VG_(get_shadow_regs_area) ( ThreadId tid,
+ /*DST*/UChar* dst,
+ /*SRC*/Int shadowNo, PtrdiffT offset, SizeT size )
+{
+ void* src;
+ ThreadState* tst;
+ vg_assert(shadowNo == 0 || shadowNo == 1 || shadowNo == 2);
+ vg_assert(VG_(is_valid_tid)(tid));
+ // Bounds check
+ vg_assert(0 <= offset && offset < sizeof(VexGuestArchState));
+ vg_assert(offset + size <= sizeof(VexGuestArchState));
+ // Copy
+ tst = & VG_(threads)[tid];
+ src = NULL;
+ switch (shadowNo) {
+ case 0: src = (void*)(((Addr)&(tst->arch.vex)) + offset); break;
+ case 1: src = (void*)(((Addr)&(tst->arch.vex_shadow1)) + offset); break;
+ case 2: src = (void*)(((Addr)&(tst->arch.vex_shadow2)) + offset); break;
+ }
+ tl_assert(src != NULL);
+ VG_(memcpy)( dst, src, size);
+}
+
+void
+VG_(set_shadow_regs_area) ( ThreadId tid,
+ /*DST*/Int shadowNo, PtrdiffT offset, SizeT size,
+ /*SRC*/const UChar* src )
+{
+ void* dst;
+ ThreadState* tst;
+ vg_assert(shadowNo == 0 || shadowNo == 1 || shadowNo == 2);
+ vg_assert(VG_(is_valid_tid)(tid));
+ // Bounds check
+ vg_assert(0 <= offset && offset < sizeof(VexGuestArchState));
+ vg_assert(offset + size <= sizeof(VexGuestArchState));
+ // Copy
+ tst = & VG_(threads)[tid];
+ dst = NULL;
+ switch (shadowNo) {
+ case 0: dst = (void*)(((Addr)&(tst->arch.vex)) + offset); break;
+ case 1: dst = (void*)(((Addr)&(tst->arch.vex_shadow1)) + offset); break;
+ case 2: dst = (void*)(((Addr)&(tst->arch.vex_shadow2)) + offset); break;
+ }
+ tl_assert(dst != NULL);
+ VG_(memcpy)( dst, src, size);
+}
+
+
+static void apply_to_GPs_of_tid(VexGuestArchState* vex, void (*f)(Addr))
+{
+#if defined(VGA_x86)
+ (*f)(vex->guest_EAX);
+ (*f)(vex->guest_ECX);
+ (*f)(vex->guest_EDX);
+ (*f)(vex->guest_EBX);
+ (*f)(vex->guest_ESI);
+ (*f)(vex->guest_EDI);
+ (*f)(vex->guest_ESP);
+ (*f)(vex->guest_EBP);
+#elif defined(VGA_amd64)
+ (*f)(vex->guest_RAX);
+ (*f)(vex->guest_RCX);
+ (*f)(vex->guest_RDX);
+ (*f)(vex->guest_RBX);
+ (*f)(vex->guest_RSI);
+ (*f)(vex->guest_RDI);
+ (*f)(vex->guest_RSP);
+ (*f)(vex->guest_RBP);
+ (*f)(vex->guest_R8);
+ (*f)(vex->guest_R9);
+ (*f)(vex->guest_R10);
+ (*f)(vex->guest_R11);
+ (*f)(vex->guest_R12);
+ (*f)(vex->guest_R13);
+ (*f)(vex->guest_R14);
+ (*f)(vex->guest_R15);
+#elif defined(VGA_ppc32) || defined(VGA_ppc64)
+ /* XXX ask tool about validity? */
+ (*f)(vex->guest_GPR0);
+ (*f)(vex->guest_GPR1);
+ (*f)(vex->guest_GPR2);
+ (*f)(vex->guest_GPR3);
+ (*f)(vex->guest_GPR4);
+ (*f)(vex->guest_GPR5);
+ (*f)(vex->guest_GPR6);
+ (*f)(vex->guest_GPR7);
+ (*f)(vex->guest_GPR8);
+ (*f)(vex->guest_GPR9);
+ (*f)(vex->guest_GPR10);
+ (*f)(vex->guest_GPR11);
+ (*f)(vex->guest_GPR12);
+ (*f)(vex->guest_GPR13);
+ (*f)(vex->guest_GPR14);
+ (*f)(vex->guest_GPR15);
+ (*f)(vex->guest_GPR16);
+ (*f)(vex->guest_GPR17);
+ (*f)(vex->guest_GPR18);
+ (*f)(vex->guest_GPR19);
+ (*f)(vex->guest_GPR20);
+ (*f)(vex->guest_GPR21);
+ (*f)(vex->guest_GPR22);
+ (*f)(vex->guest_GPR23);
+ (*f)(vex->guest_GPR24);
+ (*f)(vex->guest_GPR25);
+ (*f)(vex->guest_GPR26);
+ (*f)(vex->guest_GPR27);
+ (*f)(vex->guest_GPR28);
+ (*f)(vex->guest_GPR29);
+ (*f)(vex->guest_GPR30);
+ (*f)(vex->guest_GPR31);
+ (*f)(vex->guest_CTR);
+ (*f)(vex->guest_LR);
+
+#else
+# error Unknown arch
+#endif
+}
+
+
+void VG_(apply_to_GP_regs)(void (*f)(UWord))
+{
+ ThreadId tid;
+
+ for (tid = 1; tid < VG_N_THREADS; tid++) {
+ if (VG_(is_valid_tid)(tid)) {
+ ThreadState* tst = VG_(get_ThreadState)(tid);
+ apply_to_GPs_of_tid(&(tst->arch.vex), f);
+ }
+ }
+}
+
+void VG_(thread_stack_reset_iter)(/*OUT*/ThreadId* tid)
+{
+ *tid = (ThreadId)(-1);
+}
+
+Bool VG_(thread_stack_next)(/*MOD*/ThreadId* tid,
+ /*OUT*/Addr* stack_min,
+ /*OUT*/Addr* stack_max)
+{
+ ThreadId i;
+ for (i = (*tid)+1; i < VG_N_THREADS; i++) {
+ if (i == VG_INVALID_THREADID)
+ continue;
+ if (VG_(threads)[i].status != VgTs_Empty) {
+ *tid = i;
+ *stack_min = VG_(get_SP)(i);
+ *stack_max = VG_(threads)[i].client_stack_highest_word;
+ return True;
+ }
+ }
+ return False;
+}
+
+Addr VG_(thread_get_stack_max)(ThreadId tid)
+{
+ vg_assert(0 <= tid && tid < VG_N_THREADS && tid != VG_INVALID_THREADID);
+ vg_assert(VG_(threads)[tid].status != VgTs_Empty);
+ return VG_(threads)[tid].client_stack_highest_word;
+}
+
+SizeT VG_(thread_get_stack_size)(ThreadId tid)
+{
+ vg_assert(0 <= tid && tid < VG_N_THREADS && tid != VG_INVALID_THREADID);
+ vg_assert(VG_(threads)[tid].status != VgTs_Empty);
+ return VG_(threads)[tid].client_stack_szB;
+}
+
+//-------------------------------------------------------------
+/* 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_FP)
+ and VG_(machine_ppc32_has_VMX)
+ -------------
+ ppc64: initially: call VG_(machine_get_hwcaps)
+ call VG_(machine_ppc64_set_clszB)
+
+ then safe to use VG_(machine_get_VexArchInfo)
+ and VG_(machine_ppc64_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.
+*/
+
+/* --------- 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_FP) = 0;
+UInt VG_(machine_ppc32_has_VMX) = 0;
+#endif
+#if defined(VGA_ppc64)
+ULong VG_(machine_ppc64_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. */
+
+#if defined(VGA_ppc32) || defined(VGA_ppc64)
+#include <setjmp.h> // For jmp_buf
+static jmp_buf env_unsup_insn;
+static void handler_unsup_insn ( Int x ) { __builtin_longjmp(env_unsup_insn,1); }
+#endif
+
+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)
+ { 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.hwcaps = VEX_HWCAPS_X86_SSE1;
+ vai.hwcaps |= VEX_HWCAPS_X86_SSE2;
+ VG_(machine_x86_have_mxcsr) = 1;
+ return True;
+ }
+
+ if (have_sse1) {
+ va = VexArchX86;
+ vai.hwcaps = VEX_HWCAPS_X86_SSE1;
+ VG_(machine_x86_have_mxcsr) = 1;
+ return True;
+ }
+
+ va = VexArchX86;
+ vai.hwcaps = 0; /*baseline - no sse at all*/
+ VG_(machine_x86_have_mxcsr) = 0;
+ return True;
+ }
+
+#elif defined(VGA_amd64)
+ vg_assert(VG_(has_cpuid)());
+ va = VexArchAMD64;
+ vai.hwcaps = 0; /*baseline - SSE2 */
+ return True;
+
+#elif defined(VGA_ppc32)
+ {
+ /* Find out which subset of the ppc32 instruction set is supported by
+ verifying whether various ppc32 instructions generate a SIGILL
+ or a SIGFPE. An alternative approach is to check the AT_HWCAP and
+ AT_PLATFORM entries in the ELF auxiliary table -- see also
+ the_iifii.client_auxv in m_main.c.
+ */
+ vki_sigset_t saved_set, tmp_set;
+ struct vki_sigaction saved_sigill_act, tmp_sigill_act;
+ struct vki_sigaction saved_sigfpe_act, tmp_sigfpe_act;
+
+ volatile Bool have_F, have_V, have_FX, have_GX;
+ Int r;
+
+ VG_(sigemptyset)(&tmp_set);
+ VG_(sigaddset)(&tmp_set, VKI_SIGILL);
+ VG_(sigaddset)(&tmp_set, VKI_SIGFPE);
+
+ r = VG_(sigprocmask)(VKI_SIG_UNBLOCK, &tmp_set, &saved_set);
+ vg_assert(r == 0);
+
+ r = VG_(sigaction)(VKI_SIGILL, NULL, &saved_sigill_act);
+ vg_assert(r == 0);
+ tmp_sigill_act = saved_sigill_act;
+
+ r = VG_(sigaction)(VKI_SIGFPE, NULL, &saved_sigfpe_act);
+ vg_assert(r == 0);
+ tmp_sigfpe_act = saved_sigfpe_act;
+
+ /* NODEFER: signal handler does not return (from the kernel's point of
+ view), hence if it is to successfully catch a signal more than once,
+ we need the NODEFER flag. */
+ tmp_sigill_act.sa_flags &= ~VKI_SA_RESETHAND;
+ tmp_sigill_act.sa_flags &= ~VKI_SA_SIGINFO;
+ tmp_sigill_act.sa_flags |= VKI_SA_NODEFER;
+ tmp_sigill_act.ksa_handler = handler_unsup_insn;
+ r = VG_(sigaction)(VKI_SIGILL, &tmp_sigill_act, NULL);
+ vg_assert(r == 0);
+
+ tmp_sigfpe_act.sa_flags &= ~VKI_SA_RESETHAND;
+ tmp_sigfpe_act.sa_flags &= ~VKI_SA_SIGINFO;
+ tmp_sigfpe_act.sa_flags |= VKI_SA_NODEFER;
+ tmp_sigfpe_act.ksa_handler = handler_unsup_insn;
+ r = VG_(sigaction)(VKI_SIGFPE, &tmp_sigfpe_act, NULL);
+ vg_assert(r == 0);
+
+ /* standard FP insns */
+ have_F = True;
+ if (__builtin_setjmp(env_unsup_insn)) {
+ have_F = False;
+ } else {
+ __asm__ __volatile__(".long 0xFC000090"); /*fmr 0,0 */
+ }
+
+ /* Altivec insns */
+ have_V = True;
+ if (__builtin_setjmp(env_unsup_insn)) {
+ have_V = False;
+ } else {
+ /* Unfortunately some older assemblers don't speak Altivec (or
+ choose not to), so to be safe we directly emit the 32-bit
+ word corresponding to "vor 0,0,0". This fixes a build
+ problem that happens on Debian 3.1 (ppc32), and probably
+ various other places. */
+ __asm__ __volatile__(".long 0x10000484"); /*vor 0,0,0*/
+ }
+
+ /* General-Purpose optional (fsqrt, fsqrts) */
+ have_FX = True;
+ if (__builtin_setjmp(env_unsup_insn)) {
+ have_FX = False;
+ } else {
+ __asm__ __volatile__(".long 0xFC00002C"); /*fsqrt 0,0 */
+ }
+
+ /* Graphics optional (stfiwx, fres, frsqrte, fsel) */
+ have_GX = True;
+ if (__builtin_setjmp(env_unsup_insn)) {
+ have_GX = False;
+ } else {
+ __asm__ __volatile__(".long 0xFC000034"); /* frsqrte 0,0 */
+ }
+
+ r = VG_(sigaction)(VKI_SIGILL, &saved_sigill_act, NULL);
+ vg_assert(r == 0);
+ r = VG_(sigaction)(VKI_SIGFPE, &saved_sigfpe_act, NULL);
+ vg_assert(r == 0);
+ r = VG_(sigprocmask)(VKI_SIG_SETMASK, &saved_set, NULL);
+ vg_assert(r == 0);
+ VG_(debugLog)(1, "machine", "F %d V %d FX %d GX %d\n",
+ (Int)have_F, (Int)have_V, (Int)have_FX, (Int)have_GX);
+ /* Make FP a prerequisite for VMX (bogusly so), and for FX and GX. */
+ if (have_V && !have_F)
+ have_V = False;
+ if (have_FX && !have_F)
+ have_FX = False;
+ if (have_GX && !have_F)
+ have_GX = False;
+
+ VG_(machine_ppc32_has_FP) = have_F ? 1 : 0;
+ VG_(machine_ppc32_has_VMX) = have_V ? 1 : 0;
+
+ va = VexArchPPC32;
+
+ vai.hwcaps = 0;
+ if (have_F) vai.hwcaps |= VEX_HWCAPS_PPC32_F;
+ if (have_V) vai.hwcaps |= VEX_HWCAPS_PPC32_V;
+ if (have_FX) vai.hwcaps |= VEX_HWCAPS_PPC32_FX;
+ if (have_GX) vai.hwcaps |= VEX_HWCAPS_PPC32_GX;
+
+ /* But we're not done yet: VG_(machine_ppc32_set_clszB) must be
+ called before we're ready to go. */
+ return True;
+ }
+
+#elif defined(VGA_ppc64)
+ {
+ /* Same instruction set detection algorithm as for ppc32. */
+ vki_sigset_t saved_set, tmp_set;
+ struct vki_sigaction saved_sigill_act, tmp_sigill_act;
+ struct vki_sigaction saved_sigfpe_act, tmp_sigfpe_act;
+
+ volatile Bool have_F, have_V, have_FX, have_GX;
+
+ VG_(sigemptyset)(&tmp_set);
+ VG_(sigaddset)(&tmp_set, VKI_SIGILL);
+ VG_(sigaddset)(&tmp_set, VKI_SIGFPE);
+
+ VG_(sigprocmask)(VKI_SIG_UNBLOCK, &tmp_set, &saved_set);
+
+ VG_(sigaction)(VKI_SIGILL, NULL, &saved_sigill_act);
+ tmp_sigill_act = saved_sigill_act;
+
+ VG_(sigaction)(VKI_SIGFPE, NULL, &saved_sigfpe_act);
+ tmp_sigfpe_act = saved_sigfpe_act;
+
+
+ /* NODEFER: signal handler does not return (from the kernel's point of
+ view), hence if it is to successfully catch a signal more than once,
+ we need the NODEFER flag. */
+ tmp_sigill_act.sa_flags &= ~VKI_SA_RESETHAND;
+ tmp_sigill_act.sa_flags &= ~VKI_SA_SIGINFO;
+ tmp_sigill_act.sa_flags |= VKI_SA_NODEFER;
+ tmp_sigill_act.ksa_handler = handler_unsup_insn;
+ VG_(sigaction)(VKI_SIGILL, &tmp_sigill_act, NULL);
+
+ tmp_sigfpe_act.sa_flags &= ~VKI_SA_RESETHAND;
+ tmp_sigfpe_act.sa_flags &= ~VKI_SA_SIGINFO;
+ tmp_sigfpe_act.sa_flags |= VKI_SA_NODEFER;
+ tmp_sigfpe_act.ksa_handler = handler_unsup_insn;
+ VG_(sigaction)(VKI_SIGFPE, &tmp_sigfpe_act, NULL);
+
+ /* standard FP insns */
+ have_F = True;
+ if (__builtin_setjmp(env_unsup_insn)) {
+ have_F = False;
+ } else {
+ __asm__ __volatile__("fmr 0,0");
+ }
+
+ /* Altivec insns */
+ have_V = True;
+ if (__builtin_setjmp(env_unsup_insn)) {
+ have_V = False;
+ } else {
+ __asm__ __volatile__(".long 0x10000484"); /*vor 0,0,0*/
+ }
+
+ /* General-Purpose optional (fsqrt, fsqrts) */
+ have_FX = True;
+ if (__builtin_setjmp(env_unsup_insn)) {
+ have_FX = False;
+ } else {
+ __asm__ __volatile__(".long 0xFC00002C"); /*fsqrt 0,0*/
+ }
+
+ /* Graphics optional (stfiwx, fres, frsqrte, fsel) */
+ have_GX = True;
+ if (__builtin_setjmp(env_unsup_insn)) {
+ have_GX = False;
+ } else {
+ __asm__ __volatile__(".long 0xFC000034"); /*frsqrte 0,0*/
+ }
+
+ VG_(sigaction)(VKI_SIGILL, &saved_sigill_act, NULL);
+ VG_(sigaction)(VKI_SIGFPE, &saved_sigfpe_act, NULL);
+ VG_(sigprocmask)(VKI_SIG_SETMASK, &saved_set, NULL);
+ VG_(debugLog)(1, "machine", "F %d V %d FX %d GX %d\n",
+ (Int)have_F, (Int)have_V, (Int)have_FX, (Int)have_GX);
+ /* on ppc64, if we don't even have FP, just give up. */
+ if (!have_F)
+ return False;
+
+ VG_(machine_ppc64_has_VMX) = have_V ? 1 : 0;
+
+ va = VexArchPPC64;
+
+ vai.hwcaps = 0;
+ if (have_V) vai.hwcaps |= VEX_HWCAPS_PPC64_V;
+ if (have_FX) vai.hwcaps |= VEX_HWCAPS_PPC64_FX;
+ if (have_GX) vai.hwcaps |= VEX_HWCAPS_PPC64_GX;
+
+ /* But we're not done yet: VG_(machine_ppc64_set_clszB) must be
+ called before we're ready to go. */
+ return True;
+ }
+
+#else
+# error "Unknown arch"
+#endif
+}
+
+/* Notify host cpu cache line size. */
+#if defined(VGA_ppc32)
+void VG_(machine_ppc32_set_clszB)( Int szB )
+{
+ vg_assert(hwcaps_done);
+
+ /* Either the value must not have been set yet (zero) or we can
+ tolerate it being set to the same value multiple times, as the
+ stack scanning logic in m_main is a bit stupid. */
+ vg_assert(vai.ppc_cache_line_szB == 0
+ || vai.ppc_cache_line_szB == szB);
+
+ vg_assert(szB == 32 || szB == 64 || szB == 128);
+ vai.ppc_cache_line_szB = szB;
+}
+#endif
+
+
+/* Notify host cpu cache line size. */
+#if defined(VGA_ppc64)
+void VG_(machine_ppc64_set_clszB)( Int szB )
+{
+ vg_assert(hwcaps_done);
+
+ /* Either the value must not have been set yet (zero) or we can
+ tolerate it being set to the same value multiple times, as the
+ stack scanning logic in m_main is a bit stupid. */
+ vg_assert(vai.ppc_cache_line_szB == 0
+ || vai.ppc_cache_line_szB == szB);
+
+ vg_assert(szB == 32 || szB == 64 || szB == 128);
+ vai.ppc_cache_line_szB = szB;
+}
+#endif
+
+
+/* Fetch host cpu info, once established. */
+void VG_(machine_get_VexArchInfo)( /*OUT*/VexArch* pVa,
+ /*OUT*/VexArchInfo* pVai )
+{
+ vg_assert(hwcaps_done);
+ if (pVa) *pVa = va;
+ if (pVai) *pVai = vai;
+}
+
+
+// Given a pointer to a function as obtained by "& functionname" in C,
+// produce a pointer to the actual entry point for the function.
+void* VG_(fnptr_to_fnentry)( void* f )
+{
+#if defined(VGP_x86_linux) || defined(VGP_amd64_linux) \
+ || defined(VGP_ppc32_linux)
+ return f;
+#elif defined(VGP_ppc64_linux) || defined(VGP_ppc32_aix5) \
+ || defined(VGP_ppc64_aix5)
+ /* All other ppc variants use the AIX scheme, in which f is a
+ pointer to a 3-word function descriptor, of which the first word
+ is the entry address. */
+ UWord* descr = (UWord*)f;
+ return (void*)(descr[0]);
+#else
+# error "Unknown platform"
+#endif
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/