CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/MIPS/x86/CompLoadStore.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "ppsspp_config.h"18#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)1920#include "Core/MemMap.h"21#include "Core/MIPS/MIPSAnalyst.h"22#include "Core/Config.h"23#include "Core/MIPS/MIPSCodeUtils.h"24#include "Core/MIPS/x86/Jit.h"25#include "Core/MIPS/x86/RegCache.h"262728#define _RS MIPS_GET_RS(op)29#define _RT MIPS_GET_RT(op)30#define _RD MIPS_GET_RD(op)31#define _FS MIPS_GET_FS(op)32#define _FT MIPS_GET_FT(op)33#define _FD MIPS_GET_FD(op)34#define _SA MIPS_GET_SA(op)35#define _POS ((op>> 6) & 0x1F)36#define _SIZE ((op>>11) & 0x1F)37#define _IMM16 (signed short)(op & 0xFFFF)38#define _IMM26 (op & 0x03FFFFFF)3940// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.41// Currently known non working ones should have DISABLE.4243// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }44#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }45#define DISABLE { Comp_Generic(op); return; }46#define INVALIDOP { Comp_Generic(op); return; }4748namespace MIPSComp {49using namespace Gen;5051void Jit::CompITypeMemRead(MIPSOpcode op, u32 bits, void (XEmitter::*mov)(int, int, X64Reg, OpArg), const void *safeFunc)52{53CONDITIONAL_DISABLE(LSU);54int offset = _IMM16;55MIPSGPReg rt = _RT;56MIPSGPReg rs = _RS;5758gpr.Lock(rt, rs);59gpr.MapReg(rt, rt == rs, true);6061JitSafeMem safe(this, rs, offset);62OpArg src;63if (safe.PrepareRead(src, bits / 8))64(this->*mov)(32, bits, gpr.RX(rt), src);65if (safe.PrepareSlowRead(safeFunc))66(this->*mov)(32, bits, gpr.RX(rt), R(EAX));67safe.Finish();6869gpr.UnlockAll();70}7172static OpArg DowncastImm(OpArg in, int bits) {73if (!in.IsImm())74return in;75if (in.GetImmBits() > bits) {76in.SetImmBits(bits);77return in;78}79return in;80}8182void Jit::CompITypeMemWrite(MIPSOpcode op, u32 bits, const void *safeFunc, bool makeRTWritable)83{84CONDITIONAL_DISABLE(LSU);85int offset = _IMM16;86MIPSGPReg rt = _RT;87MIPSGPReg rs = _RS;8889gpr.Lock(rt, rs);9091if (rt == MIPS_REG_ZERO || gpr.R(rt).IsImm()) {92if (makeRTWritable) {93gpr.MapReg(rt, true, true);94}95// NOTICE_LOG(Log::JIT, "%d-bit Imm at %08x : %08x", bits, js.blockStart, (u32)gpr.R(rt).GetImmValue());96} else {97gpr.MapReg(rt, true, false);98}99100#if PPSSPP_ARCH(X86)101// We use EDX so we can have DL for 8-bit ops.102const bool needSwap = bits == 8 && !gpr.R(rt).IsSimpleReg(EDX) && !gpr.R(rt).IsSimpleReg(ECX);103if (needSwap)104gpr.FlushLockX(EDX);105#else106const bool needSwap = false;107#endif108109JitSafeMem safe(this, rs, offset);110OpArg dest;111if (safe.PrepareWrite(dest, bits / 8))112{113if (needSwap)114{115MOV(32, R(EDX), gpr.R(rt));116MOV(bits, dest, R(EDX));117}118else {119if (rt == MIPS_REG_ZERO) {120switch (bits) {121case 8: MOV(8, dest, Imm8(0)); break;122case 16: MOV(16, dest, Imm16(0)); break;123case 32: MOV(32, dest, Imm32(0)); break;124}125} else {126// The downcast is needed so we don't try to generate a 8-bit write with a 32-bit imm127// (that might have been generated from an li instruction) which is illegal.128MOV(bits, dest, DowncastImm(gpr.R(rt), bits));129}130}131}132if (safe.PrepareSlowWrite())133safe.DoSlowWrite(safeFunc, gpr.R(rt));134safe.Finish();135136if (needSwap)137gpr.UnlockAllX();138gpr.UnlockAll();139}140141void Jit::CompITypeMemUnpairedLR(MIPSOpcode op, bool isStore)142{143CONDITIONAL_DISABLE(LSU);144int offset = _IMM16;145MIPSGPReg rt = _RT;146MIPSGPReg rs = _RS;147148X64Reg shiftReg = ECX;149gpr.FlushLockX(ECX, EDX);150#if PPSSPP_ARCH(AMD64)151// On x64, we need ECX for CL, but it's also the first arg and gets lost. Annoying.152gpr.FlushLockX(R9);153shiftReg = R9;154#endif155156gpr.Lock(rt, rs);157gpr.MapReg(rt, true, !isStore);158159// Grab the offset from alignment for shifting (<< 3 for bytes -> bits.)160MOV(32, R(shiftReg), gpr.R(rs));161ADD(32, R(shiftReg), Imm32(offset));162AND(32, R(shiftReg), Imm32(3));163SHL(32, R(shiftReg), Imm8(3));164165{166JitSafeMem safe(this, rs, offset, ~3);167OpArg src;168if (safe.PrepareRead(src, 4))169{170if (!src.IsSimpleReg(EAX))171MOV(32, R(EAX), src);172173CompITypeMemUnpairedLRInner(op, shiftReg);174}175if (safe.PrepareSlowRead(safeMemFuncs.readU32))176CompITypeMemUnpairedLRInner(op, shiftReg);177safe.Finish();178}179180// For store ops, write EDX back to memory.181if (isStore)182{183JitSafeMem safe(this, rs, offset, ~3);184OpArg dest;185if (safe.PrepareWrite(dest, 4))186MOV(32, dest, R(EDX));187if (safe.PrepareSlowWrite())188safe.DoSlowWrite(safeMemFuncs.writeU32, R(EDX));189safe.Finish();190}191192gpr.UnlockAll();193gpr.UnlockAllX();194}195196void Jit::CompITypeMemUnpairedLRInner(MIPSOpcode op, X64Reg shiftReg)197{198CONDITIONAL_DISABLE(LSU);199int o = op>>26;200MIPSGPReg rt = _RT;201202// Make sure we have the shift for the target in ECX.203if (shiftReg != ECX)204MOV(32, R(ECX), R(shiftReg));205206// Now use that shift (left on target, right on source.)207switch (o)208{209case 34: //lwl210MOV(32, R(EDX), Imm32(0x00ffffff));211SHR(32, R(EDX), R(CL));212AND(32, gpr.R(rt), R(EDX));213break;214215case 38: //lwr216SHR(32, R(EAX), R(CL));217break;218219case 42: //swl220MOV(32, R(EDX), Imm32(0xffffff00));221SHL(32, R(EDX), R(CL));222AND(32, R(EAX), R(EDX));223break;224225case 46: //swr226MOV(32, R(EDX), gpr.R(rt));227SHL(32, R(EDX), R(CL));228// EDX is already the target value to write, but may be overwritten below. Save it.229PUSH(EDX);230break;231232default:233_dbg_assert_msg_(false, "Unsupported left/right load/store instruction.");234}235236// Flip ECX around from 3 bytes / 24 bits.237if (shiftReg == ECX)238{239MOV(32, R(EDX), Imm32(24));240SUB(32, R(EDX), R(ECX));241MOV(32, R(ECX), R(EDX));242}243else244{245MOV(32, R(ECX), Imm32(24));246SUB(32, R(ECX), R(shiftReg));247}248249// Use the flipped shift (left on source, right on target) and write target.250switch (o)251{252case 34: //lwl253SHL(32, R(EAX), R(CL));254255OR(32, gpr.R(rt), R(EAX));256break;257258case 38: //lwr259MOV(32, R(EDX), Imm32(0xffffff00));260SHL(32, R(EDX), R(CL));261AND(32, gpr.R(rt), R(EDX));262263OR(32, gpr.R(rt), R(EAX));264break;265266case 42: //swl267MOV(32, R(EDX), gpr.R(rt));268SHR(32, R(EDX), R(CL));269270OR(32, R(EDX), R(EAX));271break;272273case 46: //swr274MOV(32, R(EDX), Imm32(0x00ffffff));275SHR(32, R(EDX), R(CL));276AND(32, R(EAX), R(EDX));277278// This is the target value we saved earlier.279POP(EDX);280OR(32, R(EDX), R(EAX));281break;282283default:284_dbg_assert_msg_(false, "Unsupported left/right load/store instruction.");285}286}287288void Jit::Comp_ITypeMem(MIPSOpcode op)289{290CONDITIONAL_DISABLE(LSU);291int offset = _IMM16;292MIPSGPReg rs = _RS;293MIPSGPReg rt = _RT;294int o = op>>26;295if (((op >> 29) & 1) == 0 && rt == MIPS_REG_ZERO) {296// Don't load anything into $zr297return;298}299300CheckMemoryBreakpoint(0, rs, offset);301302switch (o)303{304case 37: //R(rt) = ReadMem16(addr); break; //lhu305CompITypeMemRead(op, 16, &XEmitter::MOVZX, safeMemFuncs.readU16);306break;307308case 36: //R(rt) = ReadMem8 (addr); break; //lbu309CompITypeMemRead(op, 8, &XEmitter::MOVZX, safeMemFuncs.readU8);310break;311312case 35: //R(rt) = ReadMem32(addr); break; //lw313CompITypeMemRead(op, 32, &XEmitter::MOVZX, safeMemFuncs.readU32);314break;315316case 32: //R(rt) = SignExtend8ToU32 (ReadMem8 (addr)); break; //lb317CompITypeMemRead(op, 8, &XEmitter::MOVSX, safeMemFuncs.readU8);318break;319320case 33: //R(rt) = SignExtend16ToU32(ReadMem16(addr)); break; //lh321CompITypeMemRead(op, 16, &XEmitter::MOVSX, safeMemFuncs.readU16);322break;323324case 40: //WriteMem8 (addr, R(rt)); break; //sb325CompITypeMemWrite(op, 8, safeMemFuncs.writeU8);326break;327328case 41: //WriteMem16(addr, R(rt)); break; //sh329CompITypeMemWrite(op, 16, safeMemFuncs.writeU16);330break;331332case 43: //WriteMem32(addr, R(rt)); break; //sw333CompITypeMemWrite(op, 32, safeMemFuncs.writeU32);334break;335336case 34: //lwl337{338MIPSOpcode nextOp = GetOffsetInstruction(1);339// Looking for lwr rd, offset-3(rs) which makes a pair.340u32 desiredOp = ((op & 0xFFFF0000) + (4 << 26)) + (offset - 3);341if (!js.inDelaySlot && nextOp == desiredOp && !jo.Disabled(JitDisable::LSU_UNALIGNED))342{343CheckMemoryBreakpoint(1, rs, offset - 3);344EatInstruction(nextOp);345// nextOp has the correct address.346CompITypeMemRead(nextOp, 32, &XEmitter::MOVZX, safeMemFuncs.readU32);347}348else349CompITypeMemUnpairedLR(op, false);350}351break;352353case 38: //lwr354{355MIPSOpcode nextOp = GetOffsetInstruction(1);356// Looking for lwl rd, offset+3(rs) which makes a pair.357u32 desiredOp = ((op & 0xFFFF0000) - (4 << 26)) + (offset + 3);358if (!js.inDelaySlot && nextOp == desiredOp && !jo.Disabled(JitDisable::LSU_UNALIGNED))359{360CheckMemoryBreakpoint(1, rs, offset + 3);361EatInstruction(nextOp);362// op has the correct address.363CompITypeMemRead(op, 32, &XEmitter::MOVZX, safeMemFuncs.readU32);364}365else366CompITypeMemUnpairedLR(op, false);367}368break;369370case 42: //swl371{372MIPSOpcode nextOp = GetOffsetInstruction(1);373// Looking for swr rd, offset-3(rs) which makes a pair.374u32 desiredOp = ((op & 0xFFFF0000) + (4 << 26)) + (offset - 3);375if (!js.inDelaySlot && nextOp == desiredOp && !jo.Disabled(JitDisable::LSU_UNALIGNED))376{377CheckMemoryBreakpoint(1, rs, offset - 3);378EatInstruction(nextOp);379// nextOp has the correct address.380CompITypeMemWrite(nextOp, 32, safeMemFuncs.writeU32);381}382else383CompITypeMemUnpairedLR(op, true);384}385break;386387case 46: //swr388{389MIPSOpcode nextOp = GetOffsetInstruction(1);390// Looking for swl rd, offset+3(rs) which makes a pair.391u32 desiredOp = ((op & 0xFFFF0000) - (4 << 26)) + (offset + 3);392if (!js.inDelaySlot && nextOp == desiredOp && !jo.Disabled(JitDisable::LSU_UNALIGNED))393{394CheckMemoryBreakpoint(1, rs, offset + 3);395EatInstruction(nextOp);396// op has the correct address.397CompITypeMemWrite(op, 32, safeMemFuncs.writeU32);398}399else400CompITypeMemUnpairedLR(op, true);401}402break;403404default:405Comp_Generic(op);406return;407}408409}410411void Jit::Comp_StoreSync(MIPSOpcode op) {412CONDITIONAL_DISABLE(LSU);413414int offset = _IMM16;415MIPSGPReg rt = _RT;416MIPSGPReg rs = _RS;417// Note: still does something even if loading to zero.418419CheckMemoryBreakpoint(0, rs, offset);420421FixupBranch skipStore;422FixupBranch finish;423switch (op >> 26) {424case 48: // ll425CompITypeMemRead(op, 32, &XEmitter::MOVZX, safeMemFuncs.readU32);426MOV(8, MDisp(X64JitConstants::CTXREG, -128 + offsetof(MIPSState, llBit)), Imm8(1));427break;428429case 56: // sc430// Map before the jump in case any regs spill. Unlock happens inside CompITypeMemWrite().431// This is not a very common op, but it's in jit so memory breakpoints can trip.432gpr.Lock(rt, rs);433gpr.MapReg(rt, true, true);434gpr.MapReg(rs, true, false);435436CMP(8, MDisp(X64JitConstants::CTXREG, -128 + offsetof(MIPSState, llBit)), Imm8(1));437skipStore = J_CC(CC_NE);438439CompITypeMemWrite(op, 32, safeMemFuncs.writeU32, true);440MOV(32, gpr.R(rt), Imm32(1));441finish = J();442443SetJumpTarget(skipStore);444MOV(32, gpr.R(rt), Imm32(0));445SetJumpTarget(finish);446break;447448default:449INVALIDOP;450}451}452453void Jit::Comp_Cache(MIPSOpcode op) {454CONDITIONAL_DISABLE(LSU);455456int func = (op >> 16) & 0x1F;457458// See Int_Cache for the definitions.459switch (func) {460case 24: break;461case 25: break;462case 27: break;463case 30: break;464default:465// Fall back to the interpreter.466DISABLE;467}468}469}470471#endif // PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)472473474