Skip to content

Commit

Permalink
[RISC-V] Implement NativeWalker. (dotnet#94853)
Browse files Browse the repository at this point in the history
* [RISC-V] Implement NativeWalker.

* [RISC-V] Add asserts for X0 value.
  • Loading branch information
viewizard authored Dec 4, 2023
1 parent 99877a1 commit 5d13463
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 2 deletions.
154 changes: 153 additions & 1 deletion src/coreclr/debug/ee/riscv64/walker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,158 @@

#ifdef TARGET_RISCV64

// #error "TODO-RISCV64: missing implementation"
inline uint64_t SignExtend(uint64_t value, unsigned int signbit)
{
_ASSERTE(signbit < 64);

if (signbit == 63)
return value;

uint64_t sign = value & (1ull << signbit);

if (sign)
return value | (~0ull << signbit);
else
return value;
}

inline uint64_t BitExtract(uint64_t value, unsigned int highbit, unsigned int lowbit, bool signExtend = false)
{
_ASSERTE((highbit < 64) && (lowbit < 64) && (highbit >= lowbit));
uint64_t extractedValue = (value >> lowbit) & ((1ull << ((highbit - lowbit) + 1)) - 1);

return signExtend ? SignExtend(extractedValue, highbit - lowbit) : extractedValue;
}

uint64_t NativeWalker::GetReg(uint64_t reg)
{
_ASSERTE(reg <= 31);
_ASSERTE(m_registers->pCurrentContext->R0 == 0);

return (&m_registers->pCurrentContext->R0)[reg];
}

void NativeWalker::Decode()
{
// Reset so that we do not provide bogus info
m_nextIP = NULL;
m_skipIP = NULL;
m_type = WALK_UNKNOWN;

if (m_registers == NULL)
{
// Walker does not use WALK_NEXT
// Without registers decoding will work only for handful of instructions
return;
}

// Fetch first word of the current instruction. If the current instruction is a break instruction, we'll
// need to check the patch table to get the correct instruction.
PRD_TYPE opcode = CORDbgGetInstruction(m_ip);
PRD_TYPE unpatchedOpcode;
if (DebuggerController::CheckGetPatchedOpcode(m_ip, &unpatchedOpcode))
{
opcode = unpatchedOpcode;
}

LOG((LF_CORDB, LL_INFO100000, "RiscV64Walker::Decode instruction at %p, opcode: %x\n", m_ip, opcode));

// TODO after "C" Standard Extension support implemented, add C.J, C.JAL, C.JR, C.JALR, C.BEQZ, C.BNEZ

if ((opcode & 0x7f) == 0x6f) // JAL
{
// J-immediate encodes a signed offset in multiples of 2 bytes
// 20 | 19 1 | 0
// inst[31]/sign | inst[19:12] | inst[20] | inst[30:25] | inst[24:21] | 0
uint64_t imm = SignExtend((BitExtract(opcode, 30, 21) << 1) | (BitExtract(opcode, 20, 20) << 11) |
(BitExtract(opcode, 19, 12) << 12) | (BitExtract(opcode, 31, 31) << 20), 20);
uint64_t Rd = BitExtract(opcode, 11, 7);

m_nextIP = m_ip + imm;
// The standard software calling convention uses X1 as the return address register and X5 as an alternate link register.
if (Rd == 1 || Rd == 5)
{
m_skipIP = m_ip + 4;
m_type = WALK_CALL;
}
else
{
m_skipIP = m_nextIP;
m_type = WALK_BRANCH;
}
}
else if ((opcode & 0x707f) == 0x67) // JALR
{
// I-immediate
uint64_t imm = BitExtract(opcode, 31, 20, true);
uint64_t Rs1 = BitExtract(opcode, 19, 15);
uint64_t Rd = BitExtract(opcode, 11, 7);

m_nextIP = (BYTE*)((GetReg(Rs1) + imm) & ~1ull);
// The standard software calling convention uses X1 as the return address register and X5 as an alternate link register.
if (Rd == 1 || Rd == 5)
{
m_skipIP = m_ip + 4;
m_type = WALK_CALL;
}
else if (Rs1 == 1 || Rs1 == 5)
{
m_type = WALK_RETURN;
}
else
{
m_skipIP = m_nextIP;
m_type = WALK_BRANCH;
}
}
else if (((opcode & 0x707f) == 0x63) || // BEQ
((opcode & 0x707f) == 0x1063) || // BNE
((opcode & 0x707f) == 0x4063) || // BLT
((opcode & 0x707f) == 0x5063)) // BGE
{
uint64_t Rs1 = BitExtract(opcode, 19, 15);
uint64_t Rs2 = BitExtract(opcode, 24, 20);
int64_t Rs1SValue = GetReg(Rs1);
int64_t Rs2SValue = GetReg(Rs2);

if ((((opcode & 0x707f) == 0x63) && Rs1SValue == Rs2SValue) ||
(((opcode & 0x707f) == 0x1063) && Rs1SValue != Rs2SValue) ||
(((opcode & 0x707f) == 0x4063) && Rs1SValue < Rs2SValue) ||
(((opcode & 0x707f) == 0x5063) && Rs1SValue >= Rs2SValue))
{
// B-immediate encodes a signed offset in multiples of 2 bytes
// 12 | 11 1 | 0
// inst[31]/sign | inst[7] | inst[30:25] | inst[11:8] | 0
uint64_t imm = SignExtend((BitExtract(opcode, 11, 8) << 1) | (BitExtract(opcode, 30, 25) << 5) |
(BitExtract(opcode, 7, 7) << 11) | (BitExtract(opcode, 31, 31) << 12), 12);

m_nextIP = m_ip + imm;
m_skipIP = m_nextIP;
m_type = WALK_BRANCH;
}
}
else if (((opcode & 0x707f) == 0x6063) || // BLTU
((opcode & 0x707f) == 0x7063)) // BGEU
{
uint64_t Rs1 = BitExtract(opcode, 19, 15);
uint64_t Rs2 = BitExtract(opcode, 24, 20);
uint64_t Rs1Value = GetReg(Rs1);
uint64_t Rs2Value = GetReg(Rs2);

if ((((opcode & 0x707f) == 0x6063) && Rs1Value < Rs2Value) ||
(((opcode & 0x707f) == 0x7063) && Rs1Value >= Rs2Value))
{
// B-immediate encodes a signed offset in multiples of 2 bytes
// 12 | 11 1 | 0
// inst[31]/sign | inst[7] | inst[30:25] | inst[11:8] | 0
uint64_t imm = SignExtend((BitExtract(opcode, 11, 8) << 1) | (BitExtract(opcode, 30, 25) << 5) |
(BitExtract(opcode, 7, 7) << 11) | (BitExtract(opcode, 31, 31) << 12), 12);

m_nextIP = m_ip + imm;
m_skipIP = m_nextIP;
m_type = WALK_BRANCH;
}
}
}

#endif
12 changes: 12 additions & 0 deletions src/coreclr/debug/ee/walker.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,18 @@ class NativeWalker : public Walker
static BOOL DecodePCRelativeBranchInst(PT_CONTEXT context,const PRD_TYPE opcode, PCODE& offset, WALK_TYPE& walk);
static BOOL DecodeJumpInst(const PRD_TYPE opcode, int& RegNum, PCODE& offset, WALK_TYPE& walk);
};
#elif defined (TARGET_RISCV64)
#include "controller.h"
class NativeWalker : public Walker
{
public:
void Init(const BYTE *ip, REGDISPLAY *pregisters)
{
Walker::Init(ip, pregisters);
}
void Decode();
uint64_t GetReg(uint64_t reg);
};
#else
PORTABILITY_WARNING("NativeWalker not implemented on this platform");
class NativeWalker : public Walker
Expand Down
6 changes: 5 additions & 1 deletion src/coreclr/vm/riscv64/singlestepper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,14 +287,18 @@ bool RiscV64SingleStepper::Fixup(T_CONTEXT *pCtx, DWORD dwExceptionCode)
LOG((LF_CORDB, LL_INFO100000, "RiscV64SingleStepper::Fixup hit exception pc = %lx ex = %x\n", pCtx->Pc, dwExceptionCode));
}

_ASSERTE((pCtx->Pc & 0x3) == 0); // TODO change this after "C" Standard Extension support implemented
// Note, 2 bytes alignment assertion here, since during managed stepping we could step/return
// into `libcoreclr.so` code, that compiled with "C" Standard Extension.
_ASSERTE((pCtx->Pc & 0x1) == 0);

return true;
}

// Get the current value of a register.
uint64_t RiscV64SingleStepper::GetReg(T_CONTEXT *pCtx, uint64_t reg)
{
_ASSERTE(reg <= 31);
_ASSERTE(pCtx->R0 == 0);

return (&pCtx->R0)[reg];
}
Expand Down

0 comments on commit 5d13463

Please sign in to comment.