From 5b4b5c238e7bbdb5395270fcfcb4fb120fffc107 Mon Sep 17 00:00:00 2001 From: water Date: Sat, 28 Nov 2020 19:59:23 -0500 Subject: [PATCH 1/4] update --- decompiler/IR/BasicOpBuilder.cpp | 10 +- decompiler/IR/IR.cpp | 9 + decompiler/IR/IR.h | 4 + decompiler/IR/IR_TypeAnalysis.cpp | 35 + decompiler/config/all-types.gc | 631 ++++++++++-------- decompiler/util/DecompilerTypeSystem.cpp | 2 + goal_src/engine/math/math.gc | 153 +++++ goal_src/kernel/dgo-h.gc | 26 + goal_src/kernel/gkernel-h.gc | 6 +- .../source_templates/with_game/test-math.gc | 41 ++ test/goalc/test_with_game.cpp | 6 +- 11 files changed, 649 insertions(+), 274 deletions(-) create mode 100644 test/goalc/source_templates/with_game/test-math.gc diff --git a/decompiler/IR/BasicOpBuilder.cpp b/decompiler/IR/BasicOpBuilder.cpp index e97cc35cd9..f0115af005 100644 --- a/decompiler/IR/BasicOpBuilder.cpp +++ b/decompiler/IR/BasicOpBuilder.cpp @@ -512,8 +512,14 @@ std::shared_ptr try_ld(Instruction& instr, int idx) { // TODO SPECIAL std::shared_ptr try_lw(Instruction& instr, int idx) { - if (instr.kind == InstructionKind::LW && instr.get_src(1).is_reg(make_gpr(Reg::S7)) && - instr.get_src(0).kind == InstructionAtom::IMM_SYM) { + if (instr.kind == InstructionKind::LW && instr.get_dst(0).is_reg(make_gpr(Reg::R0)) && + instr.get_src(0).is_imm() && instr.get_src(0).get_imm() == 2 && + instr.get_src(1).is_reg(make_gpr(Reg::R0))) { + auto op = std::make_shared(); + op->reg_info_set = true; + return op; + } else if (instr.kind == InstructionKind::LW && instr.get_src(1).is_reg(make_gpr(Reg::S7)) && + instr.get_src(0).kind == InstructionAtom::IMM_SYM) { // symbol load auto dst = instr.get_dst(0).get_reg(); auto op = make_set_atomic(IR_Set_Atomic::SYM_LOAD, make_reg(dst, idx), diff --git a/decompiler/IR/IR.cpp b/decompiler/IR/IR.cpp index 09b25155ea..bbb1df8831 100644 --- a/decompiler/IR/IR.cpp +++ b/decompiler/IR/IR.cpp @@ -962,6 +962,15 @@ void IR_Suspend::get_children(std::vector>* output) const { (void)output; } +goos::Object IR_Breakpoint_Atomic::to_form(const LinkedObjectFile& file) const { + (void)file; + return pretty_print::build_list("breakpoint!"); +} + +void IR_Breakpoint_Atomic::get_children(std::vector>* output) const { + (void)output; +} + goos::Object IR_Begin::to_form(const LinkedObjectFile& file) const { std::vector list; list.push_back(pretty_print::to_symbol("begin")); diff --git a/decompiler/IR/IR.h b/decompiler/IR/IR.h index cb49eb2ea9..00a5100393 100644 --- a/decompiler/IR/IR.h +++ b/decompiler/IR/IR.h @@ -212,6 +212,9 @@ class IR_FloatMath1 : public virtual IR { std::shared_ptr arg; goos::Object to_form(const LinkedObjectFile& file) const override; void get_children(std::vector>* output) const override; + TP_Type get_expression_type(const TypeState& input, + const LinkedObjectFile& file, + DecompilerTypeSystem& dts) override; }; class IR_IntMath2 : public virtual IR { @@ -428,6 +431,7 @@ class IR_Suspend : public virtual IR, public IR_Atomic { }; class IR_Breakpoint_Atomic : public virtual IR_Atomic { + public: IR_Breakpoint_Atomic() = default; goos::Object to_form(const LinkedObjectFile& file) const override; void get_children(std::vector>* output) const override; diff --git a/decompiler/IR/IR_TypeAnalysis.cpp b/decompiler/IR/IR_TypeAnalysis.cpp index 39342c76b6..2819fa880b 100644 --- a/decompiler/IR/IR_TypeAnalysis.cpp +++ b/decompiler/IR/IR_TypeAnalysis.cpp @@ -239,6 +239,13 @@ TP_Type IR_Load::get_expression_type(const TypeState& input, return TP_Type(TypeSpec("type")); } + if (input_type.kind == TP_Type::OBJECT_OF_TYPE && ro.offset == -4 && kind == UNSIGNED && + size == 4 && ro.reg.get_kind() == Reg::GPR) { + // get type of basic likely, but misrecognized as an object. + // occurs often in typecase-like structures because other possible types are "stripped". + return TP_Type::make_type_object(input_type.as_typespec().base_type()); + } + // nice ReverseDerefInputInfo rd_in; rd_in.mem_deref = true; @@ -299,6 +306,26 @@ TP_Type IR_FloatMath2::get_expression_type(const TypeState& input, } } +TP_Type IR_FloatMath1::get_expression_type(const TypeState& input, + const LinkedObjectFile& file, + DecompilerTypeSystem& dts) { + (void)input; + (void)file; + (void)dts; + // FLOAT_TO_INT, INT_TO_FLOAT, ABS, NEG, SQRT + switch (kind) { + case FLOAT_TO_INT: + return TP_Type(TypeSpec("int")); + case INT_TO_FLOAT: + case ABS: + case NEG: + case SQRT: + return TP_Type(TypeSpec("float")); + default: + assert(false); + } +} + TP_Type IR_IntMath2::get_expression_type(const TypeState& input, const LinkedObjectFile& file, DecompilerTypeSystem& dts) { @@ -626,4 +653,12 @@ void IR_AsmOp_Atomic::propagate_types(const TypeState& input, end_types.get(dst_reg->reg) = TP_Type(TypeSpec("uint")); } } +} + +void IR_Breakpoint_Atomic::propagate_types(const TypeState& input, + const LinkedObjectFile& file, + DecompilerTypeSystem& dts) { + (void)file; + (void)dts; + end_types = input; } \ No newline at end of file diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index 25aa7fddda..9bdc4b65bf 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -7,7 +7,12 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; to move -(define-extern name= (function basic basic symbol)) +(define-extern name= (function basic basic symbol)) ;; gstring +(define-extern stop (function int)) ;; logic-target +(define-extern set-blackout-frames (function int int)) + + + ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -41,6 +46,8 @@ ;; C (define-extern dgo-load (function string kheap int int none)) +(define-extern *kernel-boot-message* symbol) +(define-extern *debug-segment* symbol) ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -146,11 +153,19 @@ ; ;; likely a bitfield type ; ) -;; gcommon +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; GSTRING-H ;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; +;; nothing! -;; gcommon - +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; GKERNEL-H ;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; ;; gkernel-h (deftype kernel-context (basic) @@ -220,9 +235,9 @@ (deftype process-tree (basic) ((name basic :offset-assert 4) (mask uint32 :offset-assert 8) - (parent pointer :offset-assert 12) - (brother pointer :offset-assert 16) - (child pointer :offset-assert 20) + (parent (pointer process-tree) :offset-assert 12) + (brother (pointer process-tree) :offset-assert 16) + (child (pointer process-tree) :offset-assert 20) (ppointer pointer :offset-assert 24) (self basic :offset-assert 28) ) @@ -401,6 +416,16 @@ :flag-assert #x900000048 ) +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; GKERNEL ;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; + +(define-extern search-process-tree (function process-tree (function symbol object) object)) +(define-extern kill-by-name (function object process-tree symbol)) +(define-extern process-by-name (function object process-tree process)) +(define-extern *active-pool* process-tree) ;; pskernel (deftype lowmemmap (structure) @@ -424,7 +449,11 @@ :flag-assert #x900000044 ) - +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; DGO-H ;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; ;; dgo-h (deftype dgo-entry (structure) @@ -450,7 +479,7 @@ ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; ENGINE TYPES ;;;;;;;;;;;;;;;;;;;;;; +;;;; MATH ;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; @@ -463,6 +492,35 @@ :flag-assert #x900000008 ) +(define-extern truncate (function float float)) +(define-extern integral? (function float symbol)) +(define-extern fractional-part (function float float)) +(define-extern log2 (function int int)) +(define-extern seek (function float float float float)) +(define-extern lerp (function float float float float)) +(define-extern lerp-scale (function float float float float float float)) + +; ;;(define-extern xyzwh object) ;; unknown type +; (define-extern rand-vu-init function) +; (define-extern rand-vu-nostep function) +; (define-extern rand-vu function) +; (define-extern lerp function) +; (define-extern lerp-scale function) +; (define-extern rand-vu-int-range function) +; (define-extern lerp-clamp function) +; ;;(define-extern random-generator object) ;; unknown type +; (define-extern integral? function) +; (define-extern rand-uint31-gen function) +; (define-extern rand-vu-int-count function) +; (define-extern fractional-part function) +; (define-extern seek function) +; ;;(define-extern xyzw object) ;; unknown type +; (define-extern rand-vu-percent? function) +; ;;(define-extern *random-generator* object) ;; unknown type +; (define-extern rand-vu-float-range function) +; ;;(define-extern rgba object) ;; unknown type +; (define-extern seekl function) + ; ;; vector-h (deftype vector (structure) ((data float 4 :offset-assert 0) @@ -1923,6 +1981,8 @@ ) ) +(define-extern *display* display) + ;;;;;;;;;;;;;;; ;; file-io ;;;;;;;;;;;;;;; @@ -2282,116 +2342,124 @@ :size-assert #x50 :flag-assert #x900000050 ) -; ;; level-h -; (deftype level (basic) -; ((name basic :offset-assert 4) -; (load-name basic :offset-assert 8) -; (nickname basic :offset-assert 12) -; (index int32 :offset-assert 16) -; (status basic :offset-assert 20) -; (other basic :offset-assert 24) -; (heap kheap :inline :offset-assert 32) -; (bsp basic :offset-assert 48) -; (art-group basic :offset-assert 52) -; (info basic :offset-assert 56) -; (texture-page UNKNOWN 9 :offset-assert 60) -; (loaded-texture-page UNKNOWN 16 :offset-assert 96) -; (loaded-texture-page-count int32 :offset-assert 160) -; (foreground-sink-group UNKNOWN 3 :offset-assert 180) -; (foreground-draw-engine UNKNOWN 3 :offset-assert 272) -; (entity basic :offset-assert 284) -; (ambient basic :offset-assert 288) -; (closest-object UNKNOWN 9 :offset-assert 292) -; (upload-size UNKNOWN 9 :offset-assert 328) -; (level-distance meters :offset-assert 364) -; (inside-sphere? basic :offset-assert 368) -; (inside-boxes? basic :offset-assert 372) -; (display? basic :offset-assert 376) -; (meta-inside? basic :offset-assert 380) -; (mood basic :offset-assert 384) -; (mood-func basic :offset-assert 388) -; (vis-bits uint32 :offset-assert 392) -; (all-visible? basic :offset-assert 396) -; (force-all-visible? basic :offset-assert 400) -; (linking basic :offset-assert 404) -; (vis-info UNKNOWN 8 :offset-assert 408) -; (vis-self-index int32 :offset-assert 440) -; (vis-adj-index int32 :offset-assert 444) -; (vis-buffer UNKNOWN 2048 :offset-assert 448) -; (mem-usage-block basic :offset-assert 2496) -; (mem-usage int32 :offset-assert 2500) -; (code-memory-start uint32 :offset-assert 2504) -; (code-memory-end uint32 :offset-assert 2508) -; (texture-mask UNKNOWN 9 :offset-assert 2512) -; (force-inside? basic :offset-assert 2548) -; ) -; :method-count-assert 29 -; :size-assert #xa30 -; :flag-assert #x1d00000a30 -; (:methods -; (dummy-9 () none 9) -; (dummy-10 () none 10) -; (dummy-11 () none 11) -; (dummy-12 () none 12) -; (dummy-13 () none 13) -; (dummy-14 () none 14) -; (dummy-15 () none 15) -; (dummy-16 () none 16) -; (dummy-17 () none 17) -; (dummy-18 () none 18) -; (dummy-19 () none 19) -; (dummy-20 () none 20) -; (dummy-21 () none 21) -; (dummy-22 () none 22) -; (dummy-23 () none 23) -; (dummy-24 () none 24) -; (dummy-25 () none 25) -; (dummy-26 () none 26) -; (dummy-27 () none 27) -; (dummy-28 () none 28) -; ) -; ) +;; level-h +(deftype level (basic) + ((name basic :offset-assert 4) + (load-name basic :offset-assert 8) + (nickname basic :offset-assert 12) + (index int32 :offset-assert 16) + (status basic :offset-assert 20) + (other basic :offset-assert 24) + (heap kheap :inline :offset-assert 32) + (bsp basic :offset-assert 48) + (art-group basic :offset-assert 52) + (info basic :offset-assert 56) + (texture-page basic 9 :offset-assert 60) + (loaded-texture-page basic 16 :offset-assert 96) + (loaded-texture-page-count int32 :offset-assert 160) + (foreground-sink-group dma-foreground-sink-group 3 :inline :offset-assert 176) ;; inline basic + (foreground-draw-engine basic 3 :offset-assert 272) + (entity basic :offset-assert 284) + (ambient basic :offset-assert 288) + (closest-object basic 9 :offset-assert 292) + (upload-size uint32 9 :offset-assert 328) + (level-distance float :offset-assert 364) ; meters + (inside-sphere? basic :offset-assert 368) + (inside-boxes? basic :offset-assert 372) + (display? basic :offset-assert 376) + (meta-inside? basic :offset-assert 380) + (mood basic :offset-assert 384) + (mood-func basic :offset-assert 388) + (vis-bits uint32 :offset-assert 392) + (all-visible? basic :offset-assert 396) + (force-all-visible? basic :offset-assert 400) + (linking basic :offset-assert 404) + (vis-info level-vis-info 8 :offset-assert 408) + (vis-self-index int32 :offset-assert 440) + (vis-adj-index int32 :offset-assert 444) + (vis-buffer uint8 2048 :offset-assert 448) + (mem-usage-block basic :offset-assert 2496) + (mem-usage int32 :offset-assert 2500) + (code-memory-start uint32 :offset-assert 2504) + (code-memory-end uint32 :offset-assert 2508) + (texture-mask uint32 9 :offset-assert 2512) + (force-inside? basic :offset-assert 2548) + (pad uint8 56) + ) + :method-count-assert 29 + :size-assert #xa30 + :flag-assert #x1d00000a30 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + (dummy-15 () none 15) + (dummy-16 () none 16) + (dummy-17 () none 17) + (dummy-18 () none 18) + (dummy-19 () none 19) + (dummy-20 () none 20) + (dummy-21 () none 21) + (dummy-22 () none 22) + (dummy-23 () none 23) + (dummy-24 () none 24) + (dummy-25 () none 25) + (dummy-26 () none 26) + (dummy-27 () none 27) + (dummy-28 () none 28) + ) + ) -; ;; level-h -; (deftype level-group (basic) -; ((length int32 :offset-assert 4) -; (entity-link entity-links :offset-assert 16) -; (border? basic :offset-assert 20) -; (vis? basic :offset-assert 24) -; (want-level basic :offset-assert 28) -; (receiving-level basic :offset-assert 32) -; (load-commands basic :offset-assert 36) -; (play? basic :offset-assert 40) -; (level UNKNOWN 3 :offset-assert 100) -; (data UNKNOWN 3 :offset-assert 100) -; (level0 level :inline :offset-assert 100) -; (level1 level :inline :offset-assert 2708) -; (level-default level :inline :offset-assert 5316) -; ) -; :method-count-assert 27 -; :size-assert #x1ef4 -; :flag-assert #x1b00001ef4 -; (:methods -; (dummy-9 () none 9) -; (dummy-10 () none 10) -; (dummy-11 () none 11) -; (dummy-12 () none 12) -; (dummy-13 () none 13) -; (dummy-14 () none 14) -; (dummy-15 () none 15) -; (dummy-16 () none 16) -; (dummy-17 () none 17) -; (dummy-18 () none 18) -; (dummy-19 () none 19) -; (dummy-20 () none 20) -; (dummy-21 () none 21) -; (dummy-22 () none 22) -; (dummy-23 () none 23) -; (dummy-24 () none 24) -; (dummy-25 () none 25) -; (dummy-26 () none 26) -; ) -; ) + + + +(declare-type entity-links structure) +;; level-h +(deftype level-group (basic) + ((length int32 :offset-assert 4) + (entity-link entity-links :offset 16) ;; not sure what's going on here + (border? basic :offset-assert 20) + (vis? basic :offset-assert 24) + (want-level basic :offset-assert 28) + (receiving-level basic :offset-assert 32) + (load-commands basic :offset-assert 36) + (play? basic :offset-assert 40) + ; (level UNKNOWN 3 :offset-assert 100) + ; (data UNKNOWN 3 :offset-assert 100) + (level0 level :inline :offset 96) ;; inline basic + (level1 level :inline :offset-assert 2704) ;; inline basic + (level-default level :inline :offset-assert 5312) ;; inline basic + (pad uint32) + ) + :method-count-assert 27 + :size-assert #x1ef4 + :flag-assert #x1b00001ef4 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + (dummy-15 () none 15) + (dummy-16 () none 16) + (dummy-17 () none 17) + (dummy-18 () none 18) + (dummy-19 () none 19) + (dummy-20 () none 20) + (dummy-21 () none 21) + (dummy-22 () none 22) + (dummy-23 () none 23) + (dummy-24 () none 24) + (dummy-25 () none 25) + (dummy-26 () none 26) + ) + ) + +(define-extern *level* level-group) ;;;;;;;;;;;;;;;;;; ;; math-camera-h @@ -2692,36 +2760,75 @@ ;; settings-h ;;;;;;;;;;;;;;;; -; ;; settings-h -; (deftype setting-data (structure) -; () -; :method-count-assert 10 -; :size-assert #xc4 -; :flag-assert #xa000000c4 -; ;; too many basic blocks -; (:methods -; (dummy-9 () none 9) -; ) -; ) +;; settings-h +;; was manually done +(deftype setting-data (structure) + ((border-mode basic :offset-assert 0) + (sfx-volume float :offset-assert 4) + (music-volume float :offset-assert 8) + (dialog-volume float :offset-assert 12) + (process-mask uint32 :offset-assert 16) + (common-page int32 :offset-assert 20) + (language int64 :offset-assert 24) + (screenx int32 :offset-assert 32) + (screeny int32 :offset-assert 36) + (vibration basic :offset-assert 40) + (play-hints basic :offset-assert 44) + (movie basic :offset-assert 48) ;; special print + (talking basic :offset-assert 52) ;; special print + (spooling basic :offset-assert 56) ;; special print + (hint basic :offset-assert 60) ;; special print + (ambient basic :offset-assert 64) ;; special print + (video-mode basic :offset-assert 68) + (aspect-ratio basic :offset-assert 72) + (sound-flava uint8 :offset-assert 76) + (auto-save basic :offset-assert 80) + (music-volume-movie float :offset-assert 84) + (sfx-volume-movie float :offset-assert 88) + (music basic :offset-assert 92) + (bg-r float :offset-assert 96) + (bg-g float :offset-assert 100) + (bg-b float :offset-assert 104) + (bg-a float :offset-assert 108) + (bg-a-speed float :offset-assert 112) + (bg-a-force float :offset-assert 116) + (allow-progress basic :offset-assert 120) + (allow-pause basic :offset-assert 124) + (sound-flava-priority float :offset-assert 128) + (ocean-off basic :offset-assert 132) + (allow-look-around basic :offset-assert 136) + (ambient-volume float :offset-assert 140) + (ambient-volume-movie float :offset-assert 144) + (dialog-volume-hint float :offset-assert 148) + (dummy uint32 11 :offset-assert 152) + + ) + :method-count-assert 10 + :size-assert #xc4 + :flag-assert #xa000000c4 + (:methods + (dummy-9 () none 9) + ) + ) -; ;; settings-h -; (deftype setting-control (basic) -; ((current setting-data :inline :offset-assert 16) -; (target setting-data :inline :offset-assert 224) -; (default setting-data :inline :offset-assert 432) -; (engine basic :offset-assert 628) -; ) -; :method-count-assert 14 -; :size-assert #x278 -; :flag-assert #xe00000278 -; (:methods -; (dummy-9 () none 9) -; (dummy-10 () none 10) -; (dummy-11 () none 11) -; (dummy-12 () none 12) -; (dummy-13 () none 13) -; ) -; ) +;; settings-h +(deftype setting-control (basic) + ((current setting-data :inline :offset-assert 16) + (target setting-data :inline :offset-assert 224) + (default setting-data :inline :offset-assert 432) + (engine basic :offset-assert 628) + ) + :method-count-assert 14 + :size-assert #x278 + :flag-assert #xe00000278 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + ) + ) (deftype scf-time (structure) ((stat uint8 :offset-assert 0) @@ -2738,6 +2845,7 @@ :flag-assert #x900000008 ) +(define-extern *setting-control* setting-control) ;;;;;;;;;;;;;;;;; ;; capture @@ -2833,6 +2941,8 @@ ) ) +(define-extern *dproc* process) + ;;;;;;;;;;;;;; ;; mspace-h ;;;;;;;;;;;;;; @@ -4219,6 +4329,8 @@ ;; inherited inpspect of process ) +(define-extern *time-of-day-proc* time-of-day-proc) + ;; time-of-day-h (deftype time-of-day-palette (basic) ((width int32 :offset-assert 4) @@ -5609,37 +5721,40 @@ (force-vis? basic :offset-assert 8) (force-inside? basic :offset-assert 12) ) + :pack-me :method-count-assert 9 :size-assert #x10 :flag-assert #x900000010 ) ; ;; game-info-h -; (deftype load-state (basic) -; ((want UNKNOWN 2 :offset-assert 4) -; (vis-nick basic :offset-assert 36) -; (command-list basic :offset-assert 40) -; (object-name UNKNOWN 256 :offset-assert 44) -; (object-status UNKNOWN 256 :offset-assert 1068) -; ) -; :method-count-assert 21 -; :size-assert #x82c -; :flag-assert #x150000082c -; (:methods -; (dummy-9 () none 9) -; (dummy-10 () none 10) -; (dummy-11 () none 11) -; (dummy-12 () none 12) -; (dummy-13 () none 13) -; (dummy-14 () none 14) -; (dummy-15 () none 15) -; (dummy-16 () none 16) -; (dummy-17 () none 17) -; (dummy-18 () none 18) -; (dummy-19 () none 19) -; (dummy-20 () none 20) -; ) -; ) +(deftype load-state (basic) + ((want level-buffer-state 2 :inline :offset-assert 4) + (vis-nick basic :offset-assert 36) + (command-list basic :offset-assert 40) + (object-name basic 256 :offset-assert 44) + (object-status basic 256 :offset-assert 1068) + ) + :method-count-assert 21 + :size-assert #x82c + :flag-assert #x150000082c + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + (dummy-15 () none 15) + (dummy-16 () none 16) + (dummy-17 () none 17) + (dummy-18 () none 18) + (dummy-19 () none 19) + (dummy-20 () none 20) + ) + ) + +(define-extern *load-state* load-state) ;; game-info-h (deftype continue-point (basic) @@ -5740,6 +5855,8 @@ ) ) +(define-extern *game-info* game-info) + ;; wind-h (deftype wind-vector (structure) ((wind-pos vector2w :inline :offset-assert 0) @@ -8062,30 +8179,30 @@ ;; target-h (deftype target (process-drawable) - ((~Tcontrol basic :offset 112) - (~Tskel2 basic :offset-assert 176) - (~Tracer basic :offset-assert 180) - (~Tgame basic :offset-assert 184) - (~Tneck basic :offset-assert 188) - (~Tstate-hook-time uint64 :offset-assert 192) - (~Tstate-hook basic :offset-assert 200) - (~Tcam-user-mode basic :offset-assert 204) - (~Tsidekick uint32 :offset-assert 208) - (~Tmanipy uint32 :offset-assert 212) - (~Tattack-info attack-info :inline :offset-assert 224) - (~Tattack-info-rec attack-info :inline :offset-assert 336) - (~Tanim-seed uint64 :offset-assert 440) - (~Talt-cam-pos vector :inline :offset-assert 448) - (~Tsnowball basic :offset-assert 464) - (~Ttube basic :offset-assert 468) - (~Tflut basic :offset-assert 472) - (~Tcurrent-level basic :offset-assert 476) - (~Tsaved-pos transformq :inline :offset-assert 480) - (~Tsaved-owner uint64 :offset-assert 528) - (~Talt-neck-pos vector :inline :offset-assert 544) - (~Tfp-hud uint64 :offset-assert 560) - (~Tno-load-wait uint64 :offset-assert 568) - (~Tno-look-around-wait uint64 :offset-assert 576) + ((control basic :offset 112) + (skel2 basic :offset-assert 176) + (racer basic :offset-assert 180) + (game basic :offset-assert 184) + (neck basic :offset-assert 188) + (state-hook-time uint64 :offset-assert 192) + (state-hook basic :offset-assert 200) + (cam-user-mode basic :offset-assert 204) + (sidekick uint32 :offset-assert 208) + (manipy uint32 :offset-assert 212) + (attack-info attack-info :inline :offset-assert 224) + (attack-info-rec attack-info :inline :offset-assert 336) + (anim-seed uint64 :offset-assert 440) + (alt-cam-pos vector :inline :offset-assert 448) + (snowball basic :offset-assert 464) + (tube basic :offset-assert 468) + (flut basic :offset-assert 472) + (current-level basic :offset-assert 476) + (saved-pos transformq :inline :offset-assert 480) + (saved-owner uint64 :offset-assert 528) + (alt-neck-pos vector :inline :offset-assert 544) + (fp-hud uint64 :offset-assert 560) + (no-load-wait uint64 :offset-assert 568) + (no-look-around-wait uint64 :offset-assert 576) ) :heap-base #x1e0 :method-count-assert 21 @@ -8097,6 +8214,8 @@ ) ) +(define-extern *target* target) + ;; target-h (deftype sidekick (process-drawable) ((~Tcontrol basic :offset 112) @@ -9320,52 +9439,53 @@ ; :flag-assert #x9000040b0 ; ) -; ;; entity-h -; (deftype entity-perm (structure) -; ((user-object UNKNOWN 2 :offset-assert 0) -; (user-uint64 uint64 :offset-assert 0) -; (user-float UNKNOWN 2 :offset-assert 0) -; (user-int32 UNKNOWN 2 :offset-assert 0) -; (user-uint32 UNKNOWN 2 :offset-assert 0) -; (user-int16 UNKNOWN 4 :offset-assert 0) -; (user-uint16 UNKNOWN 4 :offset-assert 0) -; (user-int8 UNKNOWN 8 :offset-assert 0) -; (user-uint8 UNKNOWN 8 :offset-assert 0) -; (status uint16 :offset-assert 8) -; (dummy UNKNOWN 1 :offset-assert 10) -; (task uint8 :offset-assert 11) -; (aid uint32 :offset-assert 12) -; (quad uint128 :offset-assert 0) -; ) -; :method-count-assert 10 -; :size-assert #x10 -; :flag-assert #xa00000010 -; (:methods -; (dummy-9 () none 9) -; ) -; ) +;; entity-h +(deftype entity-perm (structure) + ((user-object object 2 :offset-assert 0) + (user-uint64 uint64 :offset 0) + (user-float float 2 :offset 0) + (user-int32 int32 2 :offset 0) + (user-uint32 uint32 2 :offset 0) + (user-int16 int16 4 :offset 0) + (user-uint16 uint16 4 :offset 0) + (user-int8 int8 8 :offset 0) + (user-uint8 uint8 8 :offset 0) + (status uint16 :offset-assert 8) + (dummy uint8 1 :offset-assert 10) + (task uint8 :offset-assert 11) + (aid uint32 :offset-assert 12) + (quad uint128 :offset 0) + ) + :pack-me + :method-count-assert 10 + :size-assert #x10 + :flag-assert #xa00000010 + (:methods + (dummy-9 () none 9) + ) + ) -; ;; entity-h -; (deftype entity-links (structure) -; ((prev-link entity-links :offset-assert 0) -; (next-link entity-links :offset-assert 4) -; (entity basic :offset-assert 8) -; (process basic :offset-assert 12) -; (level basic :offset-assert 16) -; (vis-id int32 :offset-assert 20) -; (trans vector :inline :offset-assert 32) -; (perm entity-perm :inline :offset-assert 48) -; (status uint16 :offset-assert 56) -; (aid uint32 :offset-assert 60) -; (task uint8 :offset-assert 59) -; ) -; :method-count-assert 10 -; :size-assert #x40 -; :flag-assert #xa00000040 -; (:methods -; (dummy-9 () none 9) -; ) -; ) +;; entity-h +(deftype entity-links (structure) + ((prev-link entity-links :offset-assert 0) + (next-link entity-links :offset-assert 4) + (entity basic :offset-assert 8) + (process basic :offset-assert 12) + (level basic :offset-assert 16) + (vis-id int32 :offset-assert 20) + (trans vector :inline :offset-assert 32) + (perm entity-perm :inline :offset-assert 48) + (status uint16 :offset 56) ;; overlays + (aid uint32 :offset 60) + (task uint8 :offset 59) + ) + :method-count-assert 10 + :size-assert #x40 + :flag-assert #xa00000040 + (:methods + (dummy-9 () none 9) + ) + ) ; ;; entity-h ; (deftype entity-perm-array (inline-array-class) @@ -30996,7 +31116,6 @@ (define-extern throw function) ;;(define-extern *nk-dead-pool* object) ;; unknown type (define-extern kill-by-type function) -(define-extern search-process-tree function) (define-extern change-to-last-brother function) ;;(define-extern *pickup-dead-pool* object) ;; unknown type @@ -31023,7 +31142,6 @@ (define-extern entity-deactivate-handler function) ;;(define-extern *null-process* object) ;; unknown type (define-extern inspect-process-heap function) -(define-extern process-by-name function) ;;(define-extern *target-pool* object) ;; unknown type ;;(define-extern *global-search-name* object) ;; unknown type ;;(define-extern *kernel-boot-mode* object) ;; unknown type @@ -31040,7 +31158,6 @@ ;;(define-extern *irx-version* object) ;; unknown type ;;(define-extern *pause-lock* object) ;; unknown type ;;(define-extern display-pool object) ;; unknown type -(define-extern kill-by-name function) ;;(define-extern *target-dead-pool* object) ;; unknown type ;;(define-extern *vis-boot* object) ;; unknown type ;;(define-extern *debug-dead-pool* object) ;; unknown type @@ -31140,28 +31257,7 @@ (define-extern inherit-state function) ;;(define-extern time-frame object) ;; unknown type ;;(define-extern part-id object) ;; unknown type -(define-extern log2 function) -;;(define-extern xyzwh object) ;; unknown type -(define-extern rand-vu-init function) -(define-extern truncate function) -(define-extern rand-vu-nostep function) -(define-extern rand-vu function) -(define-extern lerp function) -(define-extern lerp-scale function) -(define-extern rand-vu-int-range function) -(define-extern lerp-clamp function) -;;(define-extern random-generator object) ;; unknown type -(define-extern integral? function) -(define-extern rand-uint31-gen function) -(define-extern rand-vu-int-count function) -(define-extern fractional-part function) -(define-extern seek function) -;;(define-extern xyzw object) ;; unknown type -(define-extern rand-vu-percent? function) -;;(define-extern *random-generator* object) ;; unknown type -(define-extern rand-vu-float-range function) -;;(define-extern rgba object) ;; unknown type -(define-extern seekl function) + ;;(define-extern vector2h object) ;; unknown type ;;(define-extern vector4s-3 object) ;; unknown type ;;(define-extern *identity-vector* object) ;; unknown type @@ -33506,12 +33602,11 @@ ;;(define-extern health object) ;; unknown type (define-extern set-master-mode function) ;;(define-extern allow-progress object) ;; unknown type -(define-extern set-blackout-frames function) +; (define-extern set-blackout-frames function) (define-extern reset-actors function) (define-extern get-task-control function) ;;(define-extern *kernel-boot-message* object) ;; unknown type (define-extern play (function none)) -(define-extern stop function) (define-extern auto-save-check function) ;;(define-extern auto-save object) ;; unknown type ;;(define-extern game-save-tag object) ;; unknown type diff --git a/decompiler/util/DecompilerTypeSystem.cpp b/decompiler/util/DecompilerTypeSystem.cpp index f28326e3d8..35e6a3c534 100644 --- a/decompiler/util/DecompilerTypeSystem.cpp +++ b/decompiler/util/DecompilerTypeSystem.cpp @@ -70,6 +70,8 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector& file_ } if (type_kind.as_symbol()->name == "basic") { ts.forward_declare_type_as_basic(type_name.as_symbol()->name); + } else if (type_kind.as_symbol()->name == "structure") { + ts.forward_declare_type_as_structure(type_name.as_symbol()->name); } else { throw std::runtime_error("bad declare-type"); } diff --git a/goal_src/engine/math/math.gc b/goal_src/engine/math/math.gc index 32d3cdf6cc..449497474d 100644 --- a/goal_src/engine/math/math.gc +++ b/goal_src/engine/math/math.gc @@ -5,3 +5,156 @@ ;; name in dgo: math ;; dgos: GAME, ENGINE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; float macros +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; at some point, these could be more optimized. + +(defmacro fabs (x) + ;"Floating point absolute value. On MIPS there is a abs.s instruction. + ; In the future we could consider optimizing this more for x86, but this is good enough." + `(if (< ,x 0) + (- ,x) + ,x) + ) + +(defmacro fmin (x y) + `(if (< ,x ,y) + ,x + ,y) + ) + +(defmacro fmax (x y) + `(if (> ,x ,y) + ,x + ,y) + ) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; float utility +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun truncate ((x float)) + "Truncate a floating point number to an integer value. + Positive values round down and negative values round up." + (declare (inline)) + (the float (the int x)) + ) + +(defun integral? ((x float)) + "Is a float an exact integer?" + (= (truncate x) x) + ) + +(defun fractional-part ((x float)) + "Get the fractional part of a float" + (- x (truncate x)) + ) + +;; todo, rgba, xyzw, xyzwh + +(defun log2 ((x int)) + "Straight out of Bit Twiddling Hacks graphics.stanford.edu" + (- (sarv (the-as integer (the float x)) 23) 127) + ) + +(defun seek ((x float) (target float) (diff float)) + "Move x toward target by at most diff, with floats" + (let ((err (- target x))) + ;; if we can get there all at once + (if (<= (fabs err) diff) + (return-from #f target) + ) + + (if (>= err 0) + (+ x diff) + (- x diff) + ) + ) + ) + +(defun lerp ((minimum float) (maximum float) (amount float)) + "Interpolate between minimum and maximum. The output is not clamped." + (+ minimum (* amount (- maximum minimum))) + ) + +(defun lerp-scale ((min-out float) (max-out float) + (in float) (min-in float) (max-in float)) + "Interpolate from [min-in, max-in] to [min-out, max-out]. + If the output is out of range, it will be clamped. + This is not a great implementation." + (let ((scale (fmax 0.0 (fmin 1.0 (/ (- in min-in) (- max-in min-in)))))) + (+ (* (- 1.0 scale) min-out) + (* scale max-out) + ) + ) + ) + +(defun lerp-clamp ((minimum float) (maximum float) (amount float)) + "Interpolate between minimum and maximum. Clamp output. + For some reason, the interpolate here is done in a less efficient way than lerp." + (if (<= amount 0.0) + (return-from #f minimum) + ) + + (if (>= amount 1.0) + (return-from #f maximum) + ) + ;; lerp computes this part, but more efficiently + (+ (* (- 1.0 amount) minimum) + (* amount maximum) + ) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; integer utility +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun seekl ((x int) (target int) (diff int)) + "Move x toward a target by at most diff, with integers" + (let ((err (- target x))) + (if (< (abs err) diff) + (return-from #f target) + ) + + (if (>= err 0) + (+ x diff) + (- x diff) + ) + ) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; random vu +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; terrible random integer generator +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(deftype random-generator (basic) + ((seed uint32 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(define *random-generator* (new 'global 'random-generator)) +;; I wonder who wrote this code. +(set! (-> *random-generator* seed) #x666EDD1E) + +(defun rand-uint31-gen ((gen random-generator)) + "Generate a supposedly random integer" + (let ((result (-> gen seed))) + ;; addiu v1, r0, 16807 + (+! result 16807) + ;; to 32 bit, doing sign extension. + (set! result (shlv result 32)) + (set! result (sarv result 32)) + ) + ) \ No newline at end of file diff --git a/goal_src/kernel/dgo-h.gc b/goal_src/kernel/dgo-h.gc index 16372eff4d..92a38100d6 100644 --- a/goal_src/kernel/dgo-h.gc +++ b/goal_src/kernel/dgo-h.gc @@ -5,3 +5,29 @@ ;; name in dgo: dgo-h ;; dgos: KERNEL +;; I suspect that these are unused, and were for an older version of DGO. +;; All DGO stuff is handled on the IOP. + +;; seems to be unused, and not accurate to a DGO file anyway. +;; all DGO stuff is handled on the IOP. +(deftype dgo-entry (structure) + ((offset uint32 :offset-assert 0) + (length uint32 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +;; seems to be unused, and not accurate to a DGO file anyway. +;; all DGO stuff is handled on the IOP. +(deftype dgo-file (basic) + ((num-go-files uint32 :offset-assert 4) + (total-length uint32 :offset-assert 8) + (rsvd uint32 :offset-assert 12) + (data uint8 :dynamic :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) diff --git a/goal_src/kernel/gkernel-h.gc b/goal_src/kernel/gkernel-h.gc index d717fc4192..dd9d8991d2 100644 --- a/goal_src/kernel/gkernel-h.gc +++ b/goal_src/kernel/gkernel-h.gc @@ -162,9 +162,9 @@ (deftype process-tree (basic) ((name basic :offset-assert 4) (mask uint32 :offset-assert 8) - (parent pointer :offset-assert 12) - (brother pointer :offset-assert 16) - (child pointer :offset-assert 20) + (parent (pointer process-tree) :offset-assert 12) + (brother (pointer process-tree) :offset-assert 16) + (child (pointer process-tree) :offset-assert 20) (ppointer pointer :offset-assert 24) (self basic :offset-assert 28) ) diff --git a/test/goalc/source_templates/with_game/test-math.gc b/test/goalc/source_templates/with_game/test-math.gc new file mode 100644 index 0000000000..de9f5e59c8 --- /dev/null +++ b/test/goalc/source_templates/with_game/test-math.gc @@ -0,0 +1,41 @@ +(start-test "math") + +(defmacro close-enough (x y) + `(< (fabs (- ,x ,y)) 0.00001) + ) + +(expect-eq #f (= 1.0 1.000001)) +(expect-eq #t (close-enough 1.0 1.000001)) +(expect-eq #t (= 1.0 1.00000)) +(expect-eq #t (close-enough 1.0 1.00000)) +(expect-eq #f (= 1.0 1.001)) +(expect-eq #f (close-enough 1.0 1.001)) +(expect-eq 1.0 (truncate 1.2)) +(expect-eq 1.0 (truncate 1.998)) +(expect-eq -1.0 (truncate -1.9)) +(expect-eq #t (integral? -3.0)) +(expect-eq #f (integral? 2.3)) +(expect-eq #t (close-enough -0.1 (fractional-part -1.1))) +(expect-eq #t (close-enough 0.3 (fractional-part 4.3))) +(expect-eq 7 (log2 128)) +(expect-eq 1.2 (fabs 1.2)) +(expect-eq 23.2 (fabs -23.2)) +(expect-eq 7.0 (seek 6. 12. 1.)) +(expect-eq 12.0 (seek 6. 12. 100.)) +(expect-eq 4.0 (seek 6.0 0.0 2.0)) +(expect-eq 0.0 (seek 6.0 0.0 200.0)) +(expect-eq 7.5 (lerp 5.0 10.0 0.5)) +(expect-eq #t (close-enough 1.5 (lerp-scale 1.0 2.0 7.5 5.0 10.0))) +(expect-eq #t (close-enough 2.0 (lerp-scale 1.0 2.0 999.0 5.0 10.0))) +(expect-eq #t (close-enough 1.0 (lerp-scale 1.0 2.0 -999.0 5.0 10.0))) + +(expect-eq #t (close-enough 2.3 (lerp-clamp 1.7 2.3 100.0))) +(expect-eq #t (close-enough 1.7 (lerp-clamp 1.7 2.3 -100.0))) +(expect-eq #t (close-enough 7.5 (lerp-clamp 5.0 10.0 0.5))) + +(expect-eq 3 (seekl 1 5 2)) +(expect-eq 5 (seekl 1 5 200)) +(expect-eq 1 (seekl 12 0 11)) +(expect-eq -2 (seekl 12 -2 1000)) + +(finish-test) \ No newline at end of file diff --git a/test/goalc/test_with_game.cpp b/test/goalc/test_with_game.cpp index 1d491b055b..9f4a980c4e 100644 --- a/test/goalc/test_with_game.cpp +++ b/test/goalc/test_with_game.cpp @@ -302,9 +302,13 @@ TEST_F(WithGameTests, GameCount) { get_test_pass_string("game-count", 4)); } +TEST_F(WithGameTests, Math) { + runner.run_static_test(env, testCategory, "test-math.gc", get_test_pass_string("math", 31)); +} + TEST(TypeConsistency, TypeConsistency) { Compiler compiler; compiler.enable_throw_on_redefines(); compiler.run_test_no_load("test/goalc/source_templates/with_game/test-build-game.gc"); compiler.run_test_no_load("decompiler/config/all-types.gc"); -} \ No newline at end of file +} From dc32b5921724c650b022e5ca84a51ca0d5aeb8e0 Mon Sep 17 00:00:00 2001 From: water Date: Sat, 28 Nov 2020 20:53:13 -0500 Subject: [PATCH 2/4] update --- doc/code_status.md | 50 +++++++++++++++++++ goal_src/engine/geometry/bounding-box-h.gc | 40 +++++++++++++++ goal_src/engine/math/math.gc | 33 +++++++++--- goal_src/engine/math/matrix-h.gc | 42 ++++++++++++++++ goal_src/engine/math/quaternion-h.gc | 15 ++++++ goalc/compiler/Compiler.h | 2 +- goalc/compiler/IR.cpp | 8 ++- goalc/compiler/IR.h | 1 + goalc/compiler/compilation/Atoms.cpp | 1 + goalc/compiler/compilation/Math.cpp | 36 +++++++++++++ goalc/emitter/IGen.h | 13 +++++ .../arithmetic/multiply32.static.gc | 2 + .../arithmetic/multiply64.static.gc | 2 + test/goalc/test_arithmetic.cpp | 5 ++ 14 files changed, 240 insertions(+), 10 deletions(-) create mode 100644 doc/code_status.md create mode 100644 test/goalc/source_templates/arithmetic/multiply32.static.gc create mode 100644 test/goalc/source_templates/arithmetic/multiply64.static.gc diff --git a/doc/code_status.md b/doc/code_status.md new file mode 100644 index 0000000000..39f5ce82dd --- /dev/null +++ b/doc/code_status.md @@ -0,0 +1,50 @@ +## gcommon.gc +Missing stuff. + +## gstring-h.gc +Empty file. + +## gkernel-h.gc +Likely missing some macros. Missing `handle`, a child type of integer. + +## gkernel.gc +Missing lots of stuff. Will need x86-64 inline assembly and some tweaking for x86. + +## pskernel.gc +Possibly can be entirely left out. Seems to be mostly unused, or only used for PS2 debugging? + +## gstring.gc +Missing lots + +## dgo-h.gc +Done! + +## gstate.gc +Not started, probably needs state support in the compiler + +## types.gc +Needs child types of integer + +## vu-macros.gc +Empty. + +## math.gc +Has a unit test for a lot of functions. +rand-vu-init, rand-vu, rand-vu-nostep, rand-vu-float-range, rand-vu-percent?, rand-vu-int-range, rand-vu-int-count aren't implemented + +rand-uint31-gen might be wrong. + +## vector.gc +Partially done + +## gravity-h.gc +Empty file + +## bounding-box-h.gc +Just types. Done! + +## matrix-h.gc +Types and one function. Done, but the matrix-copy! function isn't that efficient. + +## quaternion-h.gc +Done! \ No newline at end of file diff --git a/goal_src/engine/geometry/bounding-box-h.gc b/goal_src/engine/geometry/bounding-box-h.gc index 4c5d79022c..f2304ba51b 100644 --- a/goal_src/engine/geometry/bounding-box-h.gc +++ b/goal_src/engine/geometry/bounding-box-h.gc @@ -5,3 +5,43 @@ ;; name in dgo: bounding-box-h ;; dgos: GAME, ENGINE +;; Types related to bounding boxes. + +;; floating point bounding box. +(deftype bounding-box (structure) + ((min vector :inline :offset-assert 0) + (max vector :inline :offset-assert 16) + ) + :method-count-assert 16 + :size-assert #x20 + :flag-assert #x1000000020 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + (dummy-15 () none 15) + ) + ) + +;; integer (word) bounding box. +(deftype bounding-box4w (structure) + ((min vector4w :inline :offset-assert 0) + (max vector4w :inline :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +;; bounding both that has both a box and box4w. +(deftype bounding-box-both (structure) + ((box bounding-box :inline :offset-assert 0) + (box4w bounding-box4w :inline :offset-assert 32) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) \ No newline at end of file diff --git a/goal_src/engine/math/math.gc b/goal_src/engine/math/math.gc index 449497474d..ebcc28697f 100644 --- a/goal_src/engine/math/math.gc +++ b/goal_src/engine/math/math.gc @@ -148,13 +148,32 @@ ;; I wonder who wrote this code. (set! (-> *random-generator* seed) #x666EDD1E) +(defmacro sext32-64 (x) + `(sarv (shlv ,x 32) 32) + ) + (defun rand-uint31-gen ((gen random-generator)) - "Generate a supposedly random integer" - (let ((result (-> gen seed))) - ;; addiu v1, r0, 16807 - (+! result 16807) - ;; to 32 bit, doing sign extension. - (set! result (shlv result 32)) - (set! result (sarv result 32)) + "Generate a supposedly random integer. + Note, this might not quite be right. + But the highest bit is always zero, like it says + and it looks kinda random to me." + (let* ((sd (-> gen seed)) + ;; addiu v1, r0, 16807 + ;; mult3 v0, v1, a1 + (prod (imul64 16807 sd)) + ;; mfhi v1 + (hi (shrv prod 32)) ;; sign extend this? + (lo (sarv (shlv prod 32) 32)) + ;; daddu v1, v1, v1 + (v1 (+ hi hi)) + ;; srl a1, v0, 31 + (a1 (logand #xffffffff (shrv lo 31))) + ;; or v1, v1, a1 + ;; daddu v0, v0 v1 + (result (+ lo (logior v1 a1))) + ) + (set! result (shrv (logand #xffffffff (shlv result 1)) 1)) + (set! (-> gen seed) result) + result ) ) \ No newline at end of file diff --git a/goal_src/engine/math/matrix-h.gc b/goal_src/engine/math/matrix-h.gc index ca0f65a3cb..d60b9fe038 100644 --- a/goal_src/engine/math/matrix-h.gc +++ b/goal_src/engine/math/matrix-h.gc @@ -5,3 +5,45 @@ ;; name in dgo: matrix-h ;; dgos: GAME, ENGINE +;; matrix-h +(deftype matrix (structure) + ((data float 16 :offset-assert 0) + (vector vector 4 :offset 0) + (quad uint128 4 :offset 0) + ) + :method-count-assert 10 + :size-assert #x40 + :flag-assert #xa00000040 + (:methods + (dummy-9 () none 9) + ) + ) + +(deftype matrix3 (structure) + ((data float 12 :offset-assert 0) + (vector vector 3 :offset 0) + (quad uint128 3 :offset 0) + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + +;; guess on signs here +(deftype matrix4h (structure) + ((data int16 16 :offset-assert 0) + (vector4h vector4h 4 :offset 0) + (long int64 4 :offset 0) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(defun matrix-copy! ((dst matrix) (src matrix)) + "Copy src to dst." + ;; actual implementation is in assembly, unrolled quad copies, loads/stores spaced out. + (dotimes (i 16 dst) + (set! (-> dst data i) (-> src data i)) + ) + ) \ No newline at end of file diff --git a/goal_src/engine/math/quaternion-h.gc b/goal_src/engine/math/quaternion-h.gc index d8dd56d83f..d2a8689f34 100644 --- a/goal_src/engine/math/quaternion-h.gc +++ b/goal_src/engine/math/quaternion-h.gc @@ -5,3 +5,18 @@ ;; name in dgo: quaternion-h ;; dgos: GAME, ENGINE +(deftype quaternion (structure) + ((data float 4 :offset-assert 0) + (x float :offset 0) + (y float :offset 4) + (z float :offset 8) + (w float :offset 12) + (vec vector :inline :offset 0) + (quad uint128 :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(define *unity-quaternion* (new 'static 'quaternion :x 0.0 :y 0.0 :z 0.0 :w 1.0)) \ No newline at end of file diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index b376389a55..8689c8b37c 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -218,9 +218,9 @@ class Compiler { // Math Val* compile_add(const goos::Object& form, const goos::Object& rest, Env* env); - Val* compile_sub(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_mul(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_imul64(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_div(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_shlv(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_sarv(const goos::Object& form, const goos::Object& rest, Env* env); diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index bb59169ee8..1aba46ddb4 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -400,6 +400,8 @@ std::string IR_IntegerMath::print() { return fmt::format("subi {}, {}", m_dest->print(), m_arg->print()); case IntegerMathKind::IMUL_32: return fmt::format("imul {}, {}", m_dest->print(), m_arg->print()); + case IntegerMathKind::IMUL_64: + return fmt::format("imul64 {}, {}", m_dest->print(), m_arg->print()); case IntegerMathKind::IDIV_32: return fmt::format("idiv {}, {}", m_dest->print(), m_arg->print()); case IntegerMathKind::IMOD_32: @@ -483,13 +485,15 @@ void IR_IntegerMath::do_codegen(emitter::ObjectGenerator* gen, gen->add_instr(IGen::imul_gpr32_gpr32(dr, get_reg(m_arg, allocs, irec)), irec); gen->add_instr(IGen::movsx_r64_r32(dr, dr), irec); } break; - + case IntegerMathKind::IMUL_64: { + auto dr = get_reg(m_dest, allocs, irec); + gen->add_instr(IGen::imul_gpr64_gpr64(dr, get_reg(m_arg, allocs, irec)), irec); + } break; case IntegerMathKind::IDIV_32: { gen->add_instr(IGen::cdq(), irec); gen->add_instr(IGen::idiv_gpr32(get_reg(m_arg, allocs, irec)), irec); gen->add_instr(IGen::movsx_r64_r32(get_reg(m_dest, allocs, irec), emitter::RAX), irec); } break; - case IntegerMathKind::IMOD_32: { gen->add_instr(IGen::cdq(), irec); gen->add_instr(IGen::idiv_gpr32(get_reg(m_arg, allocs, irec)), irec); diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index 244a7c085a..5671e0cf1b 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -193,6 +193,7 @@ enum class IntegerMathKind { ADD_64, SUB_64, IMUL_32, + IMUL_64, IDIV_32, SHLV_64, SARV_64, diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 56b0543002..0b61804731 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -101,6 +101,7 @@ static const std::unordered_map< {"+", &Compiler::compile_add}, {"-", &Compiler::compile_sub}, {"*", &Compiler::compile_mul}, + {"imul64", &Compiler::compile_imul64}, {"/", &Compiler::compile_div}, {"shlv", &Compiler::compile_shlv}, {"shrv", &Compiler::compile_shrv}, diff --git a/goalc/compiler/compilation/Math.cpp b/goalc/compiler/compilation/Math.cpp index 674152f17a..21824070de 100644 --- a/goalc/compiler/compilation/Math.cpp +++ b/goalc/compiler/compilation/Math.cpp @@ -160,6 +160,7 @@ Val* Compiler::compile_mul(const goos::Object& form, const goos::Object& rest, E auto math_type = get_math_mode(first_type); switch (math_type) { case MATH_INT: { + // todo, signed vs unsigned? auto result = env->make_gpr(first_type); env->emit(std::make_unique(result, first_val->to_gpr(env))); @@ -194,6 +195,41 @@ Val* Compiler::compile_mul(const goos::Object& form, const goos::Object& rest, E return get_none(); } +Val* Compiler::compile_imul64(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + if (!args.named.empty() || args.unnamed.empty()) { + throw_compile_error(form, "Invalid * form"); + } + + // look at the first value to determine the math mode + auto first_val = compile_error_guard(args.unnamed.at(0), env); + auto first_type = first_val->type(); + auto math_type = get_math_mode(first_type); + switch (math_type) { + case MATH_INT: { + auto result = env->make_gpr(first_type); + env->emit(std::make_unique(result, first_val->to_gpr(env))); + + for (size_t i = 1; i < args.unnamed.size(); i++) { + env->emit(std::make_unique( + IntegerMathKind::IMUL_64, result, + to_math_type(compile_error_guard(args.unnamed.at(i), env), math_type, env) + ->to_gpr(env))); + } + return result; + } + case MATH_FLOAT: + case MATH_INVALID: + throw_compile_error( + form, "Cannot determine the math mode for object of type " + first_type.print()); + break; + default: + assert(false); + } + assert(false); + return get_none(); +} + Val* Compiler::compile_sub(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); if (!args.named.empty() || args.unnamed.empty()) { diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index 219e1efd1d..a2c8814c36 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -1387,6 +1387,19 @@ class IGen { return instr; } + /*! + * Multiply gprs (64-bit, signed). + * DANGER - this treats all operands as 64-bit. This is not like the EE. + */ + static Instruction imul_gpr64_gpr64(Register dst, Register src) { + Instruction instr(0xf); + instr.set_op2(0xaf); + assert(dst.is_gpr()); + assert(src.is_gpr()); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, true); + return instr; + } + /*! * Divide (idiv, 32 bit) * todo UNTESTED diff --git a/test/goalc/source_templates/arithmetic/multiply32.static.gc b/test/goalc/source_templates/arithmetic/multiply32.static.gc new file mode 100644 index 0000000000..4a8f3c8ce3 --- /dev/null +++ b/test/goalc/source_templates/arithmetic/multiply32.static.gc @@ -0,0 +1,2 @@ +(* #x12341234 #x12341234) + diff --git a/test/goalc/source_templates/arithmetic/multiply64.static.gc b/test/goalc/source_templates/arithmetic/multiply64.static.gc new file mode 100644 index 0000000000..9a4eb4e012 --- /dev/null +++ b/test/goalc/source_templates/arithmetic/multiply64.static.gc @@ -0,0 +1,2 @@ +(imul64 #x12341234 #x12341234) + diff --git a/test/goalc/test_arithmetic.cpp b/test/goalc/test_arithmetic.cpp index 35a2c6f46e..1e7f18f07d 100644 --- a/test/goalc/test_arithmetic.cpp +++ b/test/goalc/test_arithmetic.cpp @@ -236,3 +236,8 @@ TEST_F(ArithmeticTests, Subtraction) { runner.run_static_test(env, testCategory, "subtract-2.static.gc", {"4\n"}); runner.run_static_test(env, testCategory, "subtract-let.static.gc", {"3\n"}); } + +TEST_F(ArithmeticTests, Multiplication2) { + runner.run_static_test(env, testCategory, "multiply32.static.gc", {"-1234478448\n"}); + runner.run_static_test(env, testCategory, "multiply64.static.gc", {"93270638141856400\n"}); +} \ No newline at end of file From 5f91651c9289085696fd1743c8860c2a7983339f Mon Sep 17 00:00:00 2001 From: water Date: Sat, 28 Nov 2020 21:02:50 -0500 Subject: [PATCH 3/4] fix types --- decompiler/config/all-types.gc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index 9bdc4b65bf..8a145d8456 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -31308,7 +31308,7 @@ ;;(define-extern bounding-box4w object) ;; unknown type ;;(define-extern bounding-box-both object) ;; unknown type ;;(define-extern matrix4h object) ;; unknown type -(define-extern matrix-copy! function) +; (define-extern matrix-copy! function) ;;(define-extern matrix3 object) ;; unknown type ;;(define-extern matrix object) ;; unknown type ;;(define-extern *unity-quaternion* object) ;; unknown type From e93b54347f6a7d6da0884436138ffd2a8691aa0c Mon Sep 17 00:00:00 2001 From: water Date: Sun, 29 Nov 2020 10:46:14 -0500 Subject: [PATCH 4/4] add float min max to the compiler --- doc/code_status.md | 17 ++++++- goal_src/build/dgos.txt | 4 ++ goal_src/engine/geometry/geometry-h.gc | 29 ++++++++++++ goal_src/engine/math/math.gc | 26 ++++------- goal_src/engine/math/transform-h.gc | 21 +++++++++ goalc/compiler/Compiler.h | 2 + goalc/compiler/IR.cpp | 12 +++++ goalc/compiler/IR.h | 2 +- goalc/compiler/compilation/Atoms.cpp | 2 + goalc/compiler/compilation/Math.cpp | 46 +++++++++++++++++++ goalc/emitter/IGen.h | 28 +++++++++++ .../float/float-max.static.gc | 2 + .../float/float-min.static.gc | 2 + test/goalc/test_float.cpp | 5 ++ 14 files changed, 178 insertions(+), 20 deletions(-) create mode 100644 test/goalc/source_templates/float/float-max.static.gc create mode 100644 test/goalc/source_templates/float/float-min.static.gc diff --git a/doc/code_status.md b/doc/code_status.md index 39f5ce82dd..1edc90dc76 100644 --- a/doc/code_status.md +++ b/doc/code_status.md @@ -47,4 +47,19 @@ Just types. Done! Types and one function. Done, but the matrix-copy! function isn't that efficient. ## quaternion-h.gc -Done! \ No newline at end of file +Done! + +## euler-h.gc +Needs static arrays + +## transform-h.gc +Done! + +## geometry-h.gc +Done! + +## trigonometry-h.gc +Empty File. + +## transformq-h.gc +Not done. \ No newline at end of file diff --git a/goal_src/build/dgos.txt b/goal_src/build/dgos.txt index 8be3c48970..14bee90783 100644 --- a/goal_src/build/dgos.txt +++ b/goal_src/build/dgos.txt @@ -210,6 +210,8 @@ ("ENGINE.CGO" ("types-h.o" "types-h") ("vu1-macros.o" "vu1-macros") + + ;; the "math" section ("math.o" "math") ("vector-h.o" "vector-h") ("gravity-h.o" "gravity-h") @@ -228,6 +230,8 @@ ("euler.o" "euler") ("geometry.o" "geometry") ("trigonometry.o" "trigonometry") + + ("gsound-h.o" "gsound-h") ("timer-h.o" "timer-h") ("timer.o" "timer") diff --git a/goal_src/engine/geometry/geometry-h.gc b/goal_src/engine/geometry/geometry-h.gc index acd37edb03..a4106cb795 100644 --- a/goal_src/engine/geometry/geometry-h.gc +++ b/goal_src/engine/geometry/geometry-h.gc @@ -5,3 +5,32 @@ ;; name in dgo: geometry-h ;; dgos: GAME, ENGINE +;; geometry-h +(deftype curve (structure) + ((cverts uint32 :offset-assert 0) + (num-cverts int32 :offset-assert 4) + (knots uint32 :offset-assert 8) + (num-knots int32 :offset-assert 12) + (length float :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x14 + :flag-assert #x900000014 + ) + +;; geometry-h +(deftype border-plane (basic) + ((name basic :offset-assert 4) + (action basic :offset-assert 8) + (slot int8 :offset-assert 12) + (trans vector :inline :offset-assert 16) + (normal vector :inline :offset-assert 32) + ) + :method-count-assert 11 + :size-assert #x30 + :flag-assert #xb00000030 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + ) + ) \ No newline at end of file diff --git a/goal_src/engine/math/math.gc b/goal_src/engine/math/math.gc index ebcc28697f..afe47eef51 100644 --- a/goal_src/engine/math/math.gc +++ b/goal_src/engine/math/math.gc @@ -5,30 +5,20 @@ ;; name in dgo: math ;; dgos: GAME, ENGINE +;; contains various math helpers + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; float macros ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; at some point, these could be more optimized. +;; at some point, this could be more optimized. +;; MIPS has an explicit abs.s instruction, but x86-64 doesn't. +;; modern clang on O3 does a comiss/branch and this is probably pretty close. (defmacro fabs (x) - ;"Floating point absolute value. On MIPS there is a abs.s instruction. - ; In the future we could consider optimizing this more for x86, but this is good enough." - `(if (< ,x 0) - (- ,x) - ,x) - ) - -(defmacro fmin (x y) - `(if (< ,x ,y) - ,x - ,y) - ) - -(defmacro fmax (x y) - `(if (> ,x ,y) - ,x - ,y) + `(if (< (the float ,x) 0) + (- (the float ,x)) + (the float ,x)) ) diff --git a/goal_src/engine/math/transform-h.gc b/goal_src/engine/math/transform-h.gc index 06cf11405a..9a19be0a1c 100644 --- a/goal_src/engine/math/transform-h.gc +++ b/goal_src/engine/math/transform-h.gc @@ -5,3 +5,24 @@ ;; name in dgo: transform-h ;; dgos: GAME, ENGINE + +(deftype transform (structure) + ((trans vector :inline :offset-assert 0) + (rot vector :inline :offset-assert 16) + (scale vector :inline :offset-assert 32) + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + + +(deftype trs (basic) + ((trans vector :inline :offset-assert 16) + (rot vector :inline :offset-assert 32) + (scale vector :inline :offset-assert 48) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) \ No newline at end of file diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 8689c8b37c..41c83a90d8 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -231,6 +231,8 @@ class Compiler { Val* compile_logand(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_logior(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_pointer_add(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_fmin(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_fmax(const goos::Object& form, const goos::Object& rest, Env* env); // Function Val* compile_lambda(const goos::Object& form, const goos::Object& rest, Env* env); diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 1aba46ddb4..99e99ac484 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -522,6 +522,10 @@ std::string IR_FloatMath::print() { return fmt::format("addss {}, {}", m_dest->print(), m_arg->print()); case FloatMathKind::SUB_SS: return fmt::format("subss {}, {}", m_dest->print(), m_arg->print()); + case FloatMathKind::MAX_SS: + return fmt::format("maxss {}, {}", m_dest->print(), m_arg->print()); + case FloatMathKind::MIN_SS: + return fmt::format("minss {}, {}", m_dest->print(), m_arg->print()); default: throw std::runtime_error("Unsupported FloatMathKind"); } @@ -555,6 +559,14 @@ void IR_FloatMath::do_codegen(emitter::ObjectGenerator* gen, gen->add_instr( IGen::subss_xmm_xmm(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec); break; + case FloatMathKind::MAX_SS: + gen->add_instr( + IGen::maxss_xmm_xmm(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec); + break; + case FloatMathKind::MIN_SS: + gen->add_instr( + IGen::minss_xmm_xmm(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec); + break; default: assert(false); } diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index 5671e0cf1b..a79804c2d5 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -224,7 +224,7 @@ class IR_IntegerMath : public IR { RegVal* m_arg; }; -enum class FloatMathKind { DIV_SS, MUL_SS, ADD_SS, SUB_SS }; +enum class FloatMathKind { DIV_SS, MUL_SS, ADD_SS, SUB_SS, MIN_SS, MAX_SS }; class IR_FloatMath : public IR { public: diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 0b61804731..446105fbdb 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -124,6 +124,8 @@ static const std::unordered_map< {"<", &Compiler::compile_condition_as_bool}, {">", &Compiler::compile_condition_as_bool}, {"&+", &Compiler::compile_pointer_add}, + {"fmax", &Compiler::compile_fmax}, + {"fmin", &Compiler::compile_fmin}, // BUILDER (build-dgo/build-cgo?) {"build-dgos", &Compiler::compile_build_dgo}, diff --git a/goalc/compiler/compilation/Math.cpp b/goalc/compiler/compilation/Math.cpp index 21824070de..fb05d60d49 100644 --- a/goalc/compiler/compilation/Math.cpp +++ b/goalc/compiler/compilation/Math.cpp @@ -195,6 +195,52 @@ Val* Compiler::compile_mul(const goos::Object& form, const goos::Object& rest, E return get_none(); } +Val* Compiler::compile_fmin(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + if (!args.named.empty() || args.unnamed.empty()) { + throw_compile_error(form, "Invalid fmin form"); + } + + // look at the first value to determine the math mode + auto first_val = compile_error_guard(args.unnamed.at(0), env); + if (get_math_mode(first_val->type()) != MATH_FLOAT) { + throw_compile_error(form, "Must use floats in fmin"); + } + auto result = env->make_xmm(first_val->type()); + env->emit(std::make_unique(result, first_val->to_xmm(env))); + for (size_t i = 1; i < args.unnamed.size(); i++) { + auto val = compile_error_guard(args.unnamed.at(i), env); + if (get_math_mode(val->type()) != MATH_FLOAT) { + throw_compile_error(form, "Must use floats in fmin"); + } + env->emit(std::make_unique(FloatMathKind::MIN_SS, result, val->to_xmm(env))); + } + return result; +} + +Val* Compiler::compile_fmax(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + if (!args.named.empty() || args.unnamed.empty()) { + throw_compile_error(form, "Invalid fmax form"); + } + + // look at the first value to determine the math mode + auto first_val = compile_error_guard(args.unnamed.at(0), env); + if (get_math_mode(first_val->type()) != MATH_FLOAT) { + throw_compile_error(form, "Must use floats in fmax"); + } + auto result = env->make_xmm(first_val->type()); + env->emit(std::make_unique(result, first_val->to_xmm(env))); + for (size_t i = 1; i < args.unnamed.size(); i++) { + auto val = compile_error_guard(args.unnamed.at(i), env); + if (get_math_mode(val->type()) != MATH_FLOAT) { + throw_compile_error(form, "Must use floats in fmax"); + } + env->emit(std::make_unique(FloatMathKind::MAX_SS, result, val->to_xmm(env))); + } + return result; +} + Val* Compiler::compile_imul64(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); if (!args.named.empty() || args.unnamed.empty()) { diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index a2c8814c36..111f34bf4f 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -1740,6 +1740,34 @@ class IGen { return instr; } + /*! + * Floating point minimum. + */ + static Instruction minss_xmm_xmm(Register dst, Register src) { + assert(dst.is_xmm()); + assert(src.is_xmm()); + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x5d); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false); + instr.swap_op0_rex(); + return instr; + } + + /*! + * Floating point maximum. + */ + static Instruction maxss_xmm_xmm(Register dst, Register src) { + assert(dst.is_xmm()); + assert(src.is_xmm()); + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x5f); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false); + instr.swap_op0_rex(); + return instr; + } + /*! * Convert GPR int32 to XMM float (single precision) */ diff --git a/test/goalc/source_templates/float/float-max.static.gc b/test/goalc/source_templates/float/float-max.static.gc new file mode 100644 index 0000000000..3dcef3f54e --- /dev/null +++ b/test/goalc/source_templates/float/float-max.static.gc @@ -0,0 +1,2 @@ +(define format _format) +(format #t "~,,2f~%" (fmax 1.0 2.0 -1.2 3.7 2.2)) \ No newline at end of file diff --git a/test/goalc/source_templates/float/float-min.static.gc b/test/goalc/source_templates/float/float-min.static.gc new file mode 100644 index 0000000000..2457893e3f --- /dev/null +++ b/test/goalc/source_templates/float/float-min.static.gc @@ -0,0 +1,2 @@ +(define format _format) +(format #t "~,,2f~%" (fmin 1.0 2.0 -1.2 3.7 2.2)) \ No newline at end of file diff --git a/test/goalc/test_float.cpp b/test/goalc/test_float.cpp index 7f235dbebf..ec6b398ce5 100644 --- a/test/goalc/test_float.cpp +++ b/test/goalc/test_float.cpp @@ -75,3 +75,8 @@ TEST_F(FloatTests, Functions) { env, testCategory, "nested-float-functions.static.gc", {"i 1.4400 3.4000\nr 10.1523\ni 1.2000 10.1523\nr 17.5432\n17.543 10.152\n0\n"}); } + +TEST_F(FloatTests, MinMax) { + runner.run_static_test(env, testCategory, "float-max.static.gc", {"3.70\n0\n"}); + runner.run_static_test(env, testCategory, "float-min.static.gc", {"-1.20\n0\n"}); +} \ No newline at end of file