jak-project/docs/progress-notes/jak1/decompilation_notes.md
Tyler Wilding b4391d0643
docs: documentation cleanup and improvements for normal users (#1618)
* docs: documentation pass

* docs: main README pass
2022-07-06 18:08:07 -04:00

277 lines
5.9 KiB
Markdown

# GOAL Operations
## `div.s`
Suspected source
```
(/ 1.0 x)
```
where `x` is in a GPR:
```
lwc1 f0, L345(fp) ;; first argument prepared first?
mtc1 f1, a0 ;; second argument prepared second?
div.s f0, f0, f1
```
Sequence
- Compile first
- First to FPR
- Compile second
- Second to FPR
## `daddu`
Used for `int` and `uint` addition.
Two element form:
```
daddu v0, a0, a1
```
is `(+ a0 a1)` - the order in the opcode matches the order in the expression.
## `daddiu` to get a symbol
```
daddiu v0, s7, #t
```
Note for `#t`: `#t` is linked when the code literally has a `#t` in it. Other cases are currently unknown.
## `dsubu`
Used for `int` and `uint` subtraction.
## `mult3` (EE `mult`)
Used for `int` multiplication.
Like `daddu` for opcode ordering:
```
mult3 v0, a0, a1
```
is `(* a0 a1)`.
## `div`
Used for `int` division.
```
div a0, a1
mflo v0
```
is `(/ a0 a1)`.
and also for `int` mod
```
div a0, a1
mfhi v0
```
## `or` used to get the value of false
```
or v0, s7, r0
```
## `or` used as a bitwise or
```
or v0, a0, a1
```
is `(logior a0 a1)`
## `and` used as a bitwise and
```
and v0, a0, a1
```
is `(logand a0 a1)`.
```
(logand #xfffffff0 (+ (ash (-> thing field) 2) 43))
```
is
```
ld v1, L346(fp) ;; first arg to the and
lhu a0, 14(a0) ;; second arg evaluation...
dsll a0, a0, 2
daddiu a0, a0, 43
and v0, v1, a0 ;; and result, first, second
```
## `nor` used as a bitwise nor
```
nor v0, a0, a1
```
is `(lognor a0 a1)`
## `xor` used as a bitwise xor
```
xor v0, a0, a1
```
is `(logxor a0 a1)`
## `nor` used as a logical not
```
nor v0, a0, r0
```
is `(lognot a0)`
# Common "Idioms"
## `ash`
Variable shift (`ash`) is an inline function
```
or v1, a0, r0
bgezl a1, L306
dsllv v0, v1, a1
dsubu a0, r0, a1
dsrav v0, v1, a0
L306:
```
## `abs` of integer
```
or v0, a0, r0
bltzl v0, L302
dsubu v0, r0, v0
L302:
```
## `min` of integers
```
or v0, a0, r0
or v1, a1, r0
slt a0, v0, v1
movz v0, v1, a0
```
## `max` of integers
```
or v0, a0, r0
or v1, a1, r0
slt a0, v0, v1
movn v0, v1, a0
```
# Others
## Integer constants that are large
A constant of `0xfffffff0` is loaded with `ld`
## Access value of symbol
Seems to always use `lw`?
# Control Flow Info
## Begin-like forms flush everything always, immediately after compiling
Example in `vector` with flushing:
```
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; .function vector3s+!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
L8:
daddiu sp, sp, -16
sd fp, 8(sp)
or fp, t9, r0
daddiu v1, fp, L109 ;; string: "Add 2 vectors3."
lwc1 f0, 0(a1)
lwc1 f1, 0(a2)
add.s f0, f0, f1
swc1 f0, 0(a0)
lwc1 f0, 4(a1)
lwc1 f1, 4(a2)
add.s f0, f0, f1
swc1 f0, 4(a0)
lwc1 f0, 8(a1)
lwc1 f1, 8(a2)
add.s f0, f0, f1
swc1 f0, 8(a0)
or v0, a0, r0
ld fp, 8(sp)
jr ra
daddiu sp, sp, 16
```
The `daddiu v1, fp, L109` loads a `string` into the `v1` register which is never used, immediately after the prologue. This will only happen if the value is flushed. This is very likely a documentation comment that accidentally got included as a string constant. It's unused, so there was likely no consumer of the string that did the `flush` - it was done by the top level evaluation.
```
(defun vector3s+! (stuff)
"Add 2 vectors3." ;; oops, a string constant instead of a comment.
... ; rest of the function
)
```
## Return-From evaluates to 0 bug
We would expect the value of `(return-from #f x)` to be nothing, as there's no possible way to use it. However, GOAL seems to have a small bug where `(return-from #f x)` always attempts to evaluate to 0. This would be like implementing it as:
```lisp
(set! return-reg return-value)
(goto end-of-function)
0 ;; oops
```
by accident.
Example in GOAL:
```
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; .function basic-type? (in gcommon)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
L285:
lwu v1, -4(a0)
lw a0, object(s7)
L286:
bne v1, a1, L287
or a2, s7, r0
; (return-from #f #t) starts here
daddiu v1, s7, #t ; compile/flush the #t
or v0, v1, r0 ; move to return register
beq r0, r0, L288 ; branch to end
sll r0, r0, 0 ; branch delay slot (usual filler)
or v1, r0, r0 ; unreachable loading of 0 into a register.
L287:
lwu v1, 4(v1)
bne v1, a0, L286
sll r0, r0, 0
or v0, s7, r0
L288:
jr ra
daddu sp, sp, r0
sll r0, r0, 0
sll r0, r0, 0
```
## Unused else case returning false in cond
From `delete!` in gcommon.
```
beq a2, a0, L222 ; (if (= a2 a0) only-one-case)
or a0, s7, r0 ; a0 is unused return value of if
lw a0, 2(a2) ; (set! (cdr v1) (cdr a2)), will evaluate to (cdr a2) which is stored in a0
sw a0, 2(v1)
L222: ; a0 = #f or a0 = (cdr a2) depending on branch taken, but it's totally unused!
or v0, a1, r0 ; return a1
jr ra
daddu sp, sp, r0
```
Also note that all cases stored their result in `a0`, even though nothing uses the result.
## Function Calls evaluate arguments in order:
```
lw t9, format(s7) ;; head of function
daddiu a0, s7, #t ;; first arg
daddiu a1, fp, L344 ;; second arg
sllv a2, gp, r0 ;; third arg
dsra32 a3, gp, 0 ;; fourth arg
pcpyud v1, gp, r0
sllv t0, v1, r0 ;; fifth arg
pcpyud v1, gp, r0
dsra32 t1, v1, 0 ;; sixth arg
por t2, gp, r0 ;; seventh arg
jalr ra, t9
sll v0, ra, 0
```
also an example of lack of common subexpression elimination on the `pcpyud v1, gp, r0`s.
### A second example with register type conversions:
```
lw t9, format(s7) ;; function
daddiu a0, s7, #t ;; first arg
daddiu a1, fp, L343 ;; second arg
lwc1 f0, 0(gp) ;; compile and flush third arg
mfc1 a2, f0 ;; move to correct reg type
jalr ra, t9
sll v0, ra, 0
```