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/ARM64/Arm64IRCompLoadStore.cpp
Views: 1401
// Copyright (c) 2023- 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// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.19#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))2021#include "Core/MemMap.h"22#include "Core/MIPS/ARM64/Arm64IRJit.h"23#include "Core/MIPS/ARM64/Arm64IRRegCache.h"2425// This file contains compilation for load/store instructions.26//27// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.28// Currently known non working ones should have DISABLE. No flags because that's in IR already.2930// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }31#define CONDITIONAL_DISABLE {}32#define DISABLE { CompIR_Generic(inst); return; }33#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }3435namespace MIPSComp {3637using namespace Arm64Gen;38using namespace Arm64IRJitConstants;3940static int IROpToByteWidth(IROp op) {41switch (op) {42case IROp::Load8:43case IROp::Load8Ext:44case IROp::Store8:45return 1;4647case IROp::Load16:48case IROp::Load16Ext:49case IROp::Store16:50return 2;5152case IROp::Load32:53case IROp::Load32Linked:54case IROp::Load32Left:55case IROp::Load32Right:56case IROp::LoadFloat:57case IROp::Store32:58case IROp::Store32Conditional:59case IROp::Store32Left:60case IROp::Store32Right:61case IROp::StoreFloat:62return 4;6364case IROp::LoadVec4:65case IROp::StoreVec4:66return 16;6768default:69_assert_msg_(false, "Unexpected op: %s", GetIRMeta(op) ? GetIRMeta(op)->name : "?");70return -1;71}72}7374Arm64JitBackend::LoadStoreArg Arm64JitBackend::PrepareSrc1Address(IRInst inst) {75const IRMeta *m = GetIRMeta(inst.op);7677bool src1IsPointer = regs_.IsGPRMappedAsPointer(inst.src1);78bool readsFromSrc1 = inst.src1 == inst.src3 && (m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0;79// If it's about to be clobbered, don't waste time pointerifying. Use displacement.80bool clobbersSrc1 = !readsFromSrc1 && regs_.IsGPRClobbered(inst.src1);8182int64_t imm = (int32_t)inst.constant;83// It can't be this negative, must be a constant address with the top bit set.84if ((imm & 0xC0000000) == 0x80000000) {85imm = (uint64_t)(uint32_t)inst.constant;86}8788LoadStoreArg addrArg;89if (inst.src1 == MIPS_REG_ZERO) {90// The constant gets applied later.91addrArg.base = MEMBASEREG;92#ifdef MASKED_PSP_MEMORY93imm &= Memory::MEMVIEW32_MASK;94#endif95} else if (!jo.enablePointerify && readsFromSrc1) {96#ifndef MASKED_PSP_MEMORY97if (imm == 0) {98addrArg.base = MEMBASEREG;99addrArg.regOffset = regs_.MapGPR(inst.src1);100addrArg.useRegisterOffset = true;101addrArg.signExtendRegOffset = false;102}103#endif104105// Since we can't modify src1, let's just use a temp reg while copying.106if (!addrArg.useRegisterOffset) {107ADDI2R(SCRATCH1, regs_.MapGPR(inst.src1), imm, SCRATCH2);108#ifdef MASKED_PSP_MEMORY109ANDI2R(SCRATCH1, SCRATCH1, Memory::MEMVIEW32_MASK, SCRATCH2);110#endif111112addrArg.base = MEMBASEREG;113addrArg.regOffset = SCRATCH1;114addrArg.useRegisterOffset = true;115addrArg.signExtendRegOffset = false;116}117} else if ((jo.cachePointers && !clobbersSrc1) || src1IsPointer) {118// The offset gets set later.119addrArg.base = regs_.MapGPRAsPointer(inst.src1);120} else {121ADDI2R(SCRATCH1, regs_.MapGPR(inst.src1), imm, SCRATCH2);122#ifdef MASKED_PSP_MEMORY123ANDI2R(SCRATCH1, SCRATCH1, Memory::MEMVIEW32_MASK, SCRATCH2);124#endif125126addrArg.base = MEMBASEREG;127addrArg.regOffset = SCRATCH1;128addrArg.useRegisterOffset = true;129addrArg.signExtendRegOffset = false;130}131132// That's src1 taken care of, and possibly imm.133// If useRegisterOffset is false, imm still needs to be accounted for.134if (!addrArg.useRegisterOffset && imm != 0) {135#ifdef MASKED_PSP_MEMORY136// In case we have an address + offset reg.137if (imm > 0)138imm &= Memory::MEMVIEW32_MASK;139#endif140141int scale = IROpToByteWidth(inst.op);142if (imm > 0 && (imm & (scale - 1)) == 0 && imm <= 0xFFF * scale) {143// Okay great, use the LDR/STR form.144addrArg.immOffset = (int)imm;145addrArg.useUnscaled = false;146} else if (imm >= -256 && imm < 256) {147// An unscaled offset (LDUR/STUR) should work fine for this range.148addrArg.immOffset = (int)imm;149addrArg.useUnscaled = true;150} else {151// No luck, we'll need to load into a register.152MOVI2R(SCRATCH1, imm);153addrArg.regOffset = SCRATCH1;154addrArg.useRegisterOffset = true;155addrArg.signExtendRegOffset = true;156}157}158159return addrArg;160}161162void Arm64JitBackend::CompIR_CondStore(IRInst inst) {163CONDITIONAL_DISABLE;164if (inst.op != IROp::Store32Conditional)165INVALIDOP;166167regs_.SpillLockGPR(IRREG_LLBIT, inst.src3, inst.src1);168LoadStoreArg addrArg = PrepareSrc1Address(inst);169ARM64Reg valueReg = regs_.MapGPR(inst.src3, MIPSMap::INIT);170171regs_.MapGPR(IRREG_LLBIT, MIPSMap::INIT);172173// TODO: Safe memory? Or enough to have crash handler + validate?174175FixupBranch condFailed = CBZ(regs_.R(IRREG_LLBIT));176177if (addrArg.useRegisterOffset) {178STR(valueReg, addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));179} else if (addrArg.useUnscaled) {180STUR(valueReg, addrArg.base, addrArg.immOffset);181} else {182STR(INDEX_UNSIGNED, valueReg, addrArg.base, addrArg.immOffset);183}184185if (inst.dest != MIPS_REG_ZERO) {186MOVI2R(regs_.R(inst.dest), 1);187FixupBranch finish = B();188189SetJumpTarget(condFailed);190MOVI2R(regs_.R(inst.dest), 0);191SetJumpTarget(finish);192} else {193SetJumpTarget(condFailed);194}195}196197void Arm64JitBackend::CompIR_FLoad(IRInst inst) {198CONDITIONAL_DISABLE;199200LoadStoreArg addrArg = PrepareSrc1Address(inst);201202switch (inst.op) {203case IROp::LoadFloat:204regs_.MapFPR(inst.dest, MIPSMap::NOINIT);205if (addrArg.useRegisterOffset) {206fp_.LDR(32, regs_.F(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));207} else if (addrArg.useUnscaled) {208fp_.LDUR(32, regs_.F(inst.dest), addrArg.base, addrArg.immOffset);209} else {210fp_.LDR(32, INDEX_UNSIGNED, regs_.F(inst.dest), addrArg.base, addrArg.immOffset);211}212break;213214default:215INVALIDOP;216break;217}218}219220void Arm64JitBackend::CompIR_FStore(IRInst inst) {221CONDITIONAL_DISABLE;222223LoadStoreArg addrArg = PrepareSrc1Address(inst);224225switch (inst.op) {226case IROp::StoreFloat:227regs_.MapFPR(inst.src3);228if (addrArg.useRegisterOffset) {229fp_.STR(32, regs_.F(inst.src3), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));230} else if (addrArg.useUnscaled) {231fp_.STUR(32, regs_.F(inst.src3), addrArg.base, addrArg.immOffset);232} else {233fp_.STR(32, INDEX_UNSIGNED, regs_.F(inst.src3), addrArg.base, addrArg.immOffset);234}235break;236237default:238INVALIDOP;239break;240}241}242243void Arm64JitBackend::CompIR_Load(IRInst inst) {244CONDITIONAL_DISABLE;245246regs_.SpillLockGPR(inst.dest, inst.src1);247LoadStoreArg addrArg = PrepareSrc1Address(inst);248// With NOINIT, MapReg won't subtract MEMBASEREG even if dest == src1.249regs_.MapGPR(inst.dest, MIPSMap::NOINIT);250251// TODO: Safe memory? Or enough to have crash handler + validate?252253switch (inst.op) {254case IROp::Load8:255if (addrArg.useRegisterOffset) {256LDRB(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));257} else if (addrArg.useUnscaled) {258LDURB(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);259} else {260LDRB(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);261}262break;263264case IROp::Load8Ext:265if (addrArg.useRegisterOffset) {266LDRSB(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));267} else if (addrArg.useUnscaled) {268LDURSB(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);269} else {270LDRSB(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);271}272break;273274case IROp::Load16:275if (addrArg.useRegisterOffset) {276LDRH(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));277} else if (addrArg.useUnscaled) {278LDURH(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);279} else {280LDRH(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);281}282break;283284case IROp::Load16Ext:285if (addrArg.useRegisterOffset) {286LDRSH(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));287} else if (addrArg.useUnscaled) {288LDURSH(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);289} else {290LDRSH(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);291}292break;293294case IROp::Load32:295if (addrArg.useRegisterOffset) {296LDR(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));297} else if (addrArg.useUnscaled) {298LDUR(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);299} else {300LDR(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);301}302break;303304case IROp::Load32Linked:305if (inst.dest != MIPS_REG_ZERO) {306if (addrArg.useRegisterOffset) {307LDR(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));308} else if (addrArg.useUnscaled) {309LDUR(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);310} else {311LDR(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);312}313}314regs_.SetGPRImm(IRREG_LLBIT, 1);315break;316317default:318INVALIDOP;319break;320}321}322323void Arm64JitBackend::CompIR_LoadShift(IRInst inst) {324CONDITIONAL_DISABLE;325326switch (inst.op) {327case IROp::Load32Left:328case IROp::Load32Right:329// Should not happen if the pass to split is active.330DISABLE;331break;332333default:334INVALIDOP;335break;336}337}338339void Arm64JitBackend::CompIR_Store(IRInst inst) {340CONDITIONAL_DISABLE;341342regs_.SpillLockGPR(inst.src3, inst.src1);343LoadStoreArg addrArg = PrepareSrc1Address(inst);344345ARM64Reg valueReg = regs_.TryMapTempImm(inst.src3);346if (valueReg == INVALID_REG)347valueReg = regs_.MapGPR(inst.src3);348349// TODO: Safe memory? Or enough to have crash handler + validate?350351switch (inst.op) {352case IROp::Store8:353if (addrArg.useRegisterOffset) {354STRB(valueReg, addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));355} else if (addrArg.useUnscaled) {356STURB(valueReg, addrArg.base, addrArg.immOffset);357} else {358STRB(INDEX_UNSIGNED, valueReg, addrArg.base, addrArg.immOffset);359}360break;361362case IROp::Store16:363if (addrArg.useRegisterOffset) {364STRH(valueReg, addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));365} else if (addrArg.useUnscaled) {366STURH(valueReg, addrArg.base, addrArg.immOffset);367} else {368STRH(INDEX_UNSIGNED, valueReg, addrArg.base, addrArg.immOffset);369}370break;371372case IROp::Store32:373if (addrArg.useRegisterOffset) {374STR(valueReg, addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));375} else if (addrArg.useUnscaled) {376STUR(valueReg, addrArg.base, addrArg.immOffset);377} else {378STR(INDEX_UNSIGNED, valueReg, addrArg.base, addrArg.immOffset);379}380break;381382default:383INVALIDOP;384break;385}386}387388void Arm64JitBackend::CompIR_StoreShift(IRInst inst) {389CONDITIONAL_DISABLE;390391switch (inst.op) {392case IROp::Store32Left:393case IROp::Store32Right:394// Should not happen if the pass to split is active.395DISABLE;396break;397398default:399INVALIDOP;400break;401}402}403404void Arm64JitBackend::CompIR_VecLoad(IRInst inst) {405CONDITIONAL_DISABLE;406407LoadStoreArg addrArg = PrepareSrc1Address(inst);408409switch (inst.op) {410case IROp::LoadVec4:411regs_.MapVec4(inst.dest, MIPSMap::NOINIT);412if (addrArg.useRegisterOffset) {413fp_.LDR(128, regs_.FQ(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));414} else if (addrArg.useUnscaled) {415fp_.LDUR(128, regs_.FQ(inst.dest), addrArg.base, addrArg.immOffset);416} else {417fp_.LDR(128, INDEX_UNSIGNED, regs_.FQ(inst.dest), addrArg.base, addrArg.immOffset);418}419break;420421default:422INVALIDOP;423break;424}425}426427void Arm64JitBackend::CompIR_VecStore(IRInst inst) {428CONDITIONAL_DISABLE;429430LoadStoreArg addrArg = PrepareSrc1Address(inst);431432switch (inst.op) {433case IROp::StoreVec4:434regs_.MapVec4(inst.src3);435if (addrArg.useRegisterOffset) {436fp_.STR(128, regs_.FQ(inst.src3), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));437} else if (addrArg.useUnscaled) {438fp_.STUR(128, regs_.FQ(inst.src3), addrArg.base, addrArg.immOffset);439} else {440fp_.STR(128, INDEX_UNSIGNED, regs_.FQ(inst.src3), addrArg.base, addrArg.immOffset);441}442break;443444default:445INVALIDOP;446break;447}448}449450} // namespace MIPSComp451452#endif453454455