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/Arm64IRCompALU.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 "Common/CPUDetect.h"22#include "Core/MemMap.h"23#include "Core/MIPS/ARM64/Arm64IRJit.h"24#include "Core/MIPS/ARM64/Arm64IRRegCache.h"2526// This file contains compilation for integer / arithmetic / logic related instructions.27//28// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.29// Currently known non working ones should have DISABLE. No flags because that's in IR already.3031// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }32#define CONDITIONAL_DISABLE {}33#define DISABLE { CompIR_Generic(inst); return; }34#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }3536namespace MIPSComp {3738using namespace Arm64Gen;39using namespace Arm64IRJitConstants;4041void Arm64JitBackend::CompIR_Arith(IRInst inst) {42CONDITIONAL_DISABLE;4344bool allowPtrMath = inst.constant <= 0x7FFFFFFF;45#ifdef MASKED_PSP_MEMORY46// Since we modify it, we can't safely.47allowPtrMath = false;48#endif4950switch (inst.op) {51case IROp::Add:52regs_.Map(inst);53ADD(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));54break;5556case IROp::Sub:57regs_.Map(inst);58SUB(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));59break;6061case IROp::AddConst:62if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) {63regs_.MarkGPRAsPointerDirty(inst.dest);64ADDI2R(regs_.RPtr(inst.dest), regs_.RPtr(inst.src1), (int)inst.constant, SCRATCH1_64);65} else {66regs_.Map(inst);67ADDI2R(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant, SCRATCH1);68}69break;7071case IROp::SubConst:72if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) {73regs_.MarkGPRAsPointerDirty(inst.dest);74SUBI2R(regs_.RPtr(inst.dest), regs_.RPtr(inst.src1), (int)inst.constant, SCRATCH1_64);75} else {76regs_.Map(inst);77SUBI2R(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant, SCRATCH1);78}79break;8081case IROp::Neg:82regs_.Map(inst);83NEG(regs_.R(inst.dest), regs_.R(inst.src1));84break;8586default:87INVALIDOP;88break;89}90}9192void Arm64JitBackend::CompIR_Assign(IRInst inst) {93CONDITIONAL_DISABLE;9495switch (inst.op) {96case IROp::Mov:97if (inst.dest != inst.src1) {98regs_.Map(inst);99MOV(regs_.R(inst.dest), regs_.R(inst.src1));100}101break;102103case IROp::Ext8to32:104regs_.Map(inst);105SXTB(regs_.R(inst.dest), regs_.R(inst.src1));106break;107108case IROp::Ext16to32:109regs_.Map(inst);110SXTH(regs_.R(inst.dest), regs_.R(inst.src1));111break;112113default:114INVALIDOP;115break;116}117}118119void Arm64JitBackend::CompIR_Bits(IRInst inst) {120CONDITIONAL_DISABLE;121122switch (inst.op) {123case IROp::BSwap16:124regs_.Map(inst);125REV16(regs_.R(inst.dest), regs_.R(inst.src1));126break;127128case IROp::BSwap32:129regs_.Map(inst);130REV32(regs_.R(inst.dest), regs_.R(inst.src1));131break;132133case IROp::Clz:134regs_.Map(inst);135CLZ(regs_.R(inst.dest), regs_.R(inst.src1));136break;137138case IROp::ReverseBits:139regs_.Map(inst);140RBIT(regs_.R(inst.dest), regs_.R(inst.src1));141break;142143default:144INVALIDOP;145break;146}147}148149void Arm64JitBackend::CompIR_Compare(IRInst inst) {150CONDITIONAL_DISABLE;151152switch (inst.op) {153case IROp::Slt:154regs_.Map(inst);155CMP(regs_.R(inst.src1), regs_.R(inst.src2));156CSET(regs_.R(inst.dest), CC_LT);157break;158159case IROp::SltConst:160if (inst.constant == 0) {161// Basically, getting the sign bit.162regs_.Map(inst);163UBFX(regs_.R(inst.dest), regs_.R(inst.src1), 31, 1);164} else {165regs_.Map(inst);166CMPI2R(regs_.R(inst.src1), (int32_t)inst.constant, SCRATCH1);167CSET(regs_.R(inst.dest), CC_LT);168}169break;170171case IROp::SltU:172if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) {173// This is kinda common, same as != 0. Avoid flushing src1.174regs_.SpillLockGPR(inst.src2, inst.dest);175regs_.MapGPR(inst.src2);176regs_.MapGPR(inst.dest, MIPSMap::NOINIT);177CMP(regs_.R(inst.src2), 0);178CSET(regs_.R(inst.dest), CC_NEQ);179} else {180regs_.Map(inst);181CMP(regs_.R(inst.src1), regs_.R(inst.src2));182CSET(regs_.R(inst.dest), CC_LO);183}184break;185186case IROp::SltUConst:187if (inst.constant == 0) {188regs_.SetGPRImm(inst.dest, 0);189} else {190regs_.Map(inst);191CMPI2R(regs_.R(inst.src1), (int32_t)inst.constant, SCRATCH1);192CSET(regs_.R(inst.dest), CC_LO);193}194break;195196default:197INVALIDOP;198break;199}200}201202void Arm64JitBackend::CompIR_CondAssign(IRInst inst) {203CONDITIONAL_DISABLE;204205switch (inst.op) {206case IROp::MovZ:207if (inst.dest != inst.src2) {208regs_.Map(inst);209CMP(regs_.R(inst.src1), 0);210CSEL(regs_.R(inst.dest), regs_.R(inst.src2), regs_.R(inst.dest), CC_EQ);211}212break;213214case IROp::MovNZ:215if (inst.dest != inst.src2) {216regs_.Map(inst);217CMP(regs_.R(inst.src1), 0);218CSEL(regs_.R(inst.dest), regs_.R(inst.src2), regs_.R(inst.dest), CC_NEQ);219}220break;221222case IROp::Max:223if (inst.src1 != inst.src2) {224regs_.Map(inst);225CMP(regs_.R(inst.src1), regs_.R(inst.src2));226CSEL(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2), CC_GE);227} else if (inst.dest != inst.src1) {228regs_.Map(inst);229MOV(regs_.R(inst.dest), regs_.R(inst.src1));230}231break;232233case IROp::Min:234if (inst.src1 != inst.src2) {235regs_.Map(inst);236CMP(regs_.R(inst.src1), regs_.R(inst.src2));237CSEL(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2), CC_LE);238} else if (inst.dest != inst.src1) {239regs_.Map(inst);240MOV(regs_.R(inst.dest), regs_.R(inst.src1));241}242break;243244default:245INVALIDOP;246break;247}248}249250void Arm64JitBackend::CompIR_Div(IRInst inst) {251CONDITIONAL_DISABLE;252253switch (inst.op) {254case IROp::Div:255regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } });256// INT_MIN divided by -1 = INT_MIN, anything divided by 0 = 0.257SDIV(regs_.R(IRREG_LO), regs_.R(inst.src1), regs_.R(inst.src2));258MSUB(SCRATCH1, regs_.R(inst.src2), regs_.R(IRREG_LO), regs_.R(inst.src1));259260// Now some tweaks for divide by zero and overflow.261{262// Start with divide by zero, remainder is fine.263FixupBranch skipNonZero = CBNZ(regs_.R(inst.src2));264MOVI2R(regs_.R(IRREG_LO), 1);265CMP(regs_.R(inst.src1), 0);266// mips->lo = numerator < 0 ? 1 : -1267CSNEG(regs_.R(IRREG_LO), regs_.R(IRREG_LO), regs_.R(IRREG_LO), CC_LT);268SetJumpTarget(skipNonZero);269270// For overflow, we end up with remainder as zero, need to fix.271MOVI2R(SCRATCH2, 0x80000000);272CMP(regs_.R(inst.src1), SCRATCH2);273FixupBranch notMostNegative = B(CC_NEQ);274CMPI2R(regs_.R(inst.src2), -1);275// If it's not equal, keep SCRATCH1. Otherwise (equal), invert 0 = -1.276CSINV(SCRATCH1, SCRATCH1, WZR, CC_NEQ);277SetJumpTarget(notMostNegative);278}279280BFI(regs_.R64(IRREG_LO), SCRATCH1_64, 32, 32);281break;282283case IROp::DivU:284regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } });285// Anything divided by 0 = 0.286UDIV(regs_.R(IRREG_LO), regs_.R(inst.src1), regs_.R(inst.src2));287MSUB(SCRATCH1, regs_.R(inst.src2), regs_.R(IRREG_LO), regs_.R(inst.src1));288289// On divide by zero, we have to update LO - remainder is correct.290{291FixupBranch skipNonZero = CBNZ(regs_.R(inst.src2));292MOVI2R(regs_.R(IRREG_LO), 0xFFFF);293CMP(regs_.R(inst.src1), regs_.R(IRREG_LO));294// If it's <= 0xFFFF, keep 0xFFFF. Otherwise, invert 0 = -1.295CSINV(regs_.R(IRREG_LO), regs_.R(IRREG_LO), WZR, CC_LE);296SetJumpTarget(skipNonZero);297}298299BFI(regs_.R64(IRREG_LO), SCRATCH1_64, 32, 32);300break;301302default:303INVALIDOP;304break;305}306}307308void Arm64JitBackend::CompIR_HiLo(IRInst inst) {309CONDITIONAL_DISABLE;310311switch (inst.op) {312case IROp::MtLo:313regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });314BFI(regs_.R64(IRREG_LO), regs_.R64(inst.src1), 0, 32);315break;316317case IROp::MtHi:318regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });319BFI(regs_.R64(IRREG_LO), regs_.R64(inst.src1), 32, 32);320break;321322case IROp::MfLo:323regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::INIT } });324MOV(regs_.R(inst.dest), regs_.R(IRREG_LO));325break;326327case IROp::MfHi:328regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::INIT } });329UBFX(regs_.R64(inst.dest), regs_.R64(IRREG_LO), 32, 32);330break;331332default:333INVALIDOP;334break;335}336}337338void Arm64JitBackend::CompIR_Logic(IRInst inst) {339CONDITIONAL_DISABLE;340341switch (inst.op) {342case IROp::And:343if (inst.src1 != inst.src2) {344regs_.Map(inst);345AND(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));346} else if (inst.src1 != inst.dest) {347regs_.Map(inst);348MOV(regs_.R(inst.dest), regs_.R(inst.src1));349}350break;351352case IROp::Or:353if (inst.src1 != inst.src2) {354regs_.Map(inst);355ORR(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));356} else if (inst.src1 != inst.dest) {357regs_.Map(inst);358MOV(regs_.R(inst.dest), regs_.R(inst.src1));359}360break;361362case IROp::Xor:363if (inst.src1 == inst.src2) {364regs_.SetGPRImm(inst.dest, 0);365} else {366regs_.Map(inst);367EOR(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));368}369break;370371case IROp::AndConst:372regs_.Map(inst);373ANDI2R(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant, SCRATCH1);374break;375376case IROp::OrConst:377regs_.Map(inst);378ORRI2R(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant, SCRATCH1);379break;380381case IROp::XorConst:382regs_.Map(inst);383EORI2R(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant, SCRATCH1);384break;385386case IROp::Not:387regs_.Map(inst);388MVN(regs_.R(inst.dest), regs_.R(inst.src1));389break;390391default:392INVALIDOP;393break;394}395}396397void Arm64JitBackend::CompIR_Mult(IRInst inst) {398CONDITIONAL_DISABLE;399400switch (inst.op) {401case IROp::Mult:402regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } });403SMULL(regs_.R64(IRREG_LO), regs_.R(inst.src1), regs_.R(inst.src2));404break;405406case IROp::MultU:407regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } });408UMULL(regs_.R64(IRREG_LO), regs_.R(inst.src1), regs_.R(inst.src2));409break;410411case IROp::Madd:412regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });413// Accumulator is at the end, "standard" syntax.414SMADDL(regs_.R64(IRREG_LO), regs_.R(inst.src1), regs_.R(inst.src2), regs_.R64(IRREG_LO));415break;416417case IROp::MaddU:418regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });419// Accumulator is at the end, "standard" syntax.420UMADDL(regs_.R64(IRREG_LO), regs_.R(inst.src1), regs_.R(inst.src2), regs_.R64(IRREG_LO));421break;422423case IROp::Msub:424regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });425// Accumulator is at the end, "standard" syntax.426SMSUBL(regs_.R64(IRREG_LO), regs_.R(inst.src1), regs_.R(inst.src2), regs_.R64(IRREG_LO));427break;428429case IROp::MsubU:430regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });431// Accumulator is at the end, "standard" syntax.432UMSUBL(regs_.R64(IRREG_LO), regs_.R(inst.src1), regs_.R(inst.src2), regs_.R64(IRREG_LO));433break;434435default:436INVALIDOP;437break;438}439}440441void Arm64JitBackend::CompIR_Shift(IRInst inst) {442CONDITIONAL_DISABLE;443444switch (inst.op) {445case IROp::Shl:446regs_.Map(inst);447LSLV(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));448break;449450case IROp::Shr:451regs_.Map(inst);452LSRV(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));453break;454455case IROp::Sar:456regs_.Map(inst);457ASRV(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));458break;459460case IROp::Ror:461regs_.Map(inst);462RORV(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));463break;464465case IROp::ShlImm:466// Shouldn't happen, but let's be safe of any passes that modify the ops.467if (inst.src2 >= 32) {468regs_.SetGPRImm(inst.dest, 0);469} else if (inst.src2 == 0) {470if (inst.dest != inst.src1) {471regs_.Map(inst);472MOV(regs_.R(inst.dest), regs_.R(inst.src1));473}474} else {475regs_.Map(inst);476MOV(regs_.R(inst.dest), regs_.R(inst.src1), ArithOption(regs_.R(inst.src1), ST_LSL, inst.src2));477}478break;479480case IROp::ShrImm:481// Shouldn't happen, but let's be safe of any passes that modify the ops.482if (inst.src2 >= 32) {483regs_.SetGPRImm(inst.dest, 0);484} else if (inst.src2 == 0) {485if (inst.dest != inst.src1) {486regs_.Map(inst);487MOV(regs_.R(inst.dest), regs_.R(inst.src1));488}489} else {490regs_.Map(inst);491MOV(regs_.R(inst.dest), regs_.R(inst.src1), ArithOption(regs_.R(inst.src1), ST_LSR, inst.src2));492}493break;494495case IROp::SarImm:496// Shouldn't happen, but let's be safe of any passes that modify the ops.497if (inst.src2 >= 32) {498regs_.Map(inst);499MOV(regs_.R(inst.dest), regs_.R(inst.src1), ArithOption(regs_.R(inst.src1), ST_ASR, 31));500} else if (inst.src2 == 0) {501if (inst.dest != inst.src1) {502regs_.Map(inst);503MOV(regs_.R(inst.dest), regs_.R(inst.src1));504}505} else {506regs_.Map(inst);507MOV(regs_.R(inst.dest), regs_.R(inst.src1), ArithOption(regs_.R(inst.src1), ST_ASR, inst.src2));508}509break;510511case IROp::RorImm:512if (inst.src2 == 0) {513if (inst.dest != inst.src1) {514regs_.Map(inst);515MOV(regs_.R(inst.dest), regs_.R(inst.src1));516}517} else {518regs_.Map(inst);519MOV(regs_.R(inst.dest), regs_.R(inst.src1), ArithOption(regs_.R(inst.src1), ST_ROR, inst.src2 & 31));520}521break;522523default:524INVALIDOP;525break;526}527}528529} // namespace MIPSComp530531#endif532533534