docs: Update documentation (2018-02-05)

This commit is contained in:
MerryMage 2018-02-05 22:30:39 +00:00
parent eb5591859c
commit ca43be4146
2 changed files with 102 additions and 106 deletions

View file

@ -4,15 +4,18 @@ Dynarmic is a dynamic recompiler for the ARMv6K architecture. Future plans for d
support for other versions of the ARM architecture, having a interpreter mode, and adding support support for other versions of the ARM architecture, having a interpreter mode, and adding support
for other architectures. for other architectures.
Users of this library interact with it primarily through [`include/dynarmic/dynarmic.h`](../include/dynarmic/dynarmic.h). Users of this library interact with it primarily through the interface provided in
Users specify how dynarmic's CPU core interacts with the rest of their systems by setting members of the [`include/dynarmic`](../include/dynarmic). Users specify how dynarmic's CPU core interacts with
[`Dynarmic::UserCallbacks`](../include/dynarmic/callbacks.h) structure as appropriate. Users setup the CPU state using member functions of the rest of their system providing an implementation of the relevant `UserCallbacks` interface.
`Dynarmic::Jit`, then call `Dynarmic::Jit::Execute` to start CPU execution. The callbacks defined on `UserCallbacks` Users setup the CPU state using member functions of `Jit`, then call `Jit::Execute` to start CPU
may be called from dynamically generated code, so users of the library should not depend on the stack being in a execution. The callbacks defined on `UserCallbacks` may be called from dynamically generated code,
walkable state for unwinding. so users of the library should not depend on the stack being in a walkable state for unwinding.
Dynarmic reads instructions from memory by calling `UserCallbacks::MemoryRead32`. These instructions then pass * A32: [`Jit`](../include/dynarmic/A32/a32.h), [`UserCallbacks`](../include/dynarmic/A32/config.h)
through several stages: * A64: [`Jit`](../include/dynarmic/A64/a64.h), [`UserCallbacks`](../include/dynarmic/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) 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) 2. Translation (Generation of high-level IR from the instruction)
@ -20,13 +23,13 @@ through several stages:
4. Emission (Generation of host-executable code into memory) 4. Emission (Generation of host-executable code into memory)
5. Execution (Host CPU jumps to the start of emitted code and runs it) 5. Execution (Host CPU jumps to the start of emitted code and runs it)
Using the x64 backend as an example: 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 * Decoding is done by [double dispatch](https://en.wikipedia.org/wiki/Visitor_pattern) in
[`src/frontend/decoder/{arm.h,thumb16.h,thumb32.h}`](../src/frontend/decoder/). [`src/frontend/A32/decoder/{arm.h,thumb16.h,thumb32.h}`](../src/frontend/A32/decoder/).
* Translation is done by the visitors in `src/frontend/translate/translate_{arm,thumb}.cpp`. * Translation is done by the visitors in `src/frontend/A32/translate/translate_{arm,thumb}.cpp`.
The function [`IR::Block Translate(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32)`](../src/frontend/translate/translate.h) takes a The function [`Translate`](../src/frontend/A32/translate/translate.h) takes a starting memory location,
memory location, some CPU state, and memory reader callback and returns a basic block of IR. some CPU state, and memory reader callback and returns a basic block of IR.
* The IR can be found under [`src/frontend/ir/`](../src/frontend/ir/). * The IR can be found under [`src/frontend/ir/`](../src/frontend/ir/).
* Optimizations can be found under [`src/ir_opt/`](../src/ir_opt/). * Optimizations can be found under [`src/ir_opt/`](../src/ir_opt/).
* Emission is done by `EmitX64` which can be found in `src/backend_x64/emit_x64.{h,cpp}`. * Emission is done by `EmitX64` which can be found in `src/backend_x64/emit_x64.{h,cpp}`.
@ -34,8 +37,8 @@ memory location, some CPU state, and memory reader callback and returns a basic
## Decoder ## Decoder
The decoder is a double dispatch decoder. Each instruction is represented by a line in the relevant instruction table. The decoder is a double dispatch decoder. Each instruction is represented by a line in the relevant
Here is an example line from `g_arm_instruction_table`: instruction table. Here is an example line from [`arm.h`](../src/frontend/A32/decoder/arm.h):
INST(&V::arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv") INST(&V::arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv")
@ -93,9 +96,9 @@ an IR microinstruction.
## Intermediate Representation ## Intermediate Representation
Dynarmic uses an ordered SSA intermediate representation. It is very vaguely similar to those found in other Dynarmic uses an ordered SSA intermediate representation. It is very vaguely similar to those found in other
similar projects like redream, nucleus, and xenia. Major differences are: (1) the abundance of context microinstructions similar projects like redream, nucleus, and xenia. Major differences are: (1) the abundance of context
whereas those projects generally only have two (`load_context`/`store_context`), (2) the explicit handling of microinstructions whereas those projects generally only have two (`load_context`/`store_context`), (2) the
flags as their own values, and (3) very different basic block edge handling. explicit handling of flags as their own values, and (3) very different basic block edge handling.
The intention of the context microinstructions and explicit flag handling is to allow for future optimizations. The The intention of the context microinstructions and explicit flag handling is to allow for future optimizations. The
differences in the way edges are handled are a quirk of the current implementation and dynarmic will likely add a differences in the way edges are handled are a quirk of the current implementation and dynarmic will likely add a
@ -106,6 +109,8 @@ return zero or more arguments. A subset of the microinstructions available is do
A complete list of microinstructions can be found in [src/frontend/ir/opcodes.inc](../src/frontend/ir/opcodes.inc). A complete list of microinstructions can be found in [src/frontend/ir/opcodes.inc](../src/frontend/ir/opcodes.inc).
The below lists some commonly used microinstructions.
### Immediate: Imm{U1,U8,U32,RegRef} ### Immediate: Imm{U1,U8,U32,RegRef}
<u1> ImmU1(u1 value) <u1> ImmU1(u1 value)

View file

@ -2,12 +2,14 @@
`HostLoc`s contain values. A `HostLoc` ("host value location") is either a host CPU register or a host spill location. `HostLoc`s contain values. A `HostLoc` ("host value location") is either a host CPU register or a host spill location.
Values once set cannot be changed. Values can however be moved by the register allocator between `HostLoc`s. This is handled by the register allocator itself and code that uses the register allocator need not and should not move values between registers. Values once set cannot be changed. Values can however be moved by the register allocator between `HostLoc`s. This is
handled by the register allocator itself and code that uses the register allocator need not and should not move values
between registers.
The register allocator is based on three concepts: `Use`, `Def` and `Scratch`. The register allocator is based on three concepts: `Use`, `Def` and `Scratch`.
* `Use`: The use of a value. * `Use`: The use of a value.
* `Def`: The definition of a value, this is the only time when a value is set. * `Define`: The definition of a value, this is the only time when a value is set.
* `Scratch`: Allocate a register that can be freely modified as one wishes. * `Scratch`: Allocate a register that can be freely modified as one wishes.
Note that `Use`ing a value decrements its `use_count` by one. When the `use_count` reaches zero the value is discarded and no longer exists. Note that `Use`ing a value decrements its `use_count` by one. When the `use_count` reaches zero the value is discarded and no longer exists.
@ -23,63 +25,52 @@ At runtime, allocate one of the registers in `desired_locations`. You are free t
### Pure `Use` ### Pure `Use`
Xbyak::Reg64 UseGpr(IR::Value use_value, HostLocList desired_locations = any_gpr); Xbyak::Reg64 UseGpr(Argument& arg);
Xbyak::Xmm UseXmm(IR::Value use_value, HostLocList desired_locations = any_xmm); Xbyak::Xmm UseXmm(Argument& arg);
OpArg UseOpArg(IR::Value use_value, HostLocList desired_locations); OpArg UseOpArg(Argument& arg);
void Use(Argument& arg, HostLoc host_loc);
At runtime, the value corresponding to `use_value` will be placed into one of the `HostLoc`s specified by `desired_locations`. The return value is the actual location. At runtime, the value corresponding to `arg` will be placed a register. The actual register is determined by
which one of the above functions is called. `UseGpr` places it in an unused GPR, `UseXmm` places it
in an unused XMM register, `UseOpArg` might be in a register or might be a memory location, and `Use` allows
you to specify a specific register (GPR or XMM) to use.
This register **must not** have it's value changed. This register **must not** have it's value changed.
* `UseGpr`: The location is a GPR.
* `UseXmm`: The location is an XMM register.
* `UseOpArg`: The location may be one of the locations specified by `desired_locations`, but may also be a host memory reference.
### `UseScratch` ### `UseScratch`
Xbyak::Reg64 UseScratchGpr(IR::Value use_value, HostLocList desired_locations = any_gpr) Xbyak::Reg64 UseScratchGpr(Argument& arg);
Xbyak::Xmm UseScratchXmm(IR::Value use_value, HostLocList desired_locations = any_xmm) Xbyak::Xmm UseScratchXmm(Argument& arg);
void UseScratch(Argument& arg, HostLoc host_loc);
At runtime, the value corresponding to `use_value` will be placed into one of the `HostLoc`s specified by `desired_locations`. The return value is the actual location. At runtime, the value corresponding to `arg` will be placed a register. The actual register is determined by
which one of the above functions is called. `UseScratchGpr` places it in an unused GPR, `UseScratchXmm` places it
in an unused XMM register, and `UseScratch` allows you to specify a specific register (GPR or XMM) to use.
You are free to modify the register. The register is discarded at the end of the allocation scope. The return value is the register allocated to you.
### `Def` You are free to modify the value in the register. The register is discarded at the end of the allocation scope.
A `Def` is the defintion of a value. This is the only time when a value may be set. ### `Define` as register
Xbyak::Xmm DefXmm(IR::Inst* def_inst, HostLocList desired_locations = any_xmm) A `Define` is the defintion of a value. This is the only time when a value may be set.
Xbyak::Reg64 DefGpr(IR::Inst* def_inst, HostLocList desired_locations = any_gpr)
By calling `DefXmm` or `DefGpr`, you are stating that you wish to define the value for `def_inst`, and you wish to write the value to one of the `HostLoc`s specified by `desired_locations`. You must write the value to the register returned. void DefineValue(IR::Inst* inst, const Xbyak::Reg& reg);
### `AddDef` By calling `DefineValue`, you are stating that you wish to define the value for `inst`, and you have written the
value to the specified register `reg`.
Adding a `Def` to an existing value. ### `Define`ing as an alias of a different value
void RegisterAddDef(IR::Inst* def_inst, const IR::Value& use_inst); Adding a `Define` to an existing value.
You are declaring that the value for `def_inst` is the same as the value for `use_inst`. No host machine instructions are emitted. void DefineValue(IR::Inst* inst, Argument& arg);
### `UseDef` You are declaring that the value for `inst` is the same as the value for `arg`. No host machine instructions are
emitted.
Xbyak::Reg64 UseDefGpr(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_gpr)
Xbyak::Xmm UseDefXmm(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_xmm)
At runtime, the value corresponding to `use_value` will be placed into one of the `HostLoc`s specified by `desired_locations`. The return value is the actual location. You must write the value correponding to `def_inst` by the end of the allocation scope.
### `UseDef` (OpArg variant)
std::tuple<OpArg, Xbyak::Reg64> UseDefOpArgGpr(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_gpr)
std::tuple<OpArg, Xbyak::Xmm> UseDefOpArgXmm(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_xmm)
These have the same semantics as `UseDefGpr` and `UseDefXmm` except `use_value` may not be present in the register, and may actually be in a host memory location.
## When to use each? ## When to use each?
The variety of different ways to `Use` and `Def` values are for performance reasons. * Prefer `Use` to `UseScratch` where possible.
* Prefer the `OpArg` variants where possible.
* `UseDef`: Instead of performing a `Use` and a `Def`, `UseDef` uses one less register in the case when this `Use` is the last `Use` of a value. * Prefer to **not** use the specific `HostLoc` variants where possible.
* `UseScratch`: Instead of performing a `Use` and a `Scratch`, `UseScratch` uses one less register in the case when this `Use` is the last `Use` of a value.
* `AddDef`: This drastically reduces the number of registers required when it can be used. It can be used when values are truncations of other values. For example, if `u8_value` contains the truncation of `u32_value`, `AddDef(u8_value, u32_value)` is a valid definition of `u8_value`.
* OpArg variants: Save host code-cache by merging memory loads into other instructions instead of the register allocator having to emit a `mov`.