| |
| /*---------------------------------------------------------------*/ |
| /*--- begin guest_mips_helpers.c ---*/ |
| /*---------------------------------------------------------------*/ |
| |
| /* |
| This file is part of Valgrind, a dynamic binary instrumentation |
| framework. |
| |
| Copyright (C) 2010-2015 RT-RK |
| mips-valgrind@rt-rk.com |
| |
| 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 "libvex_basictypes.h" |
| #include "libvex_emnote.h" |
| #include "libvex_guest_mips32.h" |
| #include "libvex_guest_mips64.h" |
| #include "libvex_ir.h" |
| #include "libvex.h" |
| |
| #include "main_util.h" |
| #include "main_globals.h" |
| #include "guest_generic_bb_to_IR.h" |
| #include "guest_mips_defs.h" |
| |
| /* This file contains helper functions for mips guest code. Calls to |
| these functions are generated by the back end. |
| */ |
| |
| #define ALWAYSDEFD32(field) \ |
| { offsetof(VexGuestMIPS32State, field), \ |
| (sizeof ((VexGuestMIPS32State*)0)->field) } |
| |
| #define ALWAYSDEFD64(field) \ |
| { offsetof(VexGuestMIPS64State, field), \ |
| (sizeof ((VexGuestMIPS64State*)0)->field) } |
| |
| IRExpr *guest_mips32_spechelper(const HChar * function_name, IRExpr ** args, |
| IRStmt ** precedingStmts, Int n_precedingStmts) |
| { |
| return NULL; |
| } |
| |
| IRExpr *guest_mips64_spechelper ( const HChar * function_name, IRExpr ** args, |
| IRStmt ** precedingStmts, |
| Int n_precedingStmts ) |
| { |
| return NULL; |
| } |
| |
| /* VISIBLE TO LIBVEX CLIENT */ |
| void LibVEX_GuestMIPS32_initialise( /*OUT*/ VexGuestMIPS32State * vex_state) |
| { |
| vex_state->guest_r0 = 0; /* Hardwired to 0 */ |
| vex_state->guest_r1 = 0; /* Assembler temporary */ |
| vex_state->guest_r2 = 0; /* Values for function returns ... */ |
| vex_state->guest_r3 = 0; /* ...and expression evaluation */ |
| vex_state->guest_r4 = 0; /* Function arguments */ |
| vex_state->guest_r5 = 0; |
| vex_state->guest_r6 = 0; |
| vex_state->guest_r7 = 0; |
| vex_state->guest_r8 = 0; /* Temporaries */ |
| vex_state->guest_r9 = 0; |
| vex_state->guest_r10 = 0; |
| vex_state->guest_r11 = 0; |
| vex_state->guest_r12 = 0; |
| vex_state->guest_r13 = 0; |
| vex_state->guest_r14 = 0; |
| vex_state->guest_r15 = 0; |
| vex_state->guest_r16 = 0; /* Saved temporaries */ |
| vex_state->guest_r17 = 0; |
| vex_state->guest_r18 = 0; |
| vex_state->guest_r19 = 0; |
| vex_state->guest_r20 = 0; |
| vex_state->guest_r21 = 0; |
| vex_state->guest_r22 = 0; |
| vex_state->guest_r23 = 0; |
| vex_state->guest_r24 = 0; /* Temporaries */ |
| vex_state->guest_r25 = 0; |
| vex_state->guest_r26 = 0; /* Reserved for OS kernel */ |
| vex_state->guest_r27 = 0; |
| vex_state->guest_r28 = 0; /* Global pointer */ |
| vex_state->guest_r29 = 0; /* Stack pointer */ |
| vex_state->guest_r30 = 0; /* Frame pointer */ |
| vex_state->guest_r31 = 0; /* Return address */ |
| vex_state->guest_PC = 0; /* Program counter */ |
| vex_state->guest_HI = 0; /* Multiply and divide register higher result */ |
| vex_state->guest_LO = 0; /* Multiply and divide register lower result */ |
| |
| /* FPU Registers */ |
| vex_state->guest_f0 = 0x7ff800007ff80000ULL; /* Floting point GP registers */ |
| vex_state->guest_f1 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f2 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f3 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f4 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f5 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f6 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f7 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f8 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f9 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f10 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f11 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f12 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f13 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f14 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f15 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f16 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f17 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f18 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f19 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f20 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f21 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f22 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f23 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f24 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f25 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f26 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f27 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f28 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f29 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f30 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f31 = 0x7ff800007ff80000ULL; |
| |
| vex_state->guest_FIR = 0; /* FP implementation and revision register */ |
| vex_state->guest_FCCR = 0; /* FP condition codes register */ |
| vex_state->guest_FEXR = 0; /* FP exceptions register */ |
| vex_state->guest_FENR = 0; /* FP enables register */ |
| vex_state->guest_FCSR = 0; /* FP control/status register */ |
| vex_state->guest_ULR = 0; /* TLS */ |
| |
| /* Various pseudo-regs mandated by Vex or Valgrind. */ |
| /* Emulation notes */ |
| vex_state->guest_EMNOTE = 0; |
| |
| /* For clflush: record start and length of area to invalidate */ |
| vex_state->guest_CMSTART = 0; |
| vex_state->guest_CMLEN = 0; |
| vex_state->host_EvC_COUNTER = 0; |
| vex_state->host_EvC_FAILADDR = 0; |
| |
| /* Used to record the unredirected guest address at the start of |
| a translation whose start has been redirected. By reading |
| this pseudo-register shortly afterwards, the translation can |
| find out what the corresponding no-redirection address was. |
| Note, this is only set for wrap-style redirects, not for |
| replace-style ones. */ |
| vex_state->guest_NRADDR = 0; |
| |
| vex_state->guest_COND = 0; |
| |
| vex_state->guest_CP0_status = 0; |
| |
| /* MIPS32 DSP ASE(r2) specific registers */ |
| vex_state->guest_DSPControl = 0; /* DSPControl register */ |
| vex_state->guest_ac0 = 0; /* Accumulator 0 */ |
| vex_state->guest_ac1 = 0; /* Accumulator 1 */ |
| vex_state->guest_ac2 = 0; /* Accumulator 2 */ |
| vex_state->guest_ac3 = 0; /* Accumulator 3 */ |
| } |
| |
| void LibVEX_GuestMIPS64_initialise ( /*OUT*/ VexGuestMIPS64State * vex_state ) |
| { |
| vex_state->guest_r0 = 0; /* Hardwired to 0 */ |
| vex_state->guest_r1 = 0; /* Assembler temporary */ |
| vex_state->guest_r2 = 0; /* Values for function returns ... */ |
| vex_state->guest_r3 = 0; |
| vex_state->guest_r4 = 0; /* Function arguments */ |
| vex_state->guest_r5 = 0; |
| vex_state->guest_r6 = 0; |
| vex_state->guest_r7 = 0; |
| vex_state->guest_r8 = 0; |
| vex_state->guest_r9 = 0; |
| vex_state->guest_r10 = 0; |
| vex_state->guest_r11 = 0; |
| vex_state->guest_r12 = 0; /* Temporaries */ |
| vex_state->guest_r13 = 0; |
| vex_state->guest_r14 = 0; |
| vex_state->guest_r15 = 0; |
| vex_state->guest_r16 = 0; /* Saved temporaries */ |
| vex_state->guest_r17 = 0; |
| vex_state->guest_r18 = 0; |
| vex_state->guest_r19 = 0; |
| vex_state->guest_r20 = 0; |
| vex_state->guest_r21 = 0; |
| vex_state->guest_r22 = 0; |
| vex_state->guest_r23 = 0; |
| vex_state->guest_r24 = 0; /* Temporaries */ |
| vex_state->guest_r25 = 0; |
| vex_state->guest_r26 = 0; /* Reserved for OS kernel */ |
| vex_state->guest_r27 = 0; |
| vex_state->guest_r28 = 0; /* Global pointer */ |
| vex_state->guest_r29 = 0; /* Stack pointer */ |
| vex_state->guest_r30 = 0; /* Frame pointer */ |
| vex_state->guest_r31 = 0; /* Return address */ |
| vex_state->guest_PC = 0; /* Program counter */ |
| vex_state->guest_HI = 0; /* Multiply and divide register higher result */ |
| vex_state->guest_LO = 0; /* Multiply and divide register lower result */ |
| |
| /* FPU Registers */ |
| vex_state->guest_f0 = 0x7ff800007ff80000ULL; /* Floting point registers */ |
| vex_state->guest_f1 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f2 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f3 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f4 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f5 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f6 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f7 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f8 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f9 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f10 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f11 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f12 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f13 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f14 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f15 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f16 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f17 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f18 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f19 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f20 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f21 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f22 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f23 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f24 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f25 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f26 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f27 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f28 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f29 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f30 = 0x7ff800007ff80000ULL; |
| vex_state->guest_f31 = 0x7ff800007ff80000ULL; |
| |
| vex_state->guest_FIR = 0; /* FP implementation and revision register */ |
| vex_state->guest_FCCR = 0; /* FP condition codes register */ |
| vex_state->guest_FEXR = 0; /* FP exceptions register */ |
| vex_state->guest_FENR = 0; /* FP enables register */ |
| vex_state->guest_FCSR = 0; /* FP control/status register */ |
| |
| vex_state->guest_ULR = 0; |
| |
| /* Various pseudo-regs mandated by Vex or Valgrind. */ |
| /* Emulation notes */ |
| vex_state->guest_EMNOTE = 0; |
| |
| /* For clflush: record start and length of area to invalidate */ |
| vex_state->guest_CMSTART = 0; |
| vex_state->guest_CMLEN = 0; |
| vex_state->host_EvC_COUNTER = 0; |
| vex_state->host_EvC_FAILADDR = 0; |
| |
| /* Used to record the unredirected guest address at the start of |
| a translation whose start has been redirected. By reading |
| this pseudo-register shortly afterwards, the translation can |
| find out what the corresponding no-redirection address was. |
| Note, this is only set for wrap-style redirects, not for |
| replace-style ones. */ |
| vex_state->guest_NRADDR = 0; |
| |
| vex_state->guest_COND = 0; |
| |
| vex_state->guest_CP0_status = MIPS_CP0_STATUS_FR; |
| } |
| |
| /*-----------------------------------------------------------*/ |
| /*--- Describing the mips guest state, for the benefit ---*/ |
| /*--- of iropt and instrumenters. ---*/ |
| /*-----------------------------------------------------------*/ |
| |
| /* Figure out if any part of the guest state contained in minoff |
| .. maxoff requires precise memory exceptions. If in doubt return |
| True (but this generates significantly slower code). |
| |
| We enforce precise exns for guest SP, PC. |
| |
| Only SP is needed in mode VexRegUpdSpAtMemAccess. |
| */ |
| Bool guest_mips32_state_requires_precise_mem_exns ( |
| Int minoff, Int maxoff, VexRegisterUpdates pxControl |
| ) |
| { |
| Int sp_min = offsetof(VexGuestMIPS32State, guest_r29); |
| Int sp_max = sp_min + 4 - 1; |
| Int pc_min = offsetof(VexGuestMIPS32State, guest_PC); |
| Int pc_max = pc_min + 4 - 1; |
| |
| if (maxoff < sp_min || minoff > sp_max) { |
| /* no overlap with sp */ |
| if (pxControl == VexRegUpdSpAtMemAccess) |
| return False; /* We only need to check stack pointer. */ |
| } else { |
| return True; |
| } |
| |
| if (maxoff < pc_min || minoff > pc_max) { |
| /* no overlap with pc */ |
| } else { |
| return True; |
| } |
| |
| /* We appear to need precise updates of R11 in order to get proper |
| stacktraces from non-optimised code. */ |
| Int fp_min = offsetof(VexGuestMIPS32State, guest_r30); |
| Int fp_max = fp_min + 4 - 1; |
| |
| if (maxoff < fp_min || minoff > fp_max) { |
| /* no overlap with fp */ |
| } else { |
| return True; |
| } |
| |
| return False; |
| } |
| |
| Bool guest_mips64_state_requires_precise_mem_exns ( |
| Int minoff, Int maxoff, VexRegisterUpdates pxControl |
| ) |
| { |
| Int sp_min = offsetof(VexGuestMIPS64State, guest_r29); |
| Int sp_max = sp_min + 8 - 1; |
| Int pc_min = offsetof(VexGuestMIPS64State, guest_PC); |
| Int pc_max = pc_min + 8 - 1; |
| |
| if ( maxoff < sp_min || minoff > sp_max ) { |
| /* no overlap with sp */ |
| if (pxControl == VexRegUpdSpAtMemAccess) |
| return False; /* We only need to check stack pointer. */ |
| } else { |
| return True; |
| } |
| |
| if ( maxoff < pc_min || minoff > pc_max ) { |
| /* no overlap with pc */ |
| } else { |
| return True; |
| } |
| |
| Int fp_min = offsetof(VexGuestMIPS64State, guest_r30); |
| Int fp_max = fp_min + 8 - 1; |
| |
| if ( maxoff < fp_min || minoff > fp_max ) { |
| /* no overlap with fp */ |
| } else { |
| return True; |
| } |
| |
| return False; |
| } |
| |
| VexGuestLayout mips32Guest_layout = { |
| /* Total size of the guest state, in bytes. */ |
| .total_sizeB = sizeof(VexGuestMIPS32State), |
| /* Describe the stack pointer. */ |
| .offset_SP = offsetof(VexGuestMIPS32State, guest_r29), |
| .sizeof_SP = 4, |
| /* Describe the frame pointer. */ |
| .offset_FP = offsetof(VexGuestMIPS32State, guest_r30), |
| .sizeof_FP = 4, |
| /* Describe the instruction pointer. */ |
| .offset_IP = offsetof(VexGuestMIPS32State, guest_PC), |
| .sizeof_IP = 4, |
| /* Describe any sections to be regarded by Memcheck as |
| 'always-defined'. */ |
| .n_alwaysDefd = 8, |
| /* ? :( */ |
| .alwaysDefd = { |
| /* 0 */ ALWAYSDEFD32(guest_r0), |
| /* 1 */ ALWAYSDEFD32(guest_r1), |
| /* 2 */ ALWAYSDEFD32(guest_EMNOTE), |
| /* 3 */ ALWAYSDEFD32(guest_CMSTART), |
| /* 4 */ ALWAYSDEFD32(guest_CMLEN), |
| /* 5 */ ALWAYSDEFD32(guest_r29), |
| /* 6 */ ALWAYSDEFD32(guest_r31), |
| /* 7 */ ALWAYSDEFD32(guest_ULR) |
| } |
| }; |
| |
| VexGuestLayout mips64Guest_layout = { |
| /* Total size of the guest state, in bytes. */ |
| .total_sizeB = sizeof(VexGuestMIPS64State), |
| /* Describe the stack pointer. */ |
| .offset_SP = offsetof(VexGuestMIPS64State, guest_r29), |
| .sizeof_SP = 8, |
| /* Describe the frame pointer. */ |
| .offset_FP = offsetof(VexGuestMIPS64State, guest_r30), |
| .sizeof_FP = 8, |
| /* Describe the instruction pointer. */ |
| .offset_IP = offsetof(VexGuestMIPS64State, guest_PC), |
| .sizeof_IP = 8, |
| /* Describe any sections to be regarded by Memcheck as |
| 'always-defined'. */ |
| .n_alwaysDefd = 7, |
| /* ? :( */ |
| .alwaysDefd = { |
| /* 0 */ ALWAYSDEFD64 (guest_r0), |
| /* 1 */ ALWAYSDEFD64 (guest_EMNOTE), |
| /* 2 */ ALWAYSDEFD64 (guest_CMSTART), |
| /* 3 */ ALWAYSDEFD64 (guest_CMLEN), |
| /* 4 */ ALWAYSDEFD64 (guest_r29), |
| /* 5 */ ALWAYSDEFD64 (guest_r31), |
| /* 6 */ ALWAYSDEFD64 (guest_ULR) |
| } |
| }; |
| |
| #if defined(__mips__) && ((defined(__mips_isa_rev) && __mips_isa_rev >= 2)) |
| UInt mips32_dirtyhelper_rdhwr ( UInt rt, UInt rd ) |
| { |
| UInt x = 0; |
| switch (rd) { |
| case 1: /* x = SYNCI_StepSize() */ |
| __asm__ __volatile__("rdhwr %0, $1\n\t" : "=r" (x) ); |
| break; |
| |
| case 31: /* x = CVMX_get_cycles() */ |
| __asm__ __volatile__("rdhwr %0, $31\n\t" : "=r" (x) ); |
| break; |
| |
| default: |
| vassert(0); |
| break; |
| } |
| return x; |
| } |
| |
| ULong mips64_dirtyhelper_rdhwr ( ULong rt, ULong rd ) |
| { |
| ULong x = 0; |
| switch (rd) { |
| case 1: /* x = SYNCI_StepSize() */ |
| __asm__ __volatile__("rdhwr %0, $1\n\t" : "=r" (x) ); |
| break; |
| |
| case 31: /* x = CVMX_get_cycles() */ |
| __asm__ __volatile__("rdhwr %0, $31\n\t" : "=r" (x) ); |
| break; |
| |
| default: |
| vassert(0); |
| break; |
| } |
| return x; |
| } |
| #endif |
| |
| #define ASM_VOLATILE_UNARY32(inst) \ |
| __asm__ volatile(".set push" "\n\t" \ |
| ".set hardfloat" "\n\t" \ |
| "cfc1 $t0, $31" "\n\t" \ |
| "ctc1 %2, $31" "\n\t" \ |
| "mtc1 %1, $f20" "\n\t" \ |
| #inst" $f20, $f20" "\n\t" \ |
| "cfc1 %0, $31" "\n\t" \ |
| "ctc1 $t0, $31" "\n\t" \ |
| ".set pop" "\n\t" \ |
| : "=r" (ret) \ |
| : "r" (loFsVal), "r" (fcsr) \ |
| : "t0", "$f20" \ |
| ); |
| |
| #define ASM_VOLATILE_UNARY32_DOUBLE(inst) \ |
| __asm__ volatile(".set push" "\n\t" \ |
| ".set hardfloat" "\n\t" \ |
| "cfc1 $t0, $31" "\n\t" \ |
| "ctc1 %2, $31" "\n\t" \ |
| "ldc1 $f20, 0(%1)" "\n\t" \ |
| #inst" $f20, $f20" "\n\t" \ |
| "cfc1 %0, $31" "\n\t" \ |
| "ctc1 $t0, $31" "\n\t" \ |
| ".set pop" "\n\t" \ |
| : "=r" (ret) \ |
| : "r" (&fsVal), "r" (fcsr) \ |
| : "t0", "$f20", "$f21" \ |
| ); |
| |
| #define ASM_VOLATILE_UNARY64(inst) \ |
| __asm__ volatile(".set push" "\n\t" \ |
| ".set hardfloat" "\n\t" \ |
| "cfc1 $t0, $31" "\n\t" \ |
| "ctc1 %2, $31" "\n\t" \ |
| "ldc1 $f24, 0(%1)" "\n\t" \ |
| #inst" $f24, $f24" "\n\t" \ |
| "cfc1 %0, $31" "\n\t" \ |
| "ctc1 $t0, $31" "\n\t" \ |
| ".set pop" "\n\t" \ |
| : "=r" (ret) \ |
| : "r" (&(addr[fs])), "r" (fcsr) \ |
| : "t0", "$f24" \ |
| ); |
| |
| #define ASM_VOLATILE_BINARY32(inst) \ |
| __asm__ volatile(".set push" "\n\t" \ |
| ".set hardfloat" "\n\t" \ |
| "cfc1 $t0, $31" "\n\t" \ |
| "ctc1 %3, $31" "\n\t" \ |
| "mtc1 %1, $f20" "\n\t" \ |
| "mtc1 %2, $f22" "\n\t" \ |
| #inst" $f20, $f20, $f22" "\n\t" \ |
| "cfc1 %0, $31" "\n\t" \ |
| "ctc1 $t0, $31" "\n\t" \ |
| ".set pop" "\n\t" \ |
| : "=r" (ret) \ |
| : "r" (loFsVal), "r" (loFtVal), "r" (fcsr) \ |
| : "t0", "$f20", "$f22" \ |
| ); |
| |
| #define ASM_VOLATILE_BINARY32_DOUBLE(inst) \ |
| __asm__ volatile(".set push" "\n\t" \ |
| ".set hardfloat" "\n\t" \ |
| "cfc1 $t0, $31" "\n\t" \ |
| "ctc1 %3, $31" "\n\t" \ |
| "ldc1 $f20, 0(%1)" "\n\t" \ |
| "ldc1 $f22, 0(%2)" "\n\t" \ |
| #inst" $f20, $f20, $f22" "\n\t" \ |
| "cfc1 %0, $31" "\n\t" \ |
| "ctc1 $t0, $31" "\n\t" \ |
| ".set pop" "\n\t" \ |
| : "=r" (ret) \ |
| : "r" (&fsVal), "r" (&ftVal), "r" (fcsr) \ |
| : "t0", "$f20", "$f21", "$f22", "$f23" \ |
| ); |
| |
| #define ASM_VOLATILE_BINARY64(inst) \ |
| __asm__ volatile(".set push" "\n\t" \ |
| ".set hardfloat" "\n\t" \ |
| "cfc1 $t0, $31" "\n\t" \ |
| "ctc1 %3, $31" "\n\t" \ |
| "ldc1 $f24, 0(%1)" "\n\t" \ |
| "ldc1 $f26, 0(%2)" "\n\t" \ |
| #inst" $f24, $f24, $f26" "\n\t" \ |
| "cfc1 %0, $31" "\n\t" \ |
| "ctc1 $t0, $31" "\n\t" \ |
| ".set pop" "\n\t" \ |
| : "=r" (ret) \ |
| : "r" (&(addr[fs])), "r" (&(addr[ft])), "r" (fcsr) \ |
| : "t0", "$f24", "$f26" \ |
| ); |
| |
| /* TODO: Add cases for all fpu instructions because all fpu instructions are |
| change the value of FCSR register. */ |
| extern UInt mips_dirtyhelper_calculate_FCSR_fp32 ( void* gs, UInt fs, UInt ft, |
| flt_op inst ) |
| { |
| UInt ret = 0; |
| #if defined(__mips__) |
| VexGuestMIPS32State* guest_state = (VexGuestMIPS32State*)gs; |
| UInt loFsVal, hiFsVal, loFtVal, hiFtVal; |
| #if defined (_MIPSEL) |
| ULong *addr = (ULong *)&guest_state->guest_f0; |
| loFsVal = (UInt)addr[fs]; |
| hiFsVal = (UInt)addr[fs+1]; |
| loFtVal = (UInt)addr[ft]; |
| hiFtVal = (UInt)addr[ft+1]; |
| #elif defined (_MIPSEB) |
| UInt *addr = (UInt *)&guest_state->guest_f0; |
| loFsVal = (UInt)addr[fs*2]; |
| hiFsVal = (UInt)addr[fs*2+2]; |
| loFtVal = (UInt)addr[ft*2]; |
| hiFtVal = (UInt)addr[ft*2+2]; |
| #endif |
| ULong fsVal = ((ULong) hiFsVal) << 32 | loFsVal; |
| ULong ftVal = ((ULong) hiFtVal) << 32 | loFtVal; |
| UInt fcsr = guest_state->guest_FCSR; |
| switch (inst) { |
| case ROUNDWD: |
| ASM_VOLATILE_UNARY32_DOUBLE(round.w.d) |
| break; |
| case FLOORWS: |
| ASM_VOLATILE_UNARY32(floor.w.s) |
| break; |
| case FLOORWD: |
| ASM_VOLATILE_UNARY32_DOUBLE(floor.w.d) |
| break; |
| case TRUNCWS: |
| ASM_VOLATILE_UNARY32(trunc.w.s) |
| break; |
| case TRUNCWD: |
| ASM_VOLATILE_UNARY32_DOUBLE(trunc.w.d) |
| break; |
| case CEILWS: |
| ASM_VOLATILE_UNARY32(ceil.w.s) |
| break; |
| case CEILWD: |
| ASM_VOLATILE_UNARY32_DOUBLE(ceil.w.d) |
| break; |
| case CVTDS: |
| ASM_VOLATILE_UNARY32(cvt.d.s) |
| break; |
| case CVTDW: |
| ASM_VOLATILE_UNARY32(cvt.d.w) |
| break; |
| case CVTSW: |
| ASM_VOLATILE_UNARY32(cvt.s.w) |
| break; |
| case CVTSD: |
| ASM_VOLATILE_UNARY32_DOUBLE(cvt.s.d) |
| break; |
| case CVTWS: |
| ASM_VOLATILE_UNARY32(cvt.w.s) |
| break; |
| case CVTWD: |
| ASM_VOLATILE_UNARY32_DOUBLE(cvt.w.d) |
| break; |
| case ROUNDWS: |
| ASM_VOLATILE_UNARY32(round.w.s) |
| break; |
| #if ((__mips == 32) && defined(__mips_isa_rev) && (__mips_isa_rev >= 2)) \ |
| || (__mips == 64) |
| case CEILLS: |
| ASM_VOLATILE_UNARY32(ceil.l.s) |
| break; |
| case CEILLD: |
| ASM_VOLATILE_UNARY32_DOUBLE(ceil.l.d) |
| break; |
| case CVTDL: |
| ASM_VOLATILE_UNARY32_DOUBLE(cvt.d.l) |
| break; |
| case CVTLS: |
| ASM_VOLATILE_UNARY32(cvt.l.s) |
| break; |
| case CVTLD: |
| ASM_VOLATILE_UNARY32_DOUBLE(cvt.l.d) |
| break; |
| case CVTSL: |
| ASM_VOLATILE_UNARY32_DOUBLE(cvt.s.l) |
| break; |
| case FLOORLS: |
| ASM_VOLATILE_UNARY32(floor.l.s) |
| break; |
| case FLOORLD: |
| ASM_VOLATILE_UNARY32_DOUBLE(floor.l.d) |
| break; |
| case ROUNDLS: |
| ASM_VOLATILE_UNARY32(round.l.s) |
| break; |
| case ROUNDLD: |
| ASM_VOLATILE_UNARY32_DOUBLE(round.l.d) |
| break; |
| case TRUNCLS: |
| ASM_VOLATILE_UNARY32(trunc.l.s) |
| break; |
| case TRUNCLD: |
| ASM_VOLATILE_UNARY32_DOUBLE(trunc.l.d) |
| break; |
| #endif |
| case ADDS: |
| ASM_VOLATILE_BINARY32(add.s) |
| break; |
| case ADDD: |
| ASM_VOLATILE_BINARY32_DOUBLE(add.d) |
| break; |
| case SUBS: |
| ASM_VOLATILE_BINARY32(sub.s) |
| break; |
| case SUBD: |
| ASM_VOLATILE_BINARY32_DOUBLE(sub.d) |
| break; |
| case DIVS: |
| ASM_VOLATILE_BINARY32(div.s) |
| break; |
| default: |
| vassert(0); |
| break; |
| } |
| #endif |
| return ret; |
| } |
| |
| /* TODO: Add cases for all fpu instructions because all fpu instructions are |
| change the value of FCSR register. */ |
| extern UInt mips_dirtyhelper_calculate_FCSR_fp64 ( void* gs, UInt fs, UInt ft, |
| flt_op inst ) |
| { |
| UInt ret = 0; |
| #if defined(__mips__) |
| #if defined(VGA_mips32) |
| VexGuestMIPS32State* guest_state = (VexGuestMIPS32State*)gs; |
| #else |
| VexGuestMIPS64State* guest_state = (VexGuestMIPS64State*)gs; |
| #endif |
| ULong *addr = (ULong *)&guest_state->guest_f0; |
| UInt fcsr = guest_state->guest_FCSR; |
| switch (inst) { |
| case ROUNDWD: |
| ASM_VOLATILE_UNARY64(round.w.d) |
| break; |
| case FLOORWS: |
| ASM_VOLATILE_UNARY64(floor.w.s) |
| break; |
| case FLOORWD: |
| ASM_VOLATILE_UNARY64(floor.w.d) |
| break; |
| case TRUNCWS: |
| ASM_VOLATILE_UNARY64(trunc.w.s) |
| break; |
| case TRUNCWD: |
| ASM_VOLATILE_UNARY64(trunc.w.d) |
| break; |
| case CEILWS: |
| ASM_VOLATILE_UNARY64(ceil.w.s) |
| break; |
| case CEILWD: |
| ASM_VOLATILE_UNARY64(ceil.w.d) |
| break; |
| case CVTDS: |
| ASM_VOLATILE_UNARY64(cvt.d.s) |
| break; |
| case CVTDW: |
| ASM_VOLATILE_UNARY64(cvt.d.w) |
| break; |
| case CVTSW: |
| ASM_VOLATILE_UNARY64(cvt.s.w) |
| break; |
| case CVTSD: |
| ASM_VOLATILE_UNARY64(cvt.s.d) |
| break; |
| case CVTWS: |
| ASM_VOLATILE_UNARY64(cvt.w.s) |
| break; |
| case CVTWD: |
| ASM_VOLATILE_UNARY64(cvt.w.d) |
| break; |
| case ROUNDWS: |
| ASM_VOLATILE_UNARY64(round.w.s) |
| break; |
| #if ((__mips == 32) && defined(__mips_isa_rev) && (__mips_isa_rev >= 2)) \ |
| || (__mips == 64) |
| case CEILLS: |
| ASM_VOLATILE_UNARY64(ceil.l.s) |
| break; |
| case CEILLD: |
| ASM_VOLATILE_UNARY64(ceil.l.d) |
| break; |
| case CVTDL: |
| ASM_VOLATILE_UNARY64(cvt.d.l) |
| break; |
| case CVTLS: |
| ASM_VOLATILE_UNARY64(cvt.l.s) |
| break; |
| case CVTLD: |
| ASM_VOLATILE_UNARY64(cvt.l.d) |
| break; |
| case CVTSL: |
| ASM_VOLATILE_UNARY64(cvt.s.l) |
| break; |
| case FLOORLS: |
| ASM_VOLATILE_UNARY64(floor.l.s) |
| break; |
| case FLOORLD: |
| ASM_VOLATILE_UNARY64(floor.l.d) |
| break; |
| case ROUNDLS: |
| ASM_VOLATILE_UNARY64(round.l.s) |
| break; |
| case ROUNDLD: |
| ASM_VOLATILE_UNARY64(round.l.d) |
| break; |
| case TRUNCLS: |
| ASM_VOLATILE_UNARY64(trunc.l.s) |
| break; |
| case TRUNCLD: |
| ASM_VOLATILE_UNARY64(trunc.l.d) |
| break; |
| #endif |
| case ADDS: |
| ASM_VOLATILE_BINARY64(add.s) |
| break; |
| case ADDD: |
| ASM_VOLATILE_BINARY64(add.d) |
| break; |
| case SUBS: |
| ASM_VOLATILE_BINARY64(sub.s) |
| break; |
| case SUBD: |
| ASM_VOLATILE_BINARY64(sub.d) |
| break; |
| case DIVS: |
| ASM_VOLATILE_BINARY64(div.s) |
| break; |
| default: |
| vassert(0); |
| break; |
| } |
| #endif |
| return ret; |
| } |
| |
| /*---------------------------------------------------------------*/ |
| /*--- end guest_mips_helpers.c ---*/ |
| /*---------------------------------------------------------------*/ |