jak-project/docs/markdown/progress-notes/decompilation_notes.md
Tyler Wilding fab73cab75
Initial spike of Github Pages project status / documentation portal (#400)
* docs: move markdown documentation to be caught by docsify

* docs: Initial spike of project status / doc portal

* docs: Add searchbar to docsify page

* docs: Remove the package-lock.json file, not critical for us

* docs: Fix search plugin link

* docs: Fix search results colors

* docs: Remove the navbar, now redundant.
2021-04-30 01:13:15 -04:00

5.9 KiB

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:

(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, r0s.

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