mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-01 10:52:26 +00:00
[docs] Various grammar fixes, fix dead links, update deps.md, merge RDOC into Debug.md (#4315)
Signed-off-by: lizzie <lizzie@eden-emu.dev> - [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- updates deps.md (freebsd had a package that didnt exist, oops) netbsd also had outdated packages moved windows caveats into the user guide (because well, those are user stuff, not build caveats) MANY redundant wording removals (especially on dynarmic) proper capitalization of Eden, Dynarmic, Amiibo, and CLion fixed discord link on metainfo as well also fixed file associations as well otherwise installed eden wont have icon over NSP/XCI/etc files "please" is filler fixes a HANDFUL of dead links as well Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4315 Reviewed-by: crueter <crueter@eden-emu.dev> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
This commit is contained in:
+42
-42
@@ -1,44 +1,44 @@
|
||||
# Dynarmic Design Documentation
|
||||
|
||||
Dynarmic is a dynamic recompiler for the ARMv6K architecture. Future plans for dynarmic include
|
||||
Dynarmic is a dynamic recompiler for the ARMv6K architecture. Future plans for Dynarmic include
|
||||
support for other versions of the ARM architecture, having a interpreter mode, and adding support
|
||||
for other architectures.
|
||||
|
||||
Users of this library interact with it primarily through the interface provided in
|
||||
[`src/dynarmic/interface`](../src/dynarmic/interface). Users specify how dynarmic's CPU core interacts with
|
||||
[`src/dynarmic/interface`](../../src/dynarmic/src/dynarmic/interface). Users specify how Dynarmic's CPU core interacts with
|
||||
the rest of their system providing an implementation of the relevant `UserCallbacks` interface.
|
||||
Users setup the CPU state using member functions of `Jit`, then call `Jit::Execute` to start CPU
|
||||
execution. The callbacks defined on `UserCallbacks` may be called from dynamically generated code,
|
||||
so users of the library should not depend on the stack being in a walkable state for unwinding.
|
||||
|
||||
* A32: [`Jit`](../src/dynarmic/interface/A32/a32.h), [`UserCallbacks`](../src/dynarmic/interface/A32/config.h)
|
||||
* A64: [`Jit`](../src/dynarmic/interface/A64/a64.h), [`UserCallbacks`](../src/dynarmic/interface/A64/config.h)
|
||||
* A32: [`Jit`](../../src/dynarmic/src/dynarmic/interface/A32/a32.h), [`UserCallbacks`](../../src/dynarmic/src/dynarmic/interface/A32/config.h)
|
||||
* A64: [`Jit`](../../src/dynarmic/src/dynarmic/interface/A64/a64.h), [`UserCallbacks`](../../src/dynarmic/src/dynarmic/interface/A64/config.h)
|
||||
|
||||
Dynarmic reads instructions from memory by calling `UserCallbacks::MemoryReadCode`. These
|
||||
instructions then pass through several stages:
|
||||
|
||||
1. Decoding (Identifying what type of instruction it is and breaking it up into fields)
|
||||
2. Translation (Generation of high-level IR from the instruction)
|
||||
3. Optimization (Eliminiation of redundant microinstructions, other speed improvements)
|
||||
3. Optimization (Elimination of redundant microinstructions, other speed improvements)
|
||||
4. Emission (Generation of host-executable code into memory)
|
||||
5. Execution (Host CPU jumps to the start of emitted code and runs it)
|
||||
|
||||
Using the A32 frontend with the x64 backend as an example:
|
||||
|
||||
* Decoding is done by [double dispatch](https://en.wikipedia.org/wiki/Visitor_pattern) in
|
||||
[`src/frontend/A32/decoder/{arm.h,thumb16.h,thumb32.h}`](../src/dynarmic/frontend/A32/decoder/).
|
||||
* Translation is done by the visitors in [`src/dynarmic/frontend/A32/translate/translate_{arm,thumb}.cpp`](../src/dynarmic/frontend/A32/translate/).
|
||||
The function [`Translate`](../src/dynarmic/frontend/A32/translate/translate.h) takes a starting memory location,
|
||||
[`src/frontend/A32/decoder/{arm.h,thumb16.h,thumb32.h}`](../../src/dynarmic/src/dynarmic/frontend/A32/decoder/).
|
||||
* Translation is done by the visitors in [`src/dynarmic/frontend/A32/translate/translate_{arm,thumb}.cpp`](../../src/dynarmic/src/dynarmic/frontend/A32/translate/).
|
||||
The function [`Translate`](../../src/dynarmic/src/dynarmic/frontend/A32/translate/a32_translate.cpp) takes a starting memory location,
|
||||
some CPU state, and memory reader callback and returns a basic block of IR.
|
||||
* The IR can be found under [`src/frontend/ir/`](../src/dynarmic/ir/).
|
||||
* Optimizations can be found under [`src/ir_opt/`](../src/dynarmic/ir/opt/).
|
||||
* Emission is done by `EmitX64` which can be found in [`src/dynarmic/backend/x64/emit_x64.{h,cpp}`](../src/dynarmic/backend/x64/).
|
||||
* Execution is performed by calling `BlockOfCode::RunCode` in [`src/dynarmic/backend/x64/block_of_code.{h,cpp}`](../src/dynarmic/backend/x64/).
|
||||
* The IR can be found under [`src/frontend/ir/`](../../src/dynarmic/src/dynarmic/ir/).
|
||||
* Optimizations can be found under [`src/ir/opt_passes.cpp`](../../src/dynarmic/src/dynarmic/ir/opt_passes.cpp).
|
||||
* Emission is done by `EmitX64` which can be found in [`src/dynarmic/backend/x64/emit_x64.{h,cpp}`](../../src/dynarmic/src/dynarmic/backend/x64/).
|
||||
* Execution is performed by calling `BlockOfCode::RunCode` in [`src/dynarmic/backend/x64/block_of_code.{h,cpp}`](../../src/dynarmic/src/dynarmic/backend/x64/).
|
||||
|
||||
## Decoder
|
||||
|
||||
The decoder is a double dispatch decoder. Each instruction is represented by a line in the relevant
|
||||
instruction table. Here is an example line from [`arm.h`](../src/dynarmic/frontend/A32/decoder/arm.h):
|
||||
instruction table. Here is an example line from [`arm.h`](../../src/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h):
|
||||
|
||||
INST(&V::arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv")
|
||||
|
||||
@@ -61,7 +61,7 @@ error results.
|
||||
## Translator
|
||||
|
||||
The translator is a visitor that uses the decoder to decode instructions. The translator generates IR code with the
|
||||
help of the [`IREmitter` class](../src/dynarmic/ir/ir_emitter.h). An example of a translation function follows:
|
||||
help of the [`IREmitter` class](../../src/dynarmic/src/dynarmic/ir/ir_emitter.h). An example of a translation function follows:
|
||||
|
||||
bool ArmTranslatorVisitor::arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
|
||||
u32 imm32 = ArmExpandImm(rotate, imm8);
|
||||
@@ -107,7 +107,7 @@ function analyser in the medium-term future.
|
||||
Dynarmic's intermediate representation is typed. Each microinstruction may take zero or more arguments and may
|
||||
return zero or more arguments. A subset of the microinstructions available is documented below.
|
||||
|
||||
A complete list of microinstructions can be found in [src/dynarmic/ir/opcodes.inc](../src/dynarmic/ir/opcodes.inc).
|
||||
A complete list of microinstructions can be found in [src/dynarmic/ir/opcodes.inc](../../src/dynarmic/src/dynarmic/ir/opcodes.inc).
|
||||
|
||||
The below lists some commonly used microinstructions.
|
||||
|
||||
@@ -273,7 +273,7 @@ Exclusive OR (i.e.: XOR)
|
||||
|
||||
### Callback: {Read,Write}Memory{8,16,32,64}
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
<u8> ReadMemory8(<u32> vaddr)
|
||||
<u8> ReadMemory16(<u32> vaddr)
|
||||
<u8> ReadMemory32(<u32> vaddr)
|
||||
@@ -288,7 +288,7 @@ Memory access.
|
||||
|
||||
### Terminal: ReturnToDispatch
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
SetTerm(IR::Term::ReturnToDispatch{})
|
||||
```
|
||||
|
||||
@@ -297,7 +297,7 @@ The dispatcher will use the value in R15 to determine what comes next.
|
||||
|
||||
### Terminal: LinkBlock
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
SetTerm(IR::Term::LinkBlock{next})
|
||||
```
|
||||
|
||||
@@ -307,7 +307,7 @@ dispatcher, which will return control to the host.
|
||||
|
||||
### Terminal: LinkBlockFast
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
SetTerm(IR::Term::LinkBlockFast{next})
|
||||
```
|
||||
|
||||
@@ -316,7 +316,7 @@ This promises guarantees that must be held at runtime - i.e that the program won
|
||||
|
||||
### Terminal: PopRSBHint
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
SetTerm(IR::Term::PopRSBHint{})
|
||||
```
|
||||
|
||||
@@ -328,7 +328,7 @@ this optimization or doesn't have a RSB may choose to implement this exactly as
|
||||
|
||||
### Terminal: If
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
SetTerm(IR::Term::If{cond, term_then, term_else})
|
||||
```
|
||||
|
||||
@@ -366,7 +366,7 @@ Do NEVER modify `%r15`, we must make it clear that this register is "immutable"
|
||||
|
||||
### `Scratch`
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
Xbyak::Reg64 ScratchGpr(HostLocList desired_locations = any_gpr);
|
||||
Xbyak::Xmm ScratchXmm(HostLocList desired_locations = any_xmm);
|
||||
```
|
||||
@@ -375,7 +375,7 @@ At runtime, allocate one of the registers in `desired_locations`. You are free t
|
||||
|
||||
### Pure `Use`
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
Xbyak::Reg64 UseGpr(Argument& arg);
|
||||
Xbyak::Xmm UseXmm(Argument& arg);
|
||||
OpArg UseOpArg(Argument& arg);
|
||||
@@ -391,7 +391,7 @@ This register **must not** have it's value changed.
|
||||
|
||||
### `UseScratch`
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
Xbyak::Reg64 UseScratchGpr(Argument& arg);
|
||||
Xbyak::Xmm UseScratchXmm(Argument& arg);
|
||||
void UseScratch(Argument& arg, HostLoc host_loc);
|
||||
@@ -409,7 +409,7 @@ You are free to modify the value in the register. The register is discarded at t
|
||||
|
||||
A `Define` is the defintion of a value. This is the only time when a value may be set.
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
void DefineValue(IR::Inst* inst, const Xbyak::Reg& reg);
|
||||
```
|
||||
|
||||
@@ -420,7 +420,7 @@ value to the specified register `reg`.
|
||||
|
||||
Adding a `Define` to an existing value.
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
void DefineValue(IR::Inst* inst, Argument& arg);
|
||||
```
|
||||
|
||||
@@ -458,7 +458,7 @@ One complication dynarmic has is that a compiled block is not uniquely identifia
|
||||
the PC alone, but bits in the FPSCR and CPSR are also relevant. We resolve this by
|
||||
computing a 64-bit `UniqueHash` that is guaranteed to uniquely identify a block.
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
u64 LocationDescriptor::UniqueHash() const {
|
||||
// This value MUST BE UNIQUE.
|
||||
// This calculation has to match up with EmitX64::EmitTerminalPopRSBHint
|
||||
@@ -482,18 +482,18 @@ point. Each element in `rsb_location_descriptors` is a `UniqueHash` and they
|
||||
each correspond to an element in `rsb_codeptrs`. `rsb_codeptrs` contains the
|
||||
host addresses for the corresponding the compiled blocks.
|
||||
|
||||
`RSBSize` was chosen by performance testing. Note that this is bigger than the
|
||||
`RSB_SIZE` was chosen by performance testing. Note that this is bigger than the
|
||||
size of the real RSB in hardware (which has 3 entries). Larger RSBs than 8
|
||||
showed degraded performance.
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
struct JitState {
|
||||
// ...
|
||||
|
||||
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
|
||||
static constexpr size_t RSB_SIZE = 8; // MUST be a power of 2.
|
||||
u32 rsb_ptr = 0;
|
||||
std::array<u64, RSBSize> rsb_location_descriptors;
|
||||
std::array<u64, RSBSize> rsb_codeptrs;
|
||||
std::array<u64, RSB_SIZE> rsb_location_descriptors;
|
||||
std::array<u64, RSB_SIZE> rsb_codeptrs;
|
||||
void ResetRSB();
|
||||
|
||||
// ...
|
||||
@@ -505,7 +505,7 @@ struct JitState {
|
||||
We insert our prediction at the insertion point iff the RSB doesn't already
|
||||
contain a prediction with the same `UniqueHash`.
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
void EmitX64::EmitPushRSB(IR::Block&, IR::Inst* inst) {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
@@ -521,7 +521,7 @@ void EmitX64::EmitPushRSB(IR::Block&, IR::Inst* inst) {
|
||||
|
||||
code->mov(index_reg, dword[code.ABI_JIT_PTR + offsetof(JitState, rsb_ptr)]);
|
||||
code->add(index_reg, 1);
|
||||
code->and_(index_reg, u32(JitState::RSBSize - 1));
|
||||
code->and_(index_reg, u32(JitState::RSB_SIZE - 1));
|
||||
|
||||
code->mov(loc_desc_reg, u64(imm64));
|
||||
CodePtr patch_location = code->getCurr<CodePtr>();
|
||||
@@ -530,7 +530,7 @@ void EmitX64::EmitPushRSB(IR::Block&, IR::Inst* inst) {
|
||||
code->EnsurePatchLocationSize(patch_location, 10);
|
||||
|
||||
Xbyak::Label label;
|
||||
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
||||
for (size_t i = 0; i < JitState::RSB_SIZE; ++i) {
|
||||
code->cmp(loc_desc_reg, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
||||
code->je(label, code->T_SHORT);
|
||||
}
|
||||
@@ -544,12 +544,12 @@ void EmitX64::EmitPushRSB(IR::Block&, IR::Inst* inst) {
|
||||
|
||||
In pseudocode:
|
||||
|
||||
```c++
|
||||
for (i := 0 .. RSBSize-1)
|
||||
```cpp
|
||||
for (i := 0 .. RSB_SIZE-1)
|
||||
if (rsb_location_descriptors[i] == imm64)
|
||||
goto label;
|
||||
rsb_ptr++;
|
||||
rsb_ptr %= RSBSize;
|
||||
rsb_ptr %= RSB_SIZE;
|
||||
rsb_location_desciptors[rsb_ptr] = imm64; //< The UniqueHash
|
||||
rsb_codeptr[rsb_ptr] = /* codeptr corresponding to the UniqueHash */;
|
||||
label:
|
||||
@@ -559,7 +559,7 @@ label:
|
||||
|
||||
To check if a predicition is in the RSB, we linearly scan the RSB.
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
void EmitX64::EmitTerminalPopRSBHint(IR::Term::PopRSBHint, IR::LocationDescriptor initial_location) {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
@@ -571,7 +571,7 @@ void EmitX64::EmitTerminalPopRSBHint(IR::Term::PopRSBHint, IR::LocationDescripto
|
||||
code->or_(rbx, rcx);
|
||||
|
||||
code->mov(rax, u64(code->GetReturnFromRunCodeAddress()));
|
||||
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
||||
for (size_t i = 0; i < JitState::RSB_SIZE; ++i) {
|
||||
code->cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
||||
code->cmove(rax, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_codeptrs) + i * sizeof(u64)]);
|
||||
}
|
||||
@@ -582,10 +582,10 @@ void EmitX64::EmitTerminalPopRSBHint(IR::Term::PopRSBHint, IR::LocationDescripto
|
||||
|
||||
In pseudocode:
|
||||
|
||||
```c++
|
||||
```cpp
|
||||
rbx := ComputeUniqueHash()
|
||||
rax := ReturnToDispatch
|
||||
for (i := 0 .. RSBSize-1)
|
||||
for (i := 0 .. RSB_SIZE-1)
|
||||
if (rbx == rsb_location_descriptors[i])
|
||||
rax = rsb_codeptrs[i]
|
||||
goto rax
|
||||
|
||||
Reference in New Issue
Block a user