mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
b4391d0643
* docs: documentation pass * docs: main README pass
277 lines
5.9 KiB
Markdown
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
|
|
``` |