mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-10 08:09:26 +01:00
JitRegCache: Move files to subdirectory
This commit is contained in:
parent
448fc89e4c
commit
6fef683e14
@ -236,20 +236,20 @@ if(_M_X86)
|
||||
DSP/Jit/x64/DSPJitMultiplier.cpp
|
||||
DSP/Jit/x64/DSPJitTables.cpp
|
||||
DSP/Jit/x64/DSPJitUtil.cpp
|
||||
PowerPC/Jit64/FPURegCache.cpp
|
||||
PowerPC/Jit64/GPRRegCache.cpp
|
||||
PowerPC/Jit64/Jit64_Tables.cpp
|
||||
PowerPC/Jit64/JitAsm.cpp
|
||||
PowerPC/Jit64/Jit_Branch.cpp
|
||||
PowerPC/Jit64/Jit.cpp
|
||||
PowerPC/Jit64/Jit64_Tables.cpp
|
||||
PowerPC/Jit64/Jit_Branch.cpp
|
||||
PowerPC/Jit64/Jit_FloatingPoint.cpp
|
||||
PowerPC/Jit64/Jit_Integer.cpp
|
||||
PowerPC/Jit64/Jit_LoadStore.cpp
|
||||
PowerPC/Jit64/Jit_LoadStoreFloating.cpp
|
||||
PowerPC/Jit64/Jit_LoadStorePaired.cpp
|
||||
PowerPC/Jit64/Jit_Paired.cpp
|
||||
PowerPC/Jit64/JitRegCache.cpp
|
||||
PowerPC/Jit64/Jit_SystemRegisters.cpp
|
||||
PowerPC/Jit64/JitAsm.cpp
|
||||
PowerPC/Jit64/RegCache/FPURegCache.cpp
|
||||
PowerPC/Jit64/RegCache/GPRRegCache.cpp
|
||||
PowerPC/Jit64/RegCache/JitRegCache.cpp
|
||||
PowerPC/Jit64Common/BlockCache.cpp
|
||||
PowerPC/Jit64Common/ConstantPool.cpp
|
||||
PowerPC/Jit64Common/EmuCodeBlock.cpp
|
||||
|
@ -241,8 +241,8 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\USB\Bluetooth\WiimoteDevice.cpp" />
|
||||
<ClCompile Include="IOS\USB\Bluetooth\WiimoteHIDAttr.cpp" />
|
||||
<ClCompile Include="IOS\WFS\WFSSRV.cpp" />
|
||||
<ClCompile Include="IOS\WFS\WFSI.cpp" />
|
||||
<ClCompile Include="IOS\WFS\WFSSRV.cpp" />
|
||||
<ClCompile Include="MemTools.cpp" />
|
||||
<ClCompile Include="Movie.cpp" />
|
||||
<ClCompile Include="NetPlayClient.cpp" />
|
||||
@ -260,13 +260,8 @@
|
||||
<ClCompile Include="PowerPC\Interpreter\Interpreter_Paired.cpp" />
|
||||
<ClCompile Include="PowerPC\Interpreter\Interpreter_SystemRegisters.cpp" />
|
||||
<ClCompile Include="PowerPC\Interpreter\Interpreter_Tables.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64Common\ConstantPool.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\FPURegCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\GPRRegCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\Jit.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\Jit64_Tables.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\JitAsm.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\JitRegCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\Jit_Branch.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\Jit_FloatingPoint.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\Jit_Integer.cpp" />
|
||||
@ -275,7 +270,12 @@
|
||||
<ClCompile Include="PowerPC\Jit64\Jit_LoadStorePaired.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\Jit_Paired.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\Jit_SystemRegisters.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\JitAsm.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\RegCache\FPURegCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\RegCache\GPRRegCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64\RegCache\JitRegCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64Common\BlockCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64Common\ConstantPool.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64Common\EmuCodeBlock.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64Common\FarCodeCache.cpp" />
|
||||
<ClCompile Include="PowerPC\Jit64Common\Jit64AsmCommon.cpp" />
|
||||
@ -284,10 +284,6 @@
|
||||
<ClCompile Include="PowerPC\JitCommon\JitAsmCommon.cpp" />
|
||||
<ClCompile Include="PowerPC\JitCommon\JitBase.cpp" />
|
||||
<ClCompile Include="PowerPC\JitCommon\JitCache.cpp" />
|
||||
<ClCompile Include="PowerPC\SignatureDB\CSVSignatureDB.cpp" />
|
||||
<ClCompile Include="PowerPC\SignatureDB\DSYSignatureDB.cpp" />
|
||||
<ClCompile Include="PowerPC\SignatureDB\MEGASignatureDB.cpp" />
|
||||
<ClCompile Include="PowerPC\SignatureDB\SignatureDB.cpp" />
|
||||
<ClCompile Include="PowerPC\JitInterface.cpp" />
|
||||
<ClCompile Include="PowerPC\MMU.cpp" />
|
||||
<ClCompile Include="PowerPC\PowerPC.cpp" />
|
||||
@ -295,6 +291,10 @@
|
||||
<ClCompile Include="PowerPC\PPCCache.cpp" />
|
||||
<ClCompile Include="PowerPC\PPCSymbolDB.cpp" />
|
||||
<ClCompile Include="PowerPC\PPCTables.cpp" />
|
||||
<ClCompile Include="PowerPC\SignatureDB\CSVSignatureDB.cpp" />
|
||||
<ClCompile Include="PowerPC\SignatureDB\DSYSignatureDB.cpp" />
|
||||
<ClCompile Include="PowerPC\SignatureDB\MEGASignatureDB.cpp" />
|
||||
<ClCompile Include="PowerPC\SignatureDB\SignatureDB.cpp" />
|
||||
<ClCompile Include="State.cpp" />
|
||||
<ClCompile Include="SysConf.cpp" />
|
||||
<ClCompile Include="TitleDatabase.cpp" />
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/PatchEngine.h"
|
||||
#include "Core/PowerPC/Jit64/JitAsm.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/FarCodeCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||
#include "Core/PowerPC/Jit64Common/TrampolineCache.h"
|
||||
|
@ -21,10 +21,10 @@
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/x64ABI.h"
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Core/PowerPC/Jit64/FPURegCache.h"
|
||||
#include "Core/PowerPC/Jit64/GPRRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/JitAsm.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/FPURegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/GPRRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64Base.h"
|
||||
#include "Core/PowerPC/JitCommon/JitCache.h"
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/PowerPC/Gekko.h"
|
||||
#include "Core/PowerPC/Jit64/Jit.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||
#include "Core/PowerPC/PPCAnalyst.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/PowerPC/Jit64/Jit.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||
#include "Core/PowerPC/PPCAnalyst.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Core/PowerPC/Jit64/Jit.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||
#include "Core/PowerPC/PPCAnalyst.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/HW/CPU.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||
#include "Core/PowerPC/JitInterface.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Core/PowerPC/Jit64/Jit.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||
#include "Core/PowerPC/JitCommon/JitAsmCommon.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Core/PowerPC/Jit64/Jit.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/PowerPC/Jit64/Jit.h"
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
|
127
Source/Core/Core/PowerPC/Jit64/RegCache/CachedReg.h
Normal file
127
Source/Core/Core/PowerPC/Jit64/RegCache/CachedReg.h
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/x64Emitter.h"
|
||||
|
||||
using preg_t = size_t;
|
||||
|
||||
class PPCCachedReg
|
||||
{
|
||||
public:
|
||||
enum class LocationType
|
||||
{
|
||||
/// Value is currently at its default location
|
||||
Default,
|
||||
/// Value is currently bound to a x64 register
|
||||
Bound,
|
||||
/// Value is known as an immediate and has not been written back to its default location
|
||||
Immediate,
|
||||
/// Value is known as an immediate and is already present at its default location
|
||||
SpeculativeImmediate,
|
||||
};
|
||||
|
||||
PPCCachedReg() = default;
|
||||
|
||||
explicit PPCCachedReg(Gen::OpArg default_location_)
|
||||
: default_location(default_location_), location(default_location_)
|
||||
{
|
||||
}
|
||||
|
||||
const Gen::OpArg& Location() const { return location; }
|
||||
|
||||
LocationType GetLocationType() const
|
||||
{
|
||||
if (!away)
|
||||
{
|
||||
if (location.IsImm())
|
||||
return LocationType::SpeculativeImmediate;
|
||||
|
||||
ASSERT(location == default_location);
|
||||
return LocationType::Default;
|
||||
}
|
||||
|
||||
ASSERT(location.IsImm() || location.IsSimpleReg());
|
||||
return location.IsImm() ? LocationType::Immediate : LocationType::Bound;
|
||||
}
|
||||
|
||||
bool IsAway() const { return away; }
|
||||
bool IsBound() const { return GetLocationType() == LocationType::Bound; }
|
||||
|
||||
void SetBoundTo(Gen::X64Reg xreg)
|
||||
{
|
||||
away = true;
|
||||
location = Gen::R(xreg);
|
||||
}
|
||||
|
||||
void SetFlushed()
|
||||
{
|
||||
away = false;
|
||||
location = default_location;
|
||||
}
|
||||
|
||||
void SetToImm32(u32 imm32, bool dirty = true)
|
||||
{
|
||||
away |= dirty;
|
||||
location = Gen::Imm32(imm32);
|
||||
}
|
||||
|
||||
bool IsLocked() const { return locked > 0; }
|
||||
void Lock() { locked++; }
|
||||
void Unlock()
|
||||
{
|
||||
ASSERT(IsLocked());
|
||||
locked--;
|
||||
}
|
||||
void UnlockAll() { locked = 0; } // TODO: Remove from final version
|
||||
|
||||
private:
|
||||
Gen::OpArg default_location{};
|
||||
Gen::OpArg location{};
|
||||
bool away = false; // value not in source register
|
||||
size_t locked = 0;
|
||||
};
|
||||
|
||||
class X64CachedReg
|
||||
{
|
||||
public:
|
||||
preg_t Contents() const { return ppcReg; }
|
||||
|
||||
void SetBoundTo(preg_t ppcReg_, bool dirty_)
|
||||
{
|
||||
free = false;
|
||||
ppcReg = ppcReg_;
|
||||
dirty = dirty_;
|
||||
}
|
||||
|
||||
void SetFlushed()
|
||||
{
|
||||
ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
|
||||
free = true;
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
bool IsFree() const { return free && !locked; }
|
||||
|
||||
bool IsDirty() const { return dirty; }
|
||||
void MakeDirty() { dirty = true; }
|
||||
|
||||
bool IsLocked() const { return locked > 0; }
|
||||
void Lock() { locked++; }
|
||||
void Unlock()
|
||||
{
|
||||
ASSERT(IsLocked());
|
||||
locked--;
|
||||
}
|
||||
void UnlockAll() { locked = 0; } // TODO: Remove from final version
|
||||
|
||||
private:
|
||||
preg_t ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
|
||||
bool free = true;
|
||||
bool dirty = false;
|
||||
size_t locked = 0;
|
||||
};
|
@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/PowerPC/Jit64/FPURegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/FPURegCache.h"
|
||||
|
||||
#include "Core/PowerPC/Jit64/Jit.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64Base.h"
|
@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
|
||||
class Jit64;
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/PowerPC/Jit64/GPRRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/GPRRegCache.h"
|
||||
|
||||
#include "Core/PowerPC/Jit64/Jit.h"
|
||||
#include "Core/PowerPC/Jit64Common/Jit64Base.h"
|
@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
|
||||
class Jit64;
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/PowerPC/Jit64/JitRegCache.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/JitRegCache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
@ -15,6 +15,7 @@
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Core/PowerPC/Jit64/Jit.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/CachedReg.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
using namespace Gen;
|
@ -6,131 +6,16 @@
|
||||
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Core/PowerPC/PPCAnalyst.h"
|
||||
#include "Core/PowerPC/Jit64/RegCache/CachedReg.h"
|
||||
|
||||
class Jit64;
|
||||
|
||||
using preg_t = size_t;
|
||||
|
||||
class PPCCachedReg
|
||||
{
|
||||
public:
|
||||
enum class LocationType
|
||||
{
|
||||
/// Value is currently at its default location
|
||||
Default,
|
||||
/// Value is currently bound to a x64 register
|
||||
Bound,
|
||||
/// Value is known as an immediate and has not been written back to its default location
|
||||
Immediate,
|
||||
/// Value is known as an immediate and is already present at its default location
|
||||
SpeculativeImmediate,
|
||||
};
|
||||
|
||||
PPCCachedReg() = default;
|
||||
|
||||
explicit PPCCachedReg(Gen::OpArg default_location_)
|
||||
: default_location(default_location_), location(default_location_)
|
||||
{
|
||||
}
|
||||
|
||||
const Gen::OpArg& Location() const { return location; }
|
||||
|
||||
LocationType GetLocationType() const
|
||||
{
|
||||
if (!away)
|
||||
{
|
||||
if (location.IsImm())
|
||||
return LocationType::SpeculativeImmediate;
|
||||
|
||||
ASSERT(location == default_location);
|
||||
return LocationType::Default;
|
||||
}
|
||||
|
||||
ASSERT(location.IsImm() || location.IsSimpleReg());
|
||||
return location.IsImm() ? LocationType::Immediate : LocationType::Bound;
|
||||
}
|
||||
|
||||
bool IsAway() const { return away; }
|
||||
bool IsBound() const { return GetLocationType() == LocationType::Bound; }
|
||||
|
||||
void SetBoundTo(Gen::X64Reg xreg)
|
||||
{
|
||||
away = true;
|
||||
location = Gen::R(xreg);
|
||||
}
|
||||
|
||||
void SetFlushed()
|
||||
{
|
||||
away = false;
|
||||
location = default_location;
|
||||
}
|
||||
|
||||
void SetToImm32(u32 imm32, bool dirty = true)
|
||||
{
|
||||
away |= dirty;
|
||||
location = Gen::Imm32(imm32);
|
||||
}
|
||||
|
||||
bool IsLocked() const { return locked > 0; }
|
||||
void Lock() { locked++; }
|
||||
void Unlock()
|
||||
{
|
||||
ASSERT(IsLocked());
|
||||
locked--;
|
||||
}
|
||||
void UnlockAll() { locked = 0; } // TODO: Remove from final version
|
||||
|
||||
private:
|
||||
Gen::OpArg default_location{};
|
||||
Gen::OpArg location{};
|
||||
bool away = false; // value not in source register
|
||||
size_t locked = 0;
|
||||
};
|
||||
|
||||
class X64CachedReg
|
||||
{
|
||||
public:
|
||||
preg_t Contents() const { return ppcReg; }
|
||||
|
||||
void SetBoundTo(preg_t ppcReg_, bool dirty_)
|
||||
{
|
||||
free = false;
|
||||
ppcReg = ppcReg_;
|
||||
dirty = dirty_;
|
||||
}
|
||||
|
||||
void SetFlushed()
|
||||
{
|
||||
ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
|
||||
free = true;
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
bool IsFree() const { return free && !locked; }
|
||||
|
||||
bool IsDirty() const { return dirty; }
|
||||
void MakeDirty() { dirty = true; }
|
||||
|
||||
bool IsLocked() const { return locked > 0; }
|
||||
void Lock() { locked++; }
|
||||
void Unlock()
|
||||
{
|
||||
ASSERT(IsLocked());
|
||||
locked--;
|
||||
}
|
||||
void UnlockAll() { locked = 0; } // TODO: Remove from final version
|
||||
|
||||
private:
|
||||
preg_t ppcReg = static_cast<preg_t>(Gen::INVALID_REG);
|
||||
bool free = true;
|
||||
bool dirty = false;
|
||||
size_t locked = 0;
|
||||
};
|
||||
|
||||
class RegCache
|
||||
{
|
||||
public:
|
Loading…
x
Reference in New Issue
Block a user