diff options
| author | Cliff Click <cliffc@acm.org> | 2025-04-24 14:38:36 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2025-04-24 14:38:36 -0700 |
| commit | 5d026780154ddd9ca111303df4dce9eac1c2d1c2 (patch) | |
| tree | ed6e78ca34eee5a22e4a2c3c31c063d30bbdc92d /chapter21/src/test/java/com | |
| parent | 94f43b86a3240c0af1db2b0fd0429e6ef98d04b7 (diff) | |
| download | Simple-5d026780154ddd9ca111303df4dce9eac1c2d1c2.tar.gz Simple-5d026780154ddd9ca111303df4dce9eac1c2d1c2.zip | |
Squashed commit of the following:
commit 37da12391989265ae9560e6b08d1d309ac961db5
Merge: 7357dd1a 94f43b86
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 24 14:37:32 2025 -0700
Merge branch 'main' into ch21
commit 7357dd1af9e7d4a2355d82a3e42a0123f465bd28
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 24 12:14:00 2025 -0700
minor deps elide
commit 811f17eb70afa23ed58595518e1c9fb8d93a6d76
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 24 11:25:03 2025 -0700
inf loop tree fixes
double-ended infinite loops slightly wrong loop tree
commit 282d976283fc75f001b8446302f75d349c2a53d5
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 24 10:21:03 2025 -0700
Minor formatting.
Replace if-trees with array lookups or expression switch.
Same CALLOC sentinel for all
commit a6b76731cd72fb1edef02b6b449ba7f10bb73167
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Thu Apr 24 17:36:30 2025 +0100
arm eval calloc sentinel (#210)
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* Bring back `make view`
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* Create ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* fix global symbol numbering for constants
* ASimpleReply setup
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* Update README.md
* replace hard coded opcodes with major opcodes
* Fix some formatting (#184)
* Fix title in chapter 19
* Wrap nseted scope images onto newlines in chapter 3
* add asserts and fix reg numbering
* add asserts and fix reg numbering
* minor updates
* revert changes
* remove constant pool from encoder TODO: add flag for JIT
* extend test cases
* Update ASimpleReply2.md
* Update ASimpleReplyChain.md
* minor fixes
* minor fixes
* update float test case
* stringHash fix on x64
* remove conditional ConstantPool write
* brainfuck minor fixes
* minor arm fixes
* split fix
* Ignore unicorn
* More simple replies
* ASimpleReply3
* brainfuck and fib
* brain fuck
* Update DadalReply2.md
* Update ASimpleReplyChain.md
* arg_count, person
* add ignore
* More Simply Reply
* Update ASimpleReply3.md
* typo
* riscv test sieve attempt
* minor changes
* riscv5 emulator additions
* arm eval setup
* Update riscv.java
* arm eval basics
* delta fixes
* arm fixes
* testfib arm
* arg_count
* small update for test newton
* small update for test newton
* testNewton runs on x64, riscv and arm
* arm additions
* arm pc eval fix
* arm workings
* debugging
* arm sieve test
* test person push
* brainFuck arm
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
commit d80cf39073ce13c3ad6a707cff719852cbcdfab2
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 23 20:29:34 2025 -0700
Fix some arm encoding
Fix some OBOB in R5 calloc encoding.
Sort EvalArm by major opcodes.
Add ASL. Remove leading switch. Fix OBOB in calloc.
Fix shift in ld/st with reg+offset
commit eb346519783e29a849bdd85e1d7622e33e0d8eaa
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 23 16:57:51 2025 -0700
Progress sieve on ARM
Most ops need WMASK as the output, to exclude RSP.
calloc sentinal changes to multiple of 4
NewArm needs to set arg X0 to #1; arg X1 is length.
Fix EvalARM calling calloc & not advancing PC.
Fix EvalARM using ints vs longs.
Fix EvalARM missing AndI encoding.
commit 955e7816da5bcb39843230438a4a0e1e4dd7f9f8
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 22 09:59:23 2025 -0700
bring back more loop help from ch22
commit 6d80212003d2b03c2df06683efe696238f391cf7
Author: Cliff Click <cliffc@acm.org>
Date: Mon Apr 21 14:11:27 2025 -0700
Update README.md
commit 5fb03752b195e99dd251c9d497cccc93335bffe2
Author: Cliff Click <cliffc@acm.org>
Date: Mon Apr 21 11:57:04 2025 -0700
Code quality on loop with many continues
commit 79d6d7b2a882046da6be199da0a0651c6c13bb2b
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 20 12:17:41 2025 -0700
Turn back on newton on ARM
commit c0b6e387d7c40aec9e8d626976f4cfac27c54f90
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 20 12:14:22 2025 -0700
Fix bugs
One more encoding invert-branch test version, with extensive comments and symmetry pointed out. Fixes missing back branch at loop end.
Switch the arguments to `dep.addDep(this)` to become `this.addDep(dep)` shortened to `addDep(dep)`.
One more missing dep in Region fusing.
commit 0f3ece57429baf2014fd08a4fd167c6ad7b1315b
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 20 10:46:50 2025 -0700
Correct test case ULP
commit d761c267362e618e9e01de5b1fc0a405f5b466dc
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 20 09:46:32 2025 -0700
C program silently fails on CI?
`sqrt(2)` is returning a `2.0` not e.g. `1.715...`
commit 6abe323a0cb8a3a6a52ab13ea7217a28654f55ca
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 20 09:40:19 2025 -0700
Update newtonFloat.c
commit 0cb35914c6a92be41108a7e3f16801b955967432
Author: Cliff Click <cliffc@acm.org>
Date: Sat Apr 19 16:43:25 2025 -0700
Better block layout
Keeps loops self-contained, handles more variants of infinite loops.
Fold region-to-regions together, in an effort to have fewer junk jumps
commit 4402980faff51393a4a8d35557f1ed8c06596f23
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Sat Apr 19 22:01:05 2025 +0100
TestNewton arm (#209)
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* Bring back `make view`
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* Create ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* fix global symbol numbering for constants
* ASimpleReply setup
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* Update README.md
* replace hard coded opcodes with major opcodes
* Fix some formatting (#184)
* Fix title in chapter 19
* Wrap nseted scope images onto newlines in chapter 3
* add asserts and fix reg numbering
* add asserts and fix reg numbering
* minor updates
* revert changes
* remove constant pool from encoder TODO: add flag for JIT
* extend test cases
* Update ASimpleReply2.md
* Update ASimpleReplyChain.md
* minor fixes
* minor fixes
* update float test case
* stringHash fix on x64
* remove conditional ConstantPool write
* brainfuck minor fixes
* minor arm fixes
* split fix
* Ignore unicorn
* More simple replies
* ASimpleReply3
* brainfuck and fib
* brain fuck
* Update DadalReply2.md
* Update ASimpleReplyChain.md
* arg_count, person
* add ignore
* More Simply Reply
* Update ASimpleReply3.md
* typo
* riscv test sieve attempt
* minor changes
* riscv5 emulator additions
* arm eval setup
* Update riscv.java
* arm eval basics
* delta fixes
* arm fixes
* testfib arm
* arg_count
* small update for test newton
* small update for test newton
* testNewton runs on x64, riscv and arm
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
commit 2c8f9a01f5ccdac7e145f171fa95330923521f41
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 17 10:17:32 2025 -0700
Fix arm D_OFFSET
commit e8e8335258b85f95f24ac506f770fbdd08f4a083
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 17 10:00:59 2025 -0700
win64 spills more than sys5
commit 71835b1cc3d597234e7fb50e7ddddf2b20cf10e2
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Thu Apr 17 17:54:03 2025 +0100
Arm eval (#208)
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* Bring back `make view`
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* Create ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* fix global symbol numbering for constants
* ASimpleReply setup
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* Update README.md
* replace hard coded opcodes with major opcodes
* Fix some formatting (#184)
* Fix title in chapter 19
* Wrap nseted scope images onto newlines in chapter 3
* add asserts and fix reg numbering
* add asserts and fix reg numbering
* minor updates
* revert changes
* remove constant pool from encoder TODO: add flag for JIT
* extend test cases
* Update ASimpleReply2.md
* Update ASimpleReplyChain.md
* minor fixes
* minor fixes
* update float test case
* stringHash fix on x64
* remove conditional ConstantPool write
* brainfuck minor fixes
* minor arm fixes
* split fix
* Ignore unicorn
* More simple replies
* ASimpleReply3
* brainfuck and fib
* brain fuck
* Update DadalReply2.md
* Update ASimpleReplyChain.md
* arg_count, person
* add ignore
* More Simply Reply
* Update ASimpleReply3.md
* typo
* riscv test sieve attempt
* minor changes
* riscv5 emulator additions
* arm eval setup
* Update riscv.java
* arm eval basics
* delta fixes
* arm fixes
* testfib arm
* arg_count
* small update for test newton
* small update for test newton
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
commit 38fd3c17c672c42fc768b0f31680b5d9679cac6d
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 17 09:42:52 2025 -0700
Fix CmpIX86/Jmp swap args bug
Some x86 cmp need to swap args, then Set/Jmp both need to swap `bop`.
Fix missing padding in ASM printer.
MergeSort test like brainfuck - runs on arm.
commit bd0f7963b117d3760cd0206ddfa54573c3e9b149
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 16 14:17:17 2025 -0700
Remove redundant BrainFuck test
Move all flavors into BrainFuck,java.
Check x86 by linking with C & running.
Check risc5 by running in EvalRisc5.
Check arm by encoding, but no sim (yet).
commit d3bc904d9d44bd9ad1e1b6e72c5f9c1a8c428f0b
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 16 13:56:16 2025 -0700
Fix risc5 branch invert
Had conditions on forced invert mixed up.
Sometimes need both a negate and a swap.
rename invert as negate.
commit 3ee7184fb99f8f389e1ff7178ae58e3e90571b07
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 16 09:18:34 2025 -0700
Missed cutout test
commit 2a3b2d4d4bc6099ab9fead00cfaa3f0a8e6aeb9f
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 15 19:41:55 2025 -0700
R5 fcmp, flt load from pool
exportELF(null) now patches the cpool entries as well as globals.
Repeated cpool entries folded.
Fixed FltRISC, FCMP encoding.
commit 2fb0ec312d76f22534331023c86e68b7a62b98e6
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Tue Apr 15 22:46:50 2025 +0100
riscv emulator (#206)
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* Bring back `make view`
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* Create ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* fix global symbol numbering for constants
* ASimpleReply setup
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* Update README.md
* replace hard coded opcodes with major opcodes
* Fix some formatting (#184)
* Fix title in chapter 19
* Wrap nseted scope images onto newlines in chapter 3
* add asserts and fix reg numbering
* add asserts and fix reg numbering
* minor updates
* revert changes
* remove constant pool from encoder TODO: add flag for JIT
* extend test cases
* Update ASimpleReply2.md
* Update ASimpleReplyChain.md
* minor fixes
* minor fixes
* update float test case
* stringHash fix on x64
* remove conditional ConstantPool write
* brainfuck minor fixes
* minor arm fixes
* split fix
* Ignore unicorn
* More simple replies
* ASimpleReply3
* brainfuck and fib
* brain fuck
* Update DadalReply2.md
* Update ASimpleReplyChain.md
* arg_count, person
* add ignore
* More Simply Reply
* Update ASimpleReply3.md
* typo
* riscv test sieve attempt
* minor changes
* riscv5 emulator additions
* arm eval setup
* Update riscv.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
commit b74f35618c087e4e6634707c6e03ca669315bcc9
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Tue Apr 15 17:39:27 2025 -0400
Remove incorrect assert (#205)
commit d4d894407b9cbf83217da3bde1ecbd6d783297e4
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 15 12:58:13 2025 -0700
Revamp calling convention math
Handles win64 shadow stack space correctly.
Unifies most of the caller-save mask, return mask, arg mask computations.
Unifies most of the frame-size math across all chips.
Large documentation comment in Machine about how stack math is computed.
commit 5e7223504d356cbb48c397bed0cd0012eb075935
Author: Cliff Click <cliffc@acm.org>
Date: Mon Apr 14 10:42:50 2025 -0700
win64 shadow stack killed by calls
commit e1351e0f4788779e9f37a77f0abb936f2869cbfc
Author: Cliff Click <cliffc@acm.org>
Date: Mon Apr 14 10:37:53 2025 -0700
NewXXX honors declared CC
commit faf6bae55523f12c32b0e21b3c39445459c50f71
Author: Cliff Click <cliffc@acm.org>
Date: Mon Apr 14 10:08:33 2025 -0700
Missing hasCalls for Newxxx
commit 40a6f7b90e9e5d42bdacabb1c9e93fab5d27f63d
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 13 19:51:04 2025 -0700
EvalRISC5 handles calloc
Patch calls to calloc with a sentinel; emulator spots call to sentinel and mimics calloc on the internal heap.
commit 450dd5169ebbfd8664d073b0d8baea201dae0ed2
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 13 19:49:31 2025 -0700
Cleanup recent phi xform
Missing deps
commit 2308671cd2a95aec8ab42b4eec5cb6f5cba5302c
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 13 11:30:47 2025 -0700
negate
on x86,risc,arm
commit 1e28b27ec2d1129d4a219a57ec0c661c74720493
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 13 10:23:18 2025 -0700
Handle infinite loops
Loop tree building (and breaking infinite loops) needs to happen *before* instruction selection, which is optional. Best done as a new compiler phase... which now wants a driver.
Add a compiler driver; adjust tests to use.
Add "never-branch" handling to the 3 CPU port main branch ops.
commit 0ac2721f28c729c06c28fce14f8f863f8c57e41b
Merge: a42aa8e3 53bb3207
Author: Cliff Click <cliffc@acm.org>
Date: Sat Apr 12 14:08:59 2025 -0700
Merge branch 'ch21' of https://github.com/SeaOfNodes/Simple into ch21
commit a42aa8e31488f34315717743cf075e4d7b6e3677
Author: Cliff Click <cliffc@acm.org>
Date: Sat Apr 12 14:08:57 2025 -0700
Phi generic pull down is stronger
commit 53bb3207c85bc438b9cc28f84bce458e5068ee45
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Sat Apr 12 16:33:57 2025 -0400
Add a constructor to MemOpNode for M2 (#203)
commit d08925c1ff212e2157356eacbd748e679b80954d
Author: Cliff Click <cliffc@acm.org>
Date: Sat Apr 12 11:41:58 2025 -0700
2 more encoding bugs
Missing constant NIL, arm CAST
commit 798bd959410eb6c2e0b9f7ae1222181ac2b295d4
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Sat Apr 12 13:40:20 2025 -0400
MemStore interface (#201)
* Hook for pre-encoding work needed by M2
* Add MemStore interface
* Add both MemLoad and MemStore interfaces
* Fix NPE printing StopNode
* Attach merged mem to return when forcing a loop exit
* Made requested changes. This breaks a few tests, possibly because...
...I am not sure whether some X86 ops are loads or stores.
* Mach ops need anti-deps too
MemOp subclasses are either "Load-like" or "Store-like" based on a flag, and not on subclassing LoadNode nor StoreNode.
Mach ops pick up anti-deps, no reg mask for those.
x86 bug: do not allow "dst = [mem] + #imm", no such instruction
Empty merge-mem folds away.
x86 add-to-mem works both ways
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit 47711b81ea915eeeb46b2ad8ebb291fb21cbaf6e
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Sat Apr 12 11:46:53 2025 -0400
Don't print ante-deps on multinodes when generating asm dumps (#202)
commit 08421921cbb885fdfb785b896f21b59937b99f39
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Sat Apr 12 00:25:08 2025 -0400
Hook for pre-encoding work needed by M2 (#200)
commit 6ab61f8c6ae0fb26802cfff0f4d007e8a55e817e
Author: Cliff Click <cliffc@acm.org>
Date: Fri Apr 11 21:08:03 2025 -0700
Update Chapter20Test.java
commit 404fb8dadfef10d0954abf6e84901b7d6a469f14
Author: Cliff Click <cliffc@acm.org>
Date: Fri Apr 11 21:05:51 2025 -0700
Remove debug prints
commit a2614064a6d86d146af07d0db50c2e76a2c37274
Author: Cliff Click <cliffc@acm.org>
Date: Fri Apr 11 21:01:53 2025 -0700
Larger frame encodings
commit 4e467263058c015ec0838487d1523b51fb42fcfd
Author: Cliff Click <cliffc@acm.org>
Date: Fri Apr 11 20:52:50 2025 -0700
Encoding bugs
NewX86 using wrong killmap.
Split: moving from xmm to gpr inverted REX bits
commit 3f97f091c40ab92b8ea0def108e1db870b5a6f09
Author: Cliff Click <cliffc@acm.org>
Date: Fri Apr 11 19:54:36 2025 -0700
Update TestC.java
commit 81985bd73393c95b34334b5b57613873ffad95ae
Author: Cliff Click <cliffc@acm.org>
Date: Fri Apr 11 19:35:53 2025 -0700
One more try for CI
commit 62f94ffd6020a6cb45ea7ee0bdf1f5295977f1b1
Author: Cliff Click <cliffc@acm.org>
Date: Fri Apr 11 19:23:22 2025 -0700
Update README.md
commit 752bc3e5df6e72e8b639e01a860f80c9d9b5db80
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 10 17:34:15 2025 -0700
Update NewX86.java
commit d89e11f08591f5ff05395b5dc552b630aee5d5b8
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 10 17:26:55 2025 -0700
Update NewX86.java
commit 58141543b8365fe5246fec107a9a68f26c5a7edf
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 10 17:25:38 2025 -0700
Update NewX86.java
commit 006c2351ce7eed1209296b8811f64f3f72f64045
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 10 17:23:04 2025 -0700
better ci error
commit b6765473a3ce3db943355abba8e8ff765a3f8a3b
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 10 17:18:53 2025 -0700
C tests check both Win64 qnd Sys5
NewX86 links against OS-specific `calloc`, uses OS-specific calling convention, and not the declared convention.
Encoding bug, byte-store of SIL, DIL, BPL needs a blank REX.
OBOB errors in Sys5 large-arg-count calling.
commit b68dafa3e6e231bd0d0b0e613986c7e3181349b9
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 10 13:47:06 2025 -0700
More README updates
commit 0b8c694910ccf5e615b8fb68049cffd47be2c3b9
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 10 13:33:08 2025 -0700
Update Makefile
commit f13661bd35bedb211075b8d1ac48bbbbf394001e
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 10 10:16:32 2025 -0700
Update README.md
commit 7d22a8e805befd5d733d079cbbbce634bb6ae623
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 10 09:38:18 2025 -0700
README
commit 074f718a104dbbb8b1b0a2205c176659a27dfd9f
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 9 17:12:56 2025 -0700
Handle large arg count function
2 print modes for stack slots - slot# or stack offset, only available after encoding.
Compute maxArgSlot in one place on all archs.
X86 yank flags from Splits - lest we split a XMM into the flags???
commit b32c6a879c0be7aebad25fabae6f0a24d5670366
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Tue Apr 8 21:52:12 2025 -0400
Make eq() public (#199)
commit 938c09baca29b0647dcd56e9581540d7a8142fb4
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 8 14:28:51 2025 -0700
Fix risc5 bugs from emulator
commit 06872d180720c376d3fdcc0bd0a2a973bf9341c4
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Tue Apr 8 21:40:41 2025 +0100
arg_count, person (#198)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* Bring back `make view`
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* Create ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* fix global symbol numbering for constants
* ASimpleReply setup
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* Update README.md
* replace hard coded opcodes with major opcodes
* Fix some formatting (#184)
* Fix title in chapter 19
* Wrap nseted scope images onto newlines in chapter 3
* add asserts and fix reg numbering
* add asserts and fix reg numbering
* minor updates
* revert changes
* remove constant pool from encoder TODO: add flag for JIT
* extend test cases
* minor fixes
* minor fixes
* update float test case
* stringHash fix on x64
* remove conditional ConstantPool write
* brainfuck minor fixes
* minor arm fixes
* split fix
* brainfuck and fib
* brain fuck
* arg_count, person
* add ignore
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
commit 22cb2b4555b6b539a5204c2e2b60748c8138d8dc
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 8 11:55:35 2025 -0700
Add 32b risc5 emulator
commit a0f70ef9b432fdad06879c333c59e3f3a4dbfe54
Author: Cliff Click <cliffc@acm.org>
Date: Mon Apr 7 14:55:19 2025 -0700
Clean tests
commit b7603e71510b4d0e82681b9639a56cc77bab40d6
Author: Cliff Click <cliffc@acm.org>
Date: Mon Apr 7 14:50:30 2025 -0700
Update Encoding.java
commit 840bfd14b3658f49429cbf84fbc39980477d5f6e
Author: Cliff Click <cliffc@acm.org>
Date: Mon Apr 7 14:50:24 2025 -0700
x86 shift imm are 2-addr
commit 635ef128b8e3c5a993e8551454f986098dd5092e
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Mon Apr 7 22:00:29 2025 +0100
Minor arm fixes (#197)
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* Bring back `make view`
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* Create ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* fix global symbol numbering for constants
* ASimpleReply setup
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* Update README.md
* replace hard coded opcodes with major opcodes
* Fix some formatting (#184)
* Fix title in chapter 19
* Wrap nseted scope images onto newlines in chapter 3
* add asserts and fix reg numbering
* add asserts and fix reg numbering
* minor updates
* revert changes
* remove constant pool from encoder TODO: add flag for JIT
* extend test cases
* minor fixes
* minor fixes
* update float test case
* stringHash fix on x64
* remove conditional ConstantPool write
* brainfuck minor fixes
* minor arm fixes
* split fix
* brainfuck and fib
* brain fuck
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
commit cac92d551e16e13530f0254548476ab122d30b8b
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 6 19:23:47 2025 -0700
Math lib by default for CI
commit 07530e8c5ec3e2ab98be92769d6abcb9024cfbea
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 6 19:19:49 2025 -0700
ignore gcc warning during CI
commit 7030243523999bd647938cd39013f47d9dfc6e88
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 6 19:09:10 2025 -0700
Debug printing for CI
commit a81794df00369f6adf4a06e2f0247d309a196e27
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 6 16:22:38 2025 -0700
Fix sieve
RA returns maxSlot+1
FunX86 prints as `subi`, uses Sub; Ret uses Add
X86 knows that always RPC is passed on stack "as if" a stack arg.
sieve uses unsigned ints.
Driver expects an array pointer
commit 4c4778f8cc3dd4fd354832bfb0ba1a8c03ee744d
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 6 14:43:39 2025 -0700
Fix store ref/0x66 prefix
commit 819963deb617ea622a27918101867aab3c522c3c
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 6 11:42:55 2025 -0700
More encoding bugs
Stack adjust needs REX.
Boolean load/store same as U8
Calling calloc needs size of 1 in RCX, bytes in RDX
commit abd2fc675d0fc29d5b525cdbe5b949e1c85b1e7e
Merge: e30990ad 8db8f750
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 6 10:37:17 2025 -0700
Merge branch 'ch21' of https://github.com/SeaOfNodes/Simple into ch21
commit e30990ad8929084cf433894843adc04b577d6e9f
Author: Cliff Click <cliffc@acm.org>
Date: Sun Apr 6 10:37:16 2025 -0700
End-to-end testing of x86 encoding
commit 8db8f75006a1daf522976ee97048aa4f06780c8e
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Sun Apr 6 17:11:18 2025 +0100
StringHash x64 fixed (#195)
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* Bring back `make view`
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* Create ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* fix global symbol numbering for constants
* ASimpleReply setup
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* Update README.md
* replace hard coded opcodes with major opcodes
* Fix some formatting (#184)
* Fix title in chapter 19
* Wrap nseted scope images onto newlines in chapter 3
* add asserts and fix reg numbering
* add asserts and fix reg numbering
* minor updates
* revert changes
* remove constant pool from encoder TODO: add flag for JIT
* extend test cases
* minor fixes
* minor fixes
* update float test case
* stringHash fix on x64
* remove conditional ConstantPool write
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
commit bb97129daa1504d67a8e3a76f16d6db15f7e5e7b
Author: Cliff Click <cliffc@acm.org>
Date: Sat Apr 5 15:55:48 2025 -0700
Float uses unsigned compare
commit 532b34d34fde93b2841f4c9544c673a0d89434ea
Author: Cliff Click <cliffc@acm.org>
Date: Sat Apr 5 15:53:10 2025 -0700
Fix vs0 bugs
Use a BoolNode.NE instead of inverting outputs; the outputs are already matched and cannot be inverted.
Use a `swap` not an `invert` when switching sides in a compare.
commit 7277ab049ab9cae5fa64cd5ea958f69278f773a4
Author: Cliff Click <cliffc@acm.org>
Date: Sat Apr 5 13:58:07 2025 -0700
Fix elf constants
Record more relocation info in the machine-specific encodings. Use it when building ELF.
Switch to simpler Newtons; some encoding bug in the complex one.
commit fc7d2bce649854982a3c16de164bbaafec11bc27
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Sat Apr 5 19:38:40 2025 +0100
Ch20 encoding (#194)
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* Bring back `make view`
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* Create ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* Update ASimpleReply.md
* fix global symbol numbering for constants
* ASimpleReply setup
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* Update README.md
* replace hard coded opcodes with major opcodes
* Fix some formatting (#184)
* Fix title in chapter 19
* Wrap nseted scope images onto newlines in chapter 3
* add asserts and fix reg numbering
* add asserts and fix reg numbering
* minor updates
* revert changes
* remove constant pool from encoder TODO: add flag for JIT
* extend test cases
* minor fixes
* minor fixes
* update float test case
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
commit 1741cb3039555b1446a1b0c1c3768f3763a0d9e6
Author: Cliff Click <cliffc@acm.org>
Date: Sat Apr 5 11:30:35 2025 -0700
Add in-memory linker
Encoding supports writing cpool to either Elf or in memory
ElfFile writes constant pool
commit 631b699a4cf65f6c91a24df49329bb285aec09fc
Author: Cliff Click <cliffc@acm.org>
Date: Sat Apr 5 10:04:42 2025 -0700
Move PORTS into CodeGen
commit 3016c9fcf8361cb1a0c494dbb7b997df33df5728
Author: Cliff Click <cliffc@acm.org>
Date: Fri Apr 4 16:32:25 2025 -0700
Support constant tuples
commit 887f3d9caf68b051c630bab6c8895778be8f044f
Author: Cliff Click <cliffc@acm.org>
Date: Fri Apr 4 12:29:46 2025 -0700
Partial progress cpool in ELF
Align functions to 16b
commit cc32b0cf801620f1d289cb6ae183e079111540fc
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 3 11:31:06 2025 -0700
Fix RA parallel phi assign
Force spills when phi uses phi same loop header.
This is effectively a parallel assignment, generally shifting values from one iteration to the next.
commit 37b46799c03244241d589085e24582bef8a59efb
Author: Cliff Click <cliffc@acm.org>
Date: Thu Apr 3 09:32:33 2025 -0700
Allow stacked cloneables
For RISC 2-part immediates, both parts can be treated as constants and moved/cloned
commit 35057d50ae21e143bf89158a65e2db139fcb76fa
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 2 16:39:17 2025 -0700
Bug Fix RegAlloc
RA, self-conflict around backedge of loop predates loop, must still split. Eager splitting here leads to bad allocations, but never splitting is broken.
Move building adj lists into IFG from Coalesce.
testNewtonFloat not so fragile.
Hack around bug SetArm
commit fa846b232a9eca4a7691ab59b3ea92e53c6b85a6
Merge: b73a1146 abdb59f9
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 2 14:55:51 2025 -0700
Merge branch 'ch21' of https://github.com/SeaOfNodes/Simple into ch21
commit b73a1146a1af2eabe6af1ba043fc6ff5ef8731ab
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 2 14:55:48 2025 -0700
parse negative floats
commit abdb59f94008f8620d8c654980c8850ce94a15ad
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Wed Apr 2 14:45:45 2025 -0400
Make eq public on ConstantNode (#193)
commit 9452d347d07ff38f6897be0ff8e9a50b7b43ddc3
Author: Cliff Click <cliffc@acm.org>
Date: Wed Apr 2 11:32:42 2025 -0700
Stronger inf loop assert
Handle returning value after inf loop.
Loop nesting check does not use depth until after loop tree is built.
ASMPrint can be called no machine.
commit 4cd6517df6f9fbf77215525cf0b736ab4f90b143
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 1 19:59:02 2025 -0700
Fix bug encoded inf loops
Assert functions laid out end to end.
Fix bug with layout on inf loops.
Swap default Never jump order.
commit f4542326141578053122f8e3c11fd3a1932c6866
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Wed Apr 2 03:37:23 2025 +0100
Minor bug fixes (#190)
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* fix global symbol numbering for constants
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* replace hard coded opcodes with major opcodes
* add asserts and fix reg numbering
* add asserts and fix reg numbering
* minor updates
* revert changes
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit fc783af92ecab0288b94c7b1d6ce1b1e2cab5d42
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 1 15:42:52 2025 -0700
bugfix
commit f6a047a93971574f64a779cf59dc41ff52cdfe27
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 1 15:05:31 2025 -0700
More infinite loop encodings
commit f691d2de7217b04844da51c2c6ecc0fd9137c7d6
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 1 14:26:46 2025 -0700
RA tighter def of being "in" or "out" of loop
commit d8a39c7063ecaa26a3bb962b730837a678ed4d93
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 1 12:07:36 2025 -0700
Encode infinite loops for RISC
commit 40e0111061e174810eb75c898034fc5255b025d8
Author: Cliff Click <cliffc@acm.org>
Date: Tue Apr 1 09:23:31 2025 -0700
remove debug print
commit ea737ed0a49b091478e954e125a58a1842a89e5f
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Tue Apr 1 01:53:31 2025 -0400
Get SLT[IU]* working (#192)
commit 28a873ea522190733e89602b1d50f042c41d20bc
Author: Cliff Click <cliffc@acm.org>
Date: Mon Mar 31 16:57:26 2025 -0700
RA uses killOrdered
commit 950d57cc74a36655a770daea7c28cee03b60e707
Author: Cliff Click <cliffc@acm.org>
Date: Mon Mar 31 15:40:03 2025 -0700
Create intermediate dirs
commit 6d50b541a7a5c6757ee507da24a995bc02354270
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Mon Mar 31 18:30:45 2025 -0400
Make risc op constructors public so that I can subclass them. (#191)
commit 22318a9654f4a6c3f26e6ad6800a875c16f472a2
Author: Cliff Click <cliffc@acm.org>
Date: Mon Mar 31 15:24:16 2025 -0700
Fix encoding FIB
Use common utility for walking CFG chains of empty blocks
commit 760e2b4f57ceac91017ad018f2e9234af351bf5b
Author: Cliff Click <cliffc@acm.org>
Date: Mon Mar 31 11:41:51 2025 -0700
X86 float splits
commit d98a5c8cdf7a249f3c227fc00f014d53452af858
Author: Cliff Click <cliffc@acm.org>
Date: Mon Mar 31 10:15:36 2025 -0700
Move insertAfterAndReplace to RA
Fix Split printing
commit 3a7a9bde8ee95b4e2f4c08588428c59125d727e4
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Mon Mar 31 12:38:12 2025 -0400
Fix sameBlockNoClobber and refine insertAfterAndReplace (#189)
Also fix firstReg when empty
commit ec9a8a7f49f7a8b22897ca96cf2aff8c47e475f8
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 30 16:46:18 2025 -0700
Fix bug j_type encoding
Switch I_TYPE to OP_IMM to match the other major opcodes
commit a7a9069f29b112a355596756bc53cff73e597a3f
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 30 16:12:49 2025 -0700
Pass new reg asserts
Handle -1 reg in X86 in several places.
SplitARM Float spills still wrong, but less wrong.
Cleanup RISC load/store handling.
commit aea468c0b09891f89a8346de7bb37982f7767c2b
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 30 14:54:16 2025 -0700
Update x86_64_v2.java
commit 150bbf6d1f71411c9cdffdc0081013a5dd0f4f4e
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 30 14:54:08 2025 -0700
Remove leftover prints
commit 2ce631b2086f2491e34728fae3ff397b3a35ee73
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 30 14:53:50 2025 -0700
Fix some RISC encodings around FP spills
commit f9710b82df0eb51090514c56e64e743d32d06b12
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Sun Mar 30 21:33:48 2025 +0100
Extra assert for reg numbering (#188)
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* fix global symbol numbering for constants
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* replace hard coded opcodes with major opcodes
* add asserts and fix reg numbering
* add asserts and fix reg numbering
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit fe08995cca0196850855c0f81619d264cf66b716
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Sun Mar 30 15:33:43 2025 -0400
Make RegMaskRW public and add mutating or; Fix coelesce bug (#186)
* Make RegMaskRW public and add union to it.
* Rename union to or
* Split popular non-constant
* Fix coelesce bug
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit 2fd3cd57934ef168b090e3b0ec7da7b41fa595b3
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 30 11:22:27 2025 -0700
fix indent
commit 79263e424709885da08a0cada2ace6cd744158d7
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 30 11:19:14 2025 -0700
Encode ptr loads
ptrs become 64b
Fix Cmp encoding
commit 8701c671c8e6d6eedc01436ea38bde01fba634f0
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 30 10:54:39 2025 -0700
Fix BB layout bug
Fix split of popular value bug.
commit 7d60f55d2c97677e03a6b01869b4832111a12267
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 29 16:48:49 2025 -0700
Force split during a parallel assignment
Was not inserting same-block split, but actually required if doing parallel assignment from fixed regs to fixed regs
commit 8ea9c74afef87cb562604bf94f803853babe2d33
Author: Darrick Wiebe <darrickw@gmail.com>
Date: Sat Mar 29 18:22:54 2025 -0400
Enable asm printing to be flipped to big-endian (#185)
* Suppress unchecked operation warnings
* Selectable asm dump endianness
* Fix a crash in coalesce when v2._adj is null, so has no adjacent nodes anyway.
commit 99d3fe964eb556370d0f23327b29c26cee9b3edb
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 29 14:47:33 2025 -0700
Fix non-deterministic bug
commit b09370af9db2be3b763eb72a61a34aecb743dcd9
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 29 10:48:05 2025 -0700
Split popular constants
commit 7c0a57d1693b2d714f5871f4615b17c29d15ad46
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Sat Mar 29 16:39:00 2025 +0000
Arm major opcodes, riscv load-store fixes (#183)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* fix global symbol numbering for constants
* fix TFP pathc on arm and riscv, keep symbols in good order.
* fix opcode for store and loads, memOpRisc for Store and splitting
* replace hard coded opcodes with major opcodes
* replace hard coded opcodes with major opcodes
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit 7a22f7be12dadf9936a5102757b048a354a40bf5
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Sat Mar 29 02:45:44 2025 +0000
Ch20 encoding (#181)
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
* readme fixups
* fix global symbol numbering for constants
* fix TFP pathc on arm and riscv, keep symbols in good order.
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit 82b4b2187d51f96f3217b10febfbf18071ec61fc
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 28 15:58:58 2025 -0700
risc5 encoding bug
commit b3c108731794fed83015ba92a651af13a3a1477d
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 28 14:05:49 2025 -0700
More custom op names
commit e5f156803488ade996c04b5135ac87942c354245
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 28 09:29:39 2025 -0700
Update LoadRISC.java
commit d18505d598fd7c96d152d57f6f5d1f34124defc3
Author: Cliff Click <cliffc@acm.org>
Date: Thu Mar 27 11:55:31 2025 -0700
R5 encoding bug fixes
Remove extra branch-to-next
commit e16cf5d94bce81a27f5080be6f90da448544ee2a
Author: Cliff Click <cliffc@acm.org>
Date: Wed Mar 26 16:53:49 2025 -0700
Update LUI.java
commit c2dbd396024d4ecf21747c16f410fa31a1dfdf91
Author: Cliff Click <cliffc@acm.org>
Date: Wed Mar 26 14:42:40 2025 -0700
more portable
commit d90baa2a8c0a8ce9a84befec756b39ec49360b8e
Author: Cliff Click <cliffc@acm.org>
Date: Wed Mar 26 12:16:15 2025 -0700
Minor improves to portability
commit 37150e852041029792176f9d559eb714a2a6726b
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 25 19:56:36 2025 -0700
few more publics
commit 7225eb931c2cefa105b17f393fe242c00b02a185
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 25 12:11:42 2025 -0700
RISC5 branch encoding bug
commit 4bbfd2f561f4a28fc749dd36bbb417ce03de5f78
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 25 11:21:35 2025 -0700
Minor cleanups
Custom op for risc5, a few more things public for external port
commit 5fd3508bd3c45ed83757b47caa7a24361bec71cb
Author: XmiliaH <45106915+XmiliaH@users.noreply.github.com>
Date: Tue Mar 25 18:44:41 2025 +0100
Call Simple function from C (#179)
* Call simple function from c
* Small fix
* Use different test values
commit 1f87fba4ee6b874eed6c3652e3e8a25b5b0726aa
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 25 08:53:55 2025 -0700
Update UJmpRISC.java
commit 321193056bf13d525399885afed16ffece3c2e5a
Author: Cliff Click <cliffc@acm.org>
Date: Mon Mar 24 16:35:48 2025 -0700
Make more public
Allows custom risc5 to override
commit ac3855c4313d6cab8e2bb778b5bfa679415cb519
Author: Dmitry Stogov <dmitrystogov@gmail.com>
Date: Mon Mar 24 18:56:05 2025 +0300
Driver related fixes and improvements (#175)
* Fix simple.sh (pass arguments)
* Consistently replace "_" with "-" in command-line options
* Add "--dump-time" option to print compilation statistics
* Add "--dump-size" option to print generated code size
commit 3db126e018388facfab368ecb2c8f8bc566afe1a
Author: XmiliaH <45106915+XmiliaH@users.noreply.github.com>
Date: Mon Mar 24 00:09:33 2025 +0100
First run as elf binary (#178)
commit 8d6de0e10358fc197490cdacb10cbcedc18bb4f6
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 23 15:59:23 2025 -0700
Coalesacing
Fix bug IFG adding interference to from 1 reg live range, even if success in clearing reg
Fix bug LRG merging when both masks have already been trimmed.
Missing ARM stack adjust
commit 0afb4876e602faa75319bb1faf9adc7f6f7476c2
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 23 11:26:16 2025 -0700
Correct golden spill counts
Encoding: bad test cutout required JMP
AddIRISC bad edges during spill.
commit 3e17e811c4429cda194ccdb892ddb2bd10fa71ea
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Sun Mar 23 17:10:41 2025 +0000
Ch20 encoding (#177)
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
* Store/load float arm/riscv/x64
* got rid of debug print
* splits for x86, riscv and arm
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit 880329e6b4ba209ea35ca647bb14157c1b4234fb
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 23 10:06:43 2025 -0700
More RegAlloc
Forcing kills on allocations broke a bunch of things.
Color: pickRisky single-Def cloneables that are not next to their uses. I.e., a X86 flags-producer far away from a required jump:
Pick as Risky, fail to color, and spill (clone) next to jump.
Remove a bunch of premature split fold-thrus
commit 05925aa5a73c48f4c380ec919583cb56057383ea
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 23 10:02:41 2025 -0700
x86 test op
Invert "Bool(#0,x); If " to "CmpI(x,#0); Jmp!XX"
commit e1b6befcfb865e0a8e34d895a0ddc82bf31d6d4b
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 23 10:01:32 2025 -0700
Robust pre-encoding
commit d333a9001c4f1db159dca8395b5e59c2d2aa92d7
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 23 10:01:20 2025 -0700
Share a bunch of larger common tests
Move them to `*.smp` files
commit 71d882bae6c88ce20353b787cb3699860d52b3d5
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 23 10:00:33 2025 -0700
riscv load/store does not do [reg+reg]
riscv load/store does not do [reg+reg], so the base might not be object base.
commit 94ea5ac5871b74273537d6124a1aeb64bba495d9
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Fri Mar 21 22:49:50 2025 +0000
Ch20 encoding (#176)
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
* add test export
* merged
* store opt
* store and load optimised forms handled
* tfp patch
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit de2d08cfaa659d1c22f2cbbfa84748bd5c307ac4
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 21 15:12:05 2025 -0700
Stack slot updates
Spill code work
commit 442b5060a7f5857cfdedcc69384d90fd5fed9036
Author: Cliff Click <cliffc@acm.org>
Date: Wed Mar 19 09:54:45 2025 -0700
constant pool
commit 352c6927252137a252ab942c83695890fb332004
Author: Cliff Click <cliffc@acm.org>
Date: Wed Mar 19 09:54:38 2025 -0700
ELF exporting takes a target file
commit 0328057dc9e5a7b4bcee4b9fe9260c91a59d5b5e
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 15:13:43 2025 -0700
Update Chapter21Test.java
commit fbe1e0a84e154b17e09055b184f33cdb08a0138c
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 15:13:30 2025 -0700
bugz
Fix RA bug using Win64 and not full spilling in a single block.
riscv/arm demand SystemV calling convention
Cleanup ch21 tests; add 1 export test
commit 0b107134d7c4081a4529d9cbb15897bfcdc83910
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 14:28:19 2025 -0700
Missing address size on risc
commit 59173187cc8c01506d1bc25447bd7a7f5fee8604
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 14:25:49 2025 -0700
Fix missed peep
only missing when running without asserts.
commit 3bf52024aee9021521bfb85f7e894a3b343e6c85
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 13:57:02 2025 -0700
better printing
commit 129f7a9ce2b9b170f35b07367d80179744776732
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 13:56:54 2025 -0700
add sh and bat drivers
commit 3848ef1cab34690dea7c9fba0ff08fe14b7a13e5
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 13:50:39 2025 -0700
Missed merge piece
commit 0f5ebdf4563f4d71d349a32f511137cfad37d828
Author: Dmitry Stogov <dmitrystogov@gmail.com>
Date: Tue Mar 18 22:00:24 2025 +0300
Create a better driver programn (#168)
This allows:
- read "Simple" programs from files or stdin
- selectivley dump sea-of-nodes after earch optimization pass
- save *.dot files
- print generated assembler
- evaluate progrmas with given arguments (Eval2::eval() needs improvement)
- select CPU and ABI for code-generation
commit 5cab242acdb68045e5993b75fc67b54901002072
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 11:37:26 2025 -0700
Fix comment printing
Minor encoding bugs for large- x86 jumps and arm external call
commit e625a921d8fc9540a277e5becc8fe49b557c49fc
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 11:22:07 2025 -0700
changed relo ABI
Missing patch/merge from ARM ldst-encoding
Explicit external calls.
Better printing for multi-op template encodings
commit efc53c6d4b8d11cb08ba84800d85bc725b2f2d5b
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 09:02:20 2025 -0700
Update CallRISC.java
commit 5faaadb794ad864ce3e26ed6a5fc4457a8aa2f15
Author: Cliff Click <cliffc@acm.org>
Date: Tue Mar 18 00:34:34 2025 -0700
progress on patching calls
commit 9929bca5617c2b0134ae16fb174df8d247443af8
Author: Yasser Arguelles Snape <yasserarguelles2506@gmail.com>
Date: Mon Mar 17 14:59:33 2025 -0700
ELF exporting (but in Chapter 21) (#173)
* Updated ELF exporting code, added function relocations
* added big constants (only floats currently)
commit 65ff94cc06005208d454b6759503b8604135e607
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Mon Mar 17 21:40:44 2025 +0000
Ch20 encoding (#174)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
* REX after PREFIX
* fixup load and store indirect
* updated rex prefix, if its 0x40 then its not needed
* Rex fix for floats, got rid of unnecessary stuff.
* rename to camel case, better param name
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit d5b91a513b53c0e69a82f66f350b4e91a86f615d
Author: Cliff Click <cliffc@acm.org>
Date: Mon Mar 17 12:22:33 2025 -0700
patch branch offsets
commit 1a60b525475c3ad6e548af2e9417e4639aee210c
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 16 22:25:45 2025 -0700
Update LeaX86.java
commit f727c10fe7ec84de7e8fefe16e9774bdae8eee6b
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Mon Mar 17 05:07:11 2025 +0000
Ch20 encoding (#172)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
* tfparm and intarm
* uncontional jump encoding for arm and riscv
* smarter IntARM, Intx86 does not always include rex.
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit 309da044c29f7f5e92f00aca911e69c8bfad7caf
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 16 22:03:51 2025 -0700
short-form x86 encodings
commit 248ac2317de092ea5f302266962fdbec17045d42
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 16 10:50:13 2025 -0700
Add unconditional arm,risc
commit d15e43400bb7c7d0d27257d4c907206257c912b3
Author: Cliff Click <cliffc@acm.org>
Date: Sun Mar 16 10:41:20 2025 -0700
Unconditional jumps
commit 30cea7d810dd9460fa7bd40d1dfb1393c71a0ee9
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Sun Mar 16 00:03:20 2025 +0000
floatarm (#170)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
* readme init
* Merged pr/169
* Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
* floatarm encoding
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit 251622b0111f9f49e04528a11e4ea4f19376b3ef
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 15 16:42:13 2025 -0700
Fixed the array1 test encodings
Clean up branch invert logic during BB layout
Cleanup branch target printing for RISC,ARM
ImmRISC ops mask imm12 to 12 bits
Fix NewRISC; hardcode RPC as part of call
Fix RISC inverting branchs >,>=
commit d4c1895977755a94602d9cbbe8ee743e6bdeb894
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 15 16:11:27 2025 -0700
Merged pr/169
commit dc3a84cea6ad0b99c05ff7f6ab98edb905af0c4f
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Sat Mar 15 23:09:53 2025 +0000
Ch21 fixups (#169)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
* switch to binary literals over decimals for opcodes
* logical immediates fixed, bitwise ops imm forms
* tfp and setArm fixed
* PC relative float for riscv
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit 5097e9f08dacb25e4c4aca20b48aa7d523067628
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 15 16:05:04 2025 -0700
Encoding fixes
commit fe27a3326becaf1a59286fa03d9aaa22504ec4ee
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 15 12:55:57 2025 -0700
Tests all pass
commit b6cbd3d7b7698922e60e2c3eadadab51a430efe1
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 15 12:06:11 2025 -0700
Progress towards no crashes
Multi-part constant expansion was buggy when splitting down into functions.
Improved printing for RISC multi-part constants.
commit a36369843133a170718006600890d12ceb66a553
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 14 16:48:07 2025 -0700
Buncha encoding related fixes.
RA cloning `CmpIxxx` stretches the input live range, new for cloning... and was making broken graph.
Several cloning related bugs in RISC & ARM.
Invariant around RA cloning vs InstSelect is too complex
commit 3fa376db5d05d738b6e1b6d01680ca5bfd79962d
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 14 15:17:49 2025 -0700
Progress on not-encoding tests
commit 49a9c088087918f2018a2a6173805f044fd6a70c
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 14 14:33:03 2025 -0700
Push a driver for ARM logical 12-bit immediates
commit 8784736848d846f40c90c41edeb9162c00d0bdbd
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 14 13:38:11 2025 -0700
ASM printer includes encodings
commit de7fbdcf3f2002903d03c7737f573fa1e84cb306
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 14 12:27:21 2025 -0700
Call encode
commit f3ca559f9fe58d221e2d465845932f92278a7928
Author: Cliff Click <cliffc@acm.org>
Date: Fri Mar 14 12:24:17 2025 -0700
Encoding driver ch21
commit fd0123589d21c5055e4314b8bdf7e883f3d6d956
Author: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Date: Fri Mar 14 16:56:58 2025 +0000
Ch20 encoding (#167)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* READMEs
* Update README.md
* Update README.md
* Move printing into print dir
* Move codegen into codegen
* Framework for Reg Alloc
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* First cut BuildLRG
RegMask.EMPTY -> null
* fix minor bug
* Setup for first simple split
* First cut split code for mismatch regs
* delete non existing matches
* replace RegMask.Empty with null
* Live Ranges U-F
BuildLRG for Phi
Add CmpFX86
* FPR return
CallEndX86 needed to tell fp return from gpr ret
Call has no output regs.
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* Next cut regalloc
Building IFG.
Coloring.
Some nested spilling
Optimize divf/con -> mul times inverse
* turn off ch20 tests
* Update Chapter20Test.java
* RISC-V basic isel (#156)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Bring pr/154 to ch 20
RISC5 changes.
Some missing x86 ops.
Still needs some calling convention love.
* First cut regalloc working
Biased coloring; tracking splits around a LRG.
Separate split for self-conflicts.
postColor to remove junk splits.
Some minor riscv fixes.
* Compile some riscv codes
If SplitEmptyMask does not apply, fall back to normal split.
Missing CmpFRISC.
Renamed riscv flags.
RetRISC handls FPR
remove extra RegMask constructurs
* add riscv port to readme
* arm init
* Progress reg alloc
Handle multi-node projections.
A little smarter about 2-addr scheduling
RegAlloc must-have single registers removes from other masks instead of interfering; had the mask removal backwards in a few cases.
LRG/RegMask COW expand masks before mutating.
Clone small constants instead of spilling.
Alloc & ProjX86 get output reg masks.
Full print large reg masks with sp+offset
* UF LRGs can be null during coloring
* Finish UF find
* work in progress
* README updates
* Fix ch19 mis-merge?
* x86 call args, risc5 load/store regs
* arm2 bs
* Push RISC WMASK
avoid writing to RSP
* work in progress
* clearnup arm
* Progress calling convention allocations
Call unlinks before reg alloc.
CallEnd tracks RPC
CallX86 requires TFP for correct ABI
Bias Color tracks 2-addr.
RPC gets a mask/stack-slot
Clean up call/fun ABI
Fix mask RET, RET_F
Fix print stack slot
Remove isMultiHead, isMultiTail
Better all-asm print
* Beef up Newton test
* fixed main pr problems
* regmask extra comments
* Clean pass normal tests
New fcn print upgrades several golden rule tests.
Move the unlink-all from the walk::err call (which IS used during IterPeeps) to GCM.
CallEnd upgrades return type based on constant call.
Fix unlink to also unlink CallEnd
Constant TFP print checks constant
Tested an untested Load
* Arm basics, minor changes in riscv (#160)
* Initial push
Major CodeGen cleanup; most static globals move into the CodeGen object.
* Minor progress bug with mutual recursion
* Add test from pr#148
* First cut x86 "port"
Only "return 0;", no registers, no encoding, but yah gotta start somewhere
* Update README.md
* Update README.md
* Update ListScheduler.java
* command line launcher for simple
Parser bug fref in args
Some new test cases.
* Add basic RegMask for more than 64 bits of register mask.
Add calling convention basics to X86; con+ret RegMasks.
GCM computes CFG for all users.
Drop unused MultiUse.
Pick up reg-pressure aware ListScheduler.
Re-layout CodeGen file to get code & data chunks nearer each other.
minor Ary extension
* ASM Printer
InstSel handles folding 2+ ideal ops into 1 machine op
* handle instSel for some control flow
Bool,If,CProj,Region,Phi
Handle 2-op expansions
Shuffle instSel graph walk back again.
* Basic if-block inst selects and asm prints
* Add mul-by-coinstant to shift
* left shift codegen basic
* exclude RSP from *write* mask
* 2-arg add (not immediate)
mul-by-small constant opt. Some utilities.
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* inst select for new struct allocation
* Float, bitwise and arithmetic operands codegen x86-64 (#150)
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* Handle first loop
Change inst sel walk again to pre-order, to set an early visit bit to stop cycles.
CFGNode copies dom/loop info.
Cmp not-immediate form.
Ret/Fun lazy updates
* float ops without imm values
* merge
* merge
* inst select for new struct allocation
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* Remove non-exsting FP+imm ops
* left shift codegen basic
* sar codegen basic
* merge
* some basic ops
* addf
* divf, subf, mulf
* rsp allowed in bitwise input, not output
* formatting
* float ops without imm values
* merge
* merge
* Rebased on ch 19
* Update tests
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* cleanup after merge
alpha-sort helper fcns
implicit test vs zero/null
* Add LEA op
asm print works on ideal nodes for all tests
* Minor cleanup lea
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* x86 addressing modes during inst select
Drop New taking inits; just follow with initializing stores. Simplifies inst selelection which otherwise needs to undo this optimization and emit following init stores.
Basic load/store for now, op-to-mem comes later
add same becomes Shl by 1.
Drop DivF-immiedate
* call setup
* extended ch13, 14,15,16 (#153)
* merge2
* merge3
* Fix a few minor fuzzer bugs
* Force array layout
ld/st get size (but not signed/unsigned)
Add unsigned LT for later range checks.
* Fix a bunch of float issues
was deleting required inputs
* simpler invariant
defensive copy inputs for simpler invariant in complex sharing patterns
inc/dec form (just the opcode now).
* Call & CallR X86 instructions
* merge2
* Add-from-memory op
Remove FP immediate forms.
* Remove name from TFP
Never shoulda been there (but was convenient for awhile).
Moved into FunNode, found by checking the linker table with a TFP.
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
* New (#154)
* merge2
* merge3
* GCM for float ops fixed, other cleanup - small extensions
* call setup
* merge2
* remove debug prints
add check for bad call convention
fix 3 tests (bad merge?)
* cleanup call convention abi
Needs more love at some point...
* one more cleanup ABI
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Add CmpMem form
Common addressing mode print
LEA can skip a base
* Add a AddFMemX86
A bunch of mem op patterns are missing, hopefully they are just cut-n-paste from the existing patterns.
* Docs
* XMM reg mask fix
Minor README updates
* Allow inverted cmp/mem
narrowing stores can bypass an AndMask
Array length loads do not need control
Some missing print info
* Support "*ptr op= val"
at least for MemAdd
* missed golden rule update
* Array len is u32
load-after-store zero/sign-extends if the store is truncating
* One more missed case
* riscv init
* added not imm form bitwise shifts and ops plus test cases
* addressing modes(high chance it'll get deleted)
* fix minor bug
* delete non existing matches
* replace RegMask.Empty with null
* riscv handle store and load, fltRisch uses risc reg now
* load float into GPR and then later in RA do hard split.
* minor changes
* turn off ch20 tests
* Update Chapter20Test.java
* add riscv port to readme
* arm init
* work in progress
* arm2 bs
* work in progress
* clearnup arm
* fixed main pr problems
* regmask extra comments
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
* Update Chapter20Test.java
* Weaken range-check test
since no range checks... test cannot fail (pass)
* Fix issue #161
Error adding 'null' and '5'
* bug fixs, cloning constants
Fix bug missing Find.
Fix bug setDef does unordered removal from basic blocks.
Use cloned constants for spilling.
* RA fixes
SplitSelfConflict MUST split before uses and after defs.
Split global constants across functions
Missing find in union;
Inserting split after new inserted wrong place
Pre-remove null LRGs before color.
Bias color hunts through Phis
Shorten InstructionSelection.
Rename !unified() to leader()
AddMemX86 is 2-addr
Add CastXXX to preserve register across cast.
* encoding init
* encoding init 2
* rex fix
* indirect addressing modes
* setop change opcode, rename FLOAT_OFFSET
* mask sib and modrm with 3 bits, 4th bit goes into rex.
* sign extend div with cqo
* idiv input is fixed to rax and RMASK, grab input from second slot when encoding
* encoding of newx86
* tfp and CallEndNode encoding
* sib improvement
* more cleanups
* more cleanups
* Allow a killMap to kill regs.
Bugfix scheduler.
Bugfix spiller - self-conflict order depended on hashtable iteration order, which depended on System.hashCode which would vary run to run.
Improvements to coloring
Improvements to spill quality testing
* Added flags-kill for XOR
* Missing add-deps from fuzzer
* Update Chapter14Test.java
* fixed order of operands
* base rex fixup
* more bug fix
* remove jukn files
* minimal "make release"
* further fixes
* minor fixes
* minor fixes
* minor fixes
* base != -1 check, precedence fix
* Add regkills in IFG building
Fix bug LRG union stats
Adjust self-conflict splits to be slightly less aggressive
In every use-side split, dont split-after-split same block.
NewNode is a multi, does not define a register
* 64 bit encoding for gpr(s) load
* RA improvements
Attempt to commute to keep reg-masks compatible.
Clones with kills (x86 XOR killing flags to zero) fail before the flag-def.
Bugfix failed to color last lrg.
More smarts on picking trivial LRGs during coloring
Drop isSplit, add SplitNode
Pass regalloc round to all splits for debugging
Self-conflict no split on loop backedge.
Handle split that gets made goes dead
No-reg-mask spilling handles case with many def/uses.
Add DivIX86
BrainFuck, MergeSort now allocate as testing
* 8 bit imm support
* conditional jump fix
* Push l,ast change, unbreak branch
* XOR remove extra opcode, imm size supports 8 bit.
* Finally pass the `new [u8]` double-copy test
* Fix several ARM reg setup problems
Finish Split bypass check
ARM: rsp is also a fixed zero, in R31. R0 is a valid register.
CallArm prints args, passes TFP for arg selection. Does not return a register. All CallEndARM which DOES return a register. Parms missing tfp for float args. ProjARM after a New missing regs. RetARM missing RPC.
Remove hopeful DivIX86
* 64 bit imm for alu ops encoded in extra reg then use it in Op as an operand
* Bunch of minor arm, risc fixes
small constants not cloning
* jump invert encoding
* warning comment
* Much ARM fixage
One More Go-Round with Better Biased Colors
Fix bug picking deepest spills to bias.
ARM: remove default 2-address. Fix regmask overflow. Rename some ops to be canonical.
* Fixes to release
Port does not have to be in a Simple named directory.
Call convention is just a string, no enum
Missed some tests running RISC, ARM.
* Minor release change to allow another foreign port
* better split encoding and fix CallInMask
* notNode
* fix callInMask
* riscv init
* riscv basic ALU ops and types of instructions
* Allowing non-Simple repo ports
Expanding public API slightly so alternative (non-Simple repo) ports can happen.
RegMask supports single-bit-set and a long bitmask.
Bugfix GCM splitting multiple global constants.
Bugfix LCM grabbing a projection vs multi
Bugfix RegAlloc when avoiding splitting a clonable
Source code in release
More timing in CodeGen
* riscv indirect addressing and function calls
* Cleanup call ABI presentation
Everybody uses the same names, same code shapes
* Caller save regs
...not callee save yet
* Expose caller save regs
* Store Risc adjustments, no imm in MemOpRISC
* specific print encoding for cpus
* merge fixup
* More x86,risc,arm call convention fixes
Collect callee and caller save masks (inverts of each other, except for spills).
Reorg layout to be closer to each other; remove dead mask definitions; add final keyword
* lui fix up with 32 bit immediates
* Callee-save registers
Callee-save registers and masks and inserting LRGs for same and spilling and picking good spills. Insert CalleeSaveNodes and edges to RetXXXs and RegMasks for those edges.
Iterators for regmask.
Better printing post-allocation
* RegMask supports 128 bits
All regs available arm, risc.
MergeSort compiles & runs
* added flags splitting
* Fix risc5 compares
No flags on risc5.
Branch takes 2 inputs (one can be imm12).
Float compare only sets a GPR to 0/1.
Bool has a isFloat
Common risc5 imm-form handling
* extend test cases
* imm encoding, flags fixed
* grep for Todo: relocs
* float riscv fixed
* flags for set
* lui fix for small bits [0, 12]
* arm init
* arm last touches
* fmov still needs fix
* minor fixes
* minor fixes(2)
* got rid of unnecessary stuff
* create ch21
* remove encodings from chapter20 dir
* 1/2 pass cleaning up ARM encoding API
* ARM encoding compiles
* Risc R_TYPE and I_TYPE
* Progress RISC encodings
Missing lots of stuff still
* progress risc encoding
* risc encodings compile
* Update README.md
* Fold up redundant OP/OP-FP risc5 encoding
* x86 mem opcodes first cut
break out MemOp encodings into parts; change x86 indirectAdr to use Encoding class, remove redundant asserts & code
* x86 mem op encodings
* x86 reg+imm forms
* most reg-reg forms
* More x86 encodings
* Last few encodings
---------
Co-authored-by: Cliff Click <cliffc@acm.org>
commit d8e9a8152d1d3122b5d0da6d1c7a12203011a0f7
Author: Cliff Click <cliffc@acm.org>
Date: Mon Mar 10 12:55:06 2025 -0700
First cut BB layout
commit 10013969971a6c2f3a8b0966323ae35e9a3fb1d8
Author: Cliff Click <cliffc@acm.org>
Date: Mon Mar 10 12:54:46 2025 -0700
Update README.md
commit 4d5538c4b9df61d6c2524868bd005030123b3189
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 8 15:17:22 2025 -0800
first cut encoding driver
ASM printer skips jmps to next-blocks
commit 1fd6f46cca3541861d56f3710728d34791beedf8
Author: Cliff Click <cliffc@acm.org>
Date: Sat Mar 8 11:01:01 2025 -0800
chapter 21 encodings first push
Diffstat (limited to 'chapter21/src/test/java/com')
63 files changed, 10585 insertions, 0 deletions
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter21/src/test/java/com/seaofnodes/simple/BrainFuckTest.java new file mode 100644 index 00000000..2733620e --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/BrainFuckTest.java @@ -0,0 +1,38 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class BrainFuckTest { + + @Test public void testBrainFuck() throws IOException { + String brain_fuck = "Hello World!\n"; + TestC.run("brain_fuck", brain_fuck, 40); + + EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 44, false); + int trap = R5.step(100000); + assertEquals(0,trap); + int ptr = (int)R5.regs[riscv.A0]; + assertEquals(brain_fuck.length(),R5.ld4s(ptr)); + for( int i=0; i<brain_fuck.length(); i++ ) + assertEquals(brain_fuck.charAt(i), R5.ld1z(ptr+4+i)); + + EvalArm64 A5 = TestArm64.build("brain_fuck", 0, 28, false); + int trap_arm = A5.step(100000); + assertEquals(0,trap_arm); + assertEquals(0,trap); + int ptr_arm = (int)A5.regs[arm.X0]; + assertEquals(brain_fuck.length(),A5.ld4s(ptr_arm)); + + for( int i=0; i<brain_fuck.length(); i++ ) + assertEquals(brain_fuck.charAt(i), A5.ld1z(ptr+4+i)); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter01Test.java new file mode 100644 index 00000000..cddbdfcf --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter01Test.java @@ -0,0 +1,95 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter01Test { + + @Test + public void testSimpleProgram() { + CodeGen code = new CodeGen("return 1;").parse(); + Node expr = code.expr(); + if( expr instanceof ConstantNode con ) { + assertEquals(code._start,con.in(0)); + assertEquals(TypeInteger.constant(1), con._type); + } else { + fail(); + } + } + + @Test + public void testZero() { + CodeGen code = new CodeGen("return 0;"); + code.parse(); + for( Node use : code._start._outputs ) + if( use instanceof ConstantNode con && con._type instanceof TypeInteger ) + assertEquals(TypeInteger.constant(0),con._type); + } + + @Test + public void testBad1() { + try { + new CodeGen("ret;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Undefined name 'ret'",e.getMessage()); + } + } + + @Test + public void testBad2() { + try { + new CodeGen("return 0123;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Syntax error: integer values cannot start with '0'",e.getMessage()); + } + } + + @Test + public void testNotBad3() { + // this test used to fail in chapter 1 + assertEquals("return 12;", new CodeGen("return - -12;").parse()._stop.print()); + } + + @Test + public void testBad4() { + try { + new CodeGen("return 100").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Syntax error, expected ;: ",e.getMessage()); + } + } + + @Test + public void testNotBad5() { + // this test used to fail in chapter 1 + assertEquals("return -100;", new CodeGen("return -100;").parse()._stop.print()); + } + + @Test + public void testBad6() { + try { + new CodeGen("return100;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Undefined name 'return100'",e.getMessage()); + } + } + + @Test + public void testBad7() { + try { + new CodeGen("return 1;}").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Syntax error, unexpected: }",e.getMessage()); + } + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter02Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter02Test.java new file mode 100644 index 00000000..631e1487 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter02Test.java @@ -0,0 +1,61 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter02Test { + + @Test + public void testParseGrammar() { + CodeGen code = new CodeGen("return 1+2*3+-5;"); + code.parse(); // No more disable peepholes + assertEquals("return 2;", code.print()); + } + + @Test + public void testAddPeephole() { + CodeGen code = new CodeGen("return 1+2;"); + code.parse(); + assertEquals("return 3;", code.print()); + } + + @Test + public void testSubPeephole() { + CodeGen code = new CodeGen("return 1-2;"); + code.parse(); + assertEquals("return -1;", code.print()); + } + + @Test + public void testMulPeephole() { + CodeGen code = new CodeGen("return 2*3;"); + code.parse(); + assertEquals("return 6;", code.print()); + } + + @Test + public void testDivPeephole() { + CodeGen code = new CodeGen("return 6/3;"); + code.parse(); + assertEquals("return 2;", code.print()); + } + + @Test + public void testMinusPeephole() { + CodeGen code = new CodeGen("return 6/-3;"); + code.parse(); + assertEquals("return -2;", code.print()); + } + + @Test + public void testExample() { + CodeGen code = new CodeGen("return 1+2*3+-5;"); + code.parse(); + assertEquals("return 2;", code.print()); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter03Test.java new file mode 100644 index 00000000..c4e4c91e --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter03Test.java @@ -0,0 +1,73 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter03Test { + + @Test + public void testVarDecl() { + CodeGen code = new CodeGen("int a=1; return a;"); + code.parse(); assertEquals("return 1;", code.print()); + } + + @Test + public void testVarAdd() { + CodeGen code = new CodeGen("int a=1; int b=2; return a+b;"); + code.parse(); + assertEquals("return 3;", code.print()); + } + + @Test + public void testVarScope() { + CodeGen code = new CodeGen("int a=1; int b=2; int c=0; { int b=3; c=a+b; } return c;"); + code.parse(); + assertEquals("return 4;", code.print()); + } + + @Test + public void testVarScopeNoPeephole() { + CodeGen code = new CodeGen("int a=1; int b=2; int !c=0; { int b=3; c=a+b; } return c; "); + code.parse(); // Peepholes always run now + assertEquals("return 4;", code.print()); + } + + @Test + public void testVarDist() { + CodeGen code = new CodeGen("int x0=1; int y0=2; int x1=3; int y1=4; return (x0-x1)*(x0-x1) + (y0-y1)*(y0-y1); "); + code.parse(); + assertEquals("return 8;", code.print()); + } + + @Test + public void testSelfAssign() { + try { + new CodeGen("int a=a; return a;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Undefined name 'a'",e.getMessage()); + } + } + + @Test + public void testRedeclareVar() { + try { + new CodeGen("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Redefining name 'b'",e.getMessage()); + } + } + + @Test + public void testBad1() { + try { + new CodeGen("int a=1; int b=2; int !c=0; { int b=3; c=a+b;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Syntax error, expected }: ",e.getMessage()); + } + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter04Test.java new file mode 100644 index 00000000..c6199193 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter04Test.java @@ -0,0 +1,131 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; +import org.junit.Ignore; + +import static org.junit.Assert.*; + +public class Chapter04Test { + + @Test + public void testPeephole() { + CodeGen code = new CodeGen("return 1+arg+2; "); + code.parse(); + assertEquals("return (arg+3);", code.print()); + } + + @Test + public void testPeephole2() { + CodeGen code = new CodeGen("return (1+arg)+2;"); + code.parse(); + assertEquals("return (arg+3);", code.print()); + } + + @Test + public void testAdd0() { + CodeGen code = new CodeGen("return 0+arg;"); + code.parse(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testAddAddMul() { + CodeGen code = new CodeGen("return arg+0+arg;"); + code.parse(); + assertEquals("return (arg<<1);", code.print()); + } + + @Test + public void testPeephole3() { + CodeGen code = new CodeGen("return 1+arg+2+arg+3; "); + code.parse(); + assertEquals("return ((arg<<1)+6);", code.print()); + } + + @Test + public void testMul1() { + CodeGen code = new CodeGen("return 1*arg;"); + code.parse(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testVarArg() { + CodeGen code = new CodeGen("return arg; "); + code.parse(); + assertTrue(code.ctrl() instanceof FunNode); + assertTrue(code.expr() instanceof ParmNode); + } + + // Adding functions means `main` does not constant fold the incoming arg value + @Ignore @Test + public void testConstantArg() { + CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2), 123L).parse(); + assertEquals("return 2;", code.expr().in(1).toString()); + } + + @Test + public void testCompEq() { + CodeGen code = new CodeGen("return 3==3; "); + code.parse(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testCompEq2() { + CodeGen code = new CodeGen("return 3==4; "); + code.parse(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testCompNEq() { + CodeGen code = new CodeGen("return 3!=3; "); + code.parse(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testCompNEq2() { + CodeGen code = new CodeGen("return 3!=4; "); + code.parse(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testBug1() { + CodeGen code = new CodeGen("int a=arg+1; int !b=a; b=1; return a+2; "); + code.parse(); + assertEquals("return (arg+3);", code.print()); + } + + @Test + public void testBug2() { + CodeGen code = new CodeGen("int !a=arg+1; a=a; return a; "); + code.parse(); + assertEquals("return (arg+1);", code.print()); + } + + @Test + public void testBug3() { + try { new CodeGen("inta=1; return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'inta'",e.getMessage()); } + } + + @Test + public void testBug4() { + CodeGen code = new CodeGen("return -arg;"); + code.parse(); + assertEquals("return (-arg);", code.print()); + } + + @Test + public void testBug5() { + CodeGen code = new CodeGen("return arg--2;"); + code.parse(); + assertEquals("return (arg--2);", code.print()); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java new file mode 100644 index 00000000..d1dfd216 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -0,0 +1,186 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter05Test { + + @Test + public void testIfStmt() { + CodeGen code = new CodeGen( +""" +int a = 1; +if (arg == 1) + a = arg+2; +else { + a = arg-3; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,(arg+2),(arg-3));", code.print()); + } + + @Test + public void testTest() { + CodeGen code = new CodeGen( +""" +int c = 3; +int b = 2; +if (arg == 1) { + b = 3; + c = 4; +} +return c; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,4,3);", code.print()); + } + + @Test + public void testReturn2() { + CodeGen code = new CodeGen( +""" +if( arg==1 ) + return 3; +else + return 4; +"""); + code.parse(); + assertEquals("return Phi(Region,3,4);", code.print()); + } + + @Test + public void testIfMergeB() { + CodeGen code = new CodeGen( +""" +int a=arg+1; +int b=0; +if( arg==1 ) + b=a; +else + b=a+1; +return a+b;"""); + code.parse().opto(); + assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print()); + } + + @Test + public void testIfMerge2() { + CodeGen code = new CodeGen( +""" +int a=arg+1; +int b=arg+2; +if( arg==1 ) + b=b+a; +else + a=b+1; +return a+b;"""); + code.parse().opto(); + assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print()); + } + + @Test + public void testMerge3() { + CodeGen code = new CodeGen( +""" +int a=1; +if( arg==1 ) + if( arg==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,3,5,2,4);", code.print()); + } + + @Test + public void testMerge4() { + CodeGen code = new CodeGen( +""" +int a=0; +int b=0; +if( arg ) + a=1; +if( arg==0 ) + b=2; +return arg+a+b; +"""); + code.parse().opto(); + assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print()); + } + + @Test + public void testMerge5() { + CodeGen code = new CodeGen( +""" +int a=arg==2; +if( arg==1 ) +{ + a=arg==3; +} +return a; +"""); + code.parse().opto(); + assertEquals("return (arg==Phi(Region,3,2));", code.print()); + } + + @Test + public void testTrue() { + CodeGen code = new CodeGen("return true;").parse(); + assertEquals("return 1;",code.print()); + } + + @Test + public void testHalfDef() { + try { new CodeGen("if( arg==1 ) int b=2; return b;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage()); } + } + + @Test + public void testHalfDef2() { + try { new CodeGen("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'b'",e.getMessage()); } + } + + @Test + public void testRegress1() { + try { new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage()); } + } + + + @Test + public void testBadNum() { + try { new CodeGen("return 1-;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Syntax error, expected an identifier or expression: ;",e.getMessage()); } + } + + @Test + public void testKeyword1() { + try { new CodeGen("int true=0; return true;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'true'",e.getMessage()); } + } + + @Test + public void testKeyword2() { + try { new CodeGen("int else=arg; if(else) else=2; else else=1; return else;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'else'",e.getMessage()); } + } + + @Test + public void testKeyword3() { + try { new CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'ififif'",e.getMessage()); } + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java new file mode 100644 index 00000000..b6bc2bf3 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java @@ -0,0 +1,305 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter06Test { + + @Test + public void testPeepholeReturn() { + CodeGen code = new CodeGen(""" +if( true ) return 2; +return 1; +"""); + code.parse().opto(); + assertEquals("return 2;", code.print()); + assertTrue(code.ctrl() instanceof FunNode); + } + + @Test + public void testPeepholeRotate() { + CodeGen code = new CodeGen( +""" +int a = 1; +if (arg) + a = 2; +return (arg < a) < 3; // Because (arg < a) is a bool/uint1/[0-1], its always less than 3 +"""); + code.parse().opto(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testPeepholeCFG() { + CodeGen code = new CodeGen( +""" +int a=1; +if( true ) + a=2; +else + a=3; +return a; +"""); + code.parse().opto(); + assertEquals("return 2;", code.print()); + assertTrue(code.ctrl() instanceof FunNode); + } + + @Test + public void testIfIf() { + CodeGen code = new CodeGen( +""" +int a=1; +if( arg!=1 ) + a=2; +else + a=3; +int b=4; +if( a==2 ) + b=42; +else + b=5; +return b; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,42,5);", code.print()); + } + + @Test + public void testIfArgIf() { + CodeGen code = new CodeGen( +""" +int a=1; +if( 1==1 ) + a=2; +else + a=3; +int b=4; +if( arg==2 ) + b=a; +else + b=5; +return b;"""); + code.parse().opto(); + assertEquals("return Phi(Region,2,5);", code.print()); + } + + @Test + public void testMerge3With2() { + CodeGen code = new CodeGen( +""" +arg=2; +int a=1; +if( arg==1 ) + if( arg==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +""", TypeInteger.constant(2), 123L).parse().opto(); + assertEquals("return 5;", code.print()); + } + + @Test + public void testMerge3With1() { + CodeGen code = new CodeGen( +""" +arg=1; +int a=1; +if( arg==1 ) + if( arg==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +""", TypeInteger.constant(1), 123L).parse().opto(); + assertEquals("return 3;", code.print()); + } + + @Test + public void testMerge3Peephole() { + CodeGen code = new CodeGen( +""" +int a=1; +if( arg==1 ) + if( 1==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,3,5,4);", code.print()); + } + + @Test + public void testMerge3Peephole1() { + CodeGen code = new CodeGen( +""" +arg=1; +int a=1; +if( arg==1 ) + if( 1==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +""", TypeInteger.constant(1), 123L); + code.parse().opto(); + assertEquals("return 3;", code.print()); + } + + @Test + public void testMerge3Peephole3() { + CodeGen code = new CodeGen( +""" +arg=3; +int a=1; +if( arg==1 ) + if( 1==2 ) + a=2; + else + a=3; +else if( arg==3 ) + a=4; +else + a=5; +return a; +""", TypeInteger.constant(3), 123L); + code.parse().opto(); + assertEquals("return 4;", code.print()); + } + + @Test + public void testDemo1NonConst() { + CodeGen code = new CodeGen(""" +int a = 0; +int b = 1; +if( arg ) { + a = 2; + if( arg ) { b = 2; } + else b = 3; +} +return a+b; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,4,1);", code.print()); + } + + + @Test + public void testDemo1True() { + CodeGen code = new CodeGen( +""" +arg=1; +int a = 0; +int b = 1; +if( arg ) { + a = 2; + if( arg ) { b = 2; } + else b = 3; +} +return a+b; +""", TypeInteger.constant(1), 123L); + code.parse().opto(); + assertEquals("return 4;", code.print()); + } + + @Test + public void testDemo1False() { + CodeGen code = new CodeGen( +""" +arg=0; +int a = 0; +int b = 1; +if( arg ) { + a = 2; + if( arg ) { b = 2; } + else b = 3; +} +return a+b; +""", TypeInteger.constant(0), 123L); + code.parse().opto(); + assertEquals("return 1;", code.print()); + } + + @Test + public void testDemo2NonConst() { + CodeGen code = new CodeGen(""" +int a = 0; +int b = 1; +int c = 0; +if( arg ) { + a = 1; + if( arg==2 ) { c=2; } else { c=3; } + if( arg ) { b = 2; } + else b = 3; +} +return a+b+c; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,6,1,5);", code.print()); + } + + + @Test + public void testDemo2True() { + CodeGen code = new CodeGen( +""" +arg=1; +int a = 0; +int b = 1; +int c = 0; +if( arg ) { + a = 1; + if( arg==2 ) { c=2; } else { c=3; } + if( arg ) { b = 2; } + else b = 3; +} +return a+b+c; +""", TypeInteger.constant(1), 123L); + code.parse().opto(); + assertEquals("return 6;", code.print()); + } + + @Test + public void testDemo2arg2() { + CodeGen code = new CodeGen( +""" +arg=2; +int a = 0; +int b = 1; +int c = 0; +if( arg ) { + a = 1; + if( arg==2 ) { c=2; } else { c=3; } + if( arg ) { b = 2; } + else b = 3; +} +return a+b+c; +""", TypeInteger.constant(2), 123L); + code.parse().opto(); + assertEquals("return 5;", code.print()); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter07Test.java new file mode 100644 index 00000000..88554323 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter07Test.java @@ -0,0 +1,222 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter07Test { + + @Test + public void testExample() { + CodeGen code = new CodeGen( +""" +while(arg < 10) { + arg = arg + 1; +} +return arg; +"""); + code.parse(); + assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testRegression() { + CodeGen code = new CodeGen( +""" +int a = 1; +if(arg){}else{ + while(a < 10) { + a = a + 1; + } +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1,Phi(Loop,1,(Phi_a+1)));", code.print()); + } + + @Test + public void testWhileNested() { + CodeGen code = new CodeGen( +""" +int sum = 0; +int i = 0; +while(i < arg) { + i = i + 1; + int j = 0; + while( j < arg ) { + sum = sum + j; + j = j + 1; + } +} +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,Phi(Loop,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", code.print()); + } + + @Test + public void testWhileScope() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(a < 10) { + if (a == 2) a = 3; + else b = 4; +} +return b; +"""); + code.parse(); + assertEquals("return Phi(Loop,2,Phi(Region,Phi_b,4));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhileNestedIfAndInc() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(a < 10) { + if (a == 2) a = 3; + else b = 4; + b = b + 1; + a = a + 1; +} +return b; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + + @Test + public void testWhile() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(a < 10) { + a = a + 1; + a = a + 2; +} +return a; +"""); + code.parse(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhilePeep() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(a < 10) { + a = a + 1; + a = a + 2; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile2() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(arg) a = 2; +return a; +"""); + code.parse(); + assertEquals("return Phi(Loop,1,2);", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile2Peep() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(arg) a = 2; +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,2);", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile3() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(a < 10) { + int b = a + 1; + a = b + 2; +} +return a; +"""); + code.parse(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile3Peep() { + CodeGen code = new CodeGen( +""" +int a = 1; +while(a < 10) { + int b = a + 1; + a = b + 2; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile4() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(a < 10) { + int b = a + 1; + a = b + 2; +} +return a; +"""); + code.parse(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testWhile4Peep() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(a < 10) { + int b = a + 1; + a = b + 2; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java new file mode 100644 index 00000000..09669cc7 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java @@ -0,0 +1,208 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter08Test { + + @Test + public void testEx6() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + break; + if (arg == 6) + break; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Region,(Phi(Loop,arg,Add)+1),Add,Phi_arg);", code.print()); + assertTrue(code.ctrl() instanceof RegionNode); + Assert.assertEquals( "5", Eval2.eval(code, 1)); + Assert.assertEquals("10", Eval2.eval(code, 6)); + } + + + @Test + public void testEx5() { + CodeGen code = new CodeGen( + """ +int a = 1; +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + continue; + if (arg == 7) + continue; + a = a + 1; +} +return a; + """); + code.parse().opto(); + assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1),Phi_a));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + + @Test + public void testEx4() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + continue; + if (arg == 6) + break; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print()); + assertTrue(code.ctrl() instanceof RegionNode); + } + + @Test + public void testEx3() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 6) + break; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print()); + assertTrue(code.ctrl() instanceof RegionNode); + } + + + @Test + public void testEx2() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + continue; + if (arg == 6) + continue; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + + @Test + public void testEx1() { + CodeGen code = new CodeGen( + """ +while(arg < 10) { + arg = arg + 1; + if (arg == 5) + continue; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print()); + assertTrue(code.ctrl() instanceof CProjNode); + } + + @Test + public void testRegress1() { + CodeGen code = new CodeGen( + """ +while( arg < 10 ) { + int a = arg+2; + if( a > 4 ) + break; +} +return arg; + """); + code.parse().opto(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testRegress2() { + CodeGen code = new CodeGen("if(1) return 0; else while(arg>- -arg) arg=arg+1; return 0;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testBreakOutsideLoop() { + try { + new CodeGen(""" +if(arg <= 10) { + break; + arg = arg + 1; +} +return arg; +""").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("No active loop for a break or continue",e.getMessage()); + } + } + + @Test + public void testRegress3() { + CodeGen code = new CodeGen(""" +while(arg < 10) { + break; +} +return arg; +"""); + code.parse().opto(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testRegress4() { + CodeGen code = new CodeGen(""" +int a = 1; +while(arg < 10) { + a = a + 1; + if (arg > 2) { + int a = 17; + break; + } +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,Phi(Loop,1,(Phi_a+1)),Add);", code.print()); + assertTrue(code.ctrl() instanceof RegionNode); + } + + @Test + public void testRegress5() { + CodeGen code = new CodeGen(""" +int a = 1; +while(1) { + a = a + 1; + if (a<10) continue; + break; +} +return a; +"""); + code.parse().opto(); + assertEquals("return (Phi(Loop,1,Add)+1);", code.print()); + assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 ); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter09Test.java new file mode 100644 index 00000000..401f346f --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter09Test.java @@ -0,0 +1,296 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Assert; +import org.junit.Test; +import org.junit.Ignore; + +import static org.junit.Assert.*; + +public class Chapter09Test { + + @Test + @Ignore + public void testJig() { + CodeGen code = new CodeGen(""" +int v0=0; +arg=0; +while(v0) { + while(1) if(arg*arg*0==0) {} + while(0) {} + arg=1; +} +return 0; + """); + code.parse().opto(); + } + + @Test + public void testGVN1() { + CodeGen code = new CodeGen( + """ +int x = arg + arg; +if(arg < 10) { + return arg + arg; +} +else { + x = x + 1; +} +return x; + """); + code.parse().opto(); + assertEquals("return Phi(Region,(arg<<1),(Shl+1));", code.print()); + Assert.assertEquals( "2", Eval2.eval(code, 1)); + Assert.assertEquals("23", Eval2.eval(code, 11)); + } + + @Test + public void testGVN2() { + CodeGen code = new CodeGen("return arg*arg-arg*arg;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + Assert.assertEquals("0", Eval2.eval(code, 1, 1)); + } + + @Test + public void testWorklist1() { + CodeGen code = new CodeGen(""" +int step = 1; +while (arg < 10) { + arg = arg + step + 1; +} +return arg; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,arg,(Phi_arg+2));", code.print()); + Assert.assertEquals("11", Eval2.eval(code, 1)); + } + + @Test + public void testWorklist2() { + CodeGen code = new CodeGen( + """ +int cond = 0; +int one = 1; +while (arg < 10) { + if (cond) one = 2; + arg = arg + one*3 + 1; +} +return arg; + """); + code.parse().opto(); + assertEquals("return Phi(Loop,arg,(Phi_arg+4));", code.print()); + Assert.assertEquals("13", Eval2.eval(code, 1)); + } + + @Test + public void testWorklist3() { + CodeGen code = new CodeGen( + """ +int v1 = 0; +int v2 = 0; +int v3 = 0; +int v4 = 0; +int v5 = 0; +int v6 = 0; +int v7 = 0; +int v8 = 0; +while (arg) { + if (v1) v2 = 1; + if (v2) v3 = 1; + if (v3) v4 = 1; + if (v4) v5 = 1; + if (v5) v6 = 1; + if (v6) v7 = 1; + if (v7) v8 = 1; + arg = arg + v8 + 1; +} +return arg; + """); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testRegionPeepBug() { + CodeGen code = new CodeGen( + """ +int v0=0; +int v1=0; +while(v1+arg) { + arg=0; + int v2=v0; + while(arg+1) {} + v0=1; + v1=v2; +} + """); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testWhile0() { + CodeGen code = new CodeGen("while(0) continue; if(0) arg=0;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testWhile1() { + CodeGen code = new CodeGen(""" +if(0) while(0) { + int arg=arg; + while(0) {} +} +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + + @Test + public void testPrecedence() { + CodeGen code = new CodeGen("return 3-1+2;"); + code.parse().opto(); + assertEquals("return 4;", code.print()); + } + + @Test + public void testSwap2() { + CodeGen code = new CodeGen("return 1+(1+1);"); + code.parse().opto(); + assertEquals("return 3;", code.print()); + } + + + @Test + public void testFuzz0() { + CodeGen code = new CodeGen(""" +int one = 1; +int a = 0; +int zero = 0; +while(arg) { + a = -(one + a + 2); + arg = arg + 1; + one = one + zero; +} +return a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print()); + } + + @Test + public void testFuzz1() { + CodeGen code = new CodeGen(""" +while(1) {} +while(arg) break; +while(arg) arg=0; +arg=0; +int v0=0!=0<-0; +return -0+0+0; +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + + @Test + public void testFuzz2() { + CodeGen code = new CodeGen("return 0+-0;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testFuzz3() { + CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testFuzz4() { + CodeGen code = new CodeGen(""" +while(1) { + arg=0<=0; + if(1<0) while(arg==-0) arg=arg-arg; +} +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testFuzz5() { + CodeGen code = new CodeGen(""" +{ + int v0=0; + while(1) + int v1=0--0; + while(v0) + break; + while(-v0) { + while(0+0+v0) continue; + break; + } + if(-0!=-0+0+v0) while(0+0+0+0) + break; +} +return 0!=0; +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testFuzz6() { + CodeGen code = new CodeGen( +""" +int v0=0; +while(0==1) while(v0) + v0=1+v0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testFuzz7() { + CodeGen code = new CodeGen(""" +while(1) {} +int v0=0; +while(v0) + {} +int v1=0; +while(1) + v1=1; +return v1+v0; + """); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testFuzz8() { + CodeGen code = new CodeGen("while(arg) arg = arg - 1; return arg;"); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testMeet() { + Type t1 = Type.TOP; + Type t2 = TypeInteger.TOP; + Assert.assertEquals(TypeInteger.TOP, t1.meet(t2)); + Assert.assertEquals(TypeInteger.TOP, t2.meet(t1)); + t1 = Type.BOTTOM; + t2 = TypeInteger.BOT; + Assert.assertEquals(Type.BOTTOM, t1.meet(t2)); + Assert.assertEquals(Type.BOTTOM, t2.meet(t1)); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter10Test.java new file mode 100644 index 00000000..1253e4a6 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter10Test.java @@ -0,0 +1,397 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter10Test { + + @Test + public void testFuzzer() { + CodeGen code = new CodeGen( +""" +int a = arg/3; +int b = arg*5; +int x = arg*7; +int y = arg/11; +int p; int g; int h; +if( (arg/13)==0 ) { + p = x + y; + g = x; + h = y; +} else { + p = a + b; + g = a; + h = b; +} +int r = g+h; +return p-r; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testStruct() { + CodeGen code = new CodeGen(""" +struct Bar { + int a; + int b; +}; +struct Foo { + int x; +}; +Foo? foo = null; +Bar !bar = new Bar; +bar.a = 1; +bar.a = 2; +return bar.a; +"""); + code.parse().opto(); + assertEquals("return 2;", code.print()); + } + + @Test + public void testExample() { + CodeGen code = new CodeGen(""" +struct Vector2D { int x; int y; }; +Vector2D !v = new Vector2D; +v.x = 1; +if (arg) + v.y = 2; +else + v.y = 3; +return v; +"""); + code.parse().opto(); + assertEquals("return Vector2D;", code.print()); + } + + @Test + public void testBug() { + CodeGen code = new CodeGen(""" +struct s0 { + int v0; +}; +s0? v1=null; +int v3=v1.zAicm; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Accessing unknown field 'zAicm' from 'null'",e.getMessage()); } + } + + @Test + public void testBug2() { + CodeGen code = new CodeGen(""" +struct s0 { int v0; }; +arg=0+new s0.0; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); } + } + + @Test + public void testLoop() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +Bar !bar = new Bar; +while (arg) { + bar.a = bar.a + 2; + arg = arg + 1; +} +return bar.a; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_a+2));", code.print()); + } + + @Test + public void testIf() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +Bar !bar = new Bar; +if (arg) bar = null; +bar.a = 1; +return bar.a; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Type null is not of declared type *Bar",e.getMessage()); } + } + + @Test + public void testIf2() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +Bar? !bar = null; +if (arg) bar = new Bar; +bar.a = 1; +return bar.a; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); } + } + + @Test + public void testIf3() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +Bar bar = null; +if (arg) bar = null; +bar.a = 1; +return bar.a; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Type null is not of declared type *Bar", e.getMessage()); } + } + + @Test + public void testIfOrNull() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +Bar? !bar = new Bar; +if (arg) bar = null; +if( bar ) bar.a = 1; +return bar; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,null,Bar);", code.print()); + } + + @Test + public void testIfOrNull2() { + CodeGen code = new CodeGen( +""" +struct Bar { int a; }; +Bar? !bar = new Bar; +if (arg) bar = null; +int rez = 3; +if( !bar ) rez=4; +else bar.a = 1; +return rez; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,4,3);", code.print()); + } + + @Test + public void testWhileWithNullInside() { + CodeGen code = new CodeGen(""" +struct s0 {int v0;}; +s0? !v0 = new s0; +int ret = 0; +while(arg) { + ret = v0.v0; + v0 = null; + arg = arg - 1; +} +return ret; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { + assertEquals("Might be null accessing 'v0'", e.getMessage()); } + } + + @Test + public void testRedeclareStruct() { + CodeGen code = new CodeGen(""" +struct s0 { + int v0; +}; +s0? v1=new s0; +s0? v1; +v1=new s0; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Redefining name 'v1'", e.getMessage()); } + } + + @Test + public void testIter() { + // Build and use an iterator + CodeGen code = new CodeGen( +""" +struct Iter { + int x; + int len; +}; +Iter !i = new Iter; +i.len = arg; +int sum=0; +while( i.x < i.len ) { + sum = sum + i.x; + i.x = i.x + 1; +} +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", code.print()); + } + + + @Test + public void test1() { + CodeGen code = new CodeGen(""" +struct s0 {int v0;}; +s0 !ret = new s0; +while(arg) { + s0 !v0 = new s0; + v0.v0 = arg; + arg = arg-1; + if (arg==5) ret=v0; + +} +return ret; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print()); + } + + @Test + public void test2() { + CodeGen code = new CodeGen(""" +struct s0 {int v0;}; +s0 !ret = new s0; +s0 !v0 = new s0; +while(arg) { + v0.v0 = arg; + arg = arg-1; + if (arg==5) ret=v0; + +} +return ret; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print()); + } + + + @Test + public void test3() { + CodeGen code = new CodeGen(""" +struct s0 {int v0;}; +s0 !ret = new s0; +while(arg < 10) { + s0 !v0 = new s0; + if (arg == 5) ret=v0; + arg = arg + 1; +} +return ret; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print()); + } + + @Test + public void testBug3() { + CodeGen code = new CodeGen(""" +struct s0 { int f0; }; +return new s0; +int v0=null.f0; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Accessing unknown field 'f0' from 'null'",e.getMessage()); } + } + + @Test + public void testBug4() { + CodeGen code = new CodeGen(""" +if(0) { + while(0) if(arg) continue; + int v0=0; + while(1) { + int v2=-arg; + v0=v2; + } +} + """); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testBug5() { + CodeGen code = new CodeGen(""" +struct s0 { + int f0; +}; +if(0) return 0; +else return new s0; +if(new s0.f0) return 0; + """); + code.parse().opto(); + assertEquals("return s0;", code.print()); + } + + @Test + public void testBug6MissedWorklist() { + CodeGen code = new CodeGen(""" +while(0) {} +int v4=0; +while(0<arg) { + v4=v4+1; + while(1) v4=-v4; + while(0) arg=-1; +} +return 0; + """); + code.parse().opto(); + } + + @Test + public void testBug7() { + CodeGen code = new CodeGen(""" +struct s0 { int f0; }; +s0 v0 = new s0; +while(v0.f0) {} +s0 v1 = v0; +return v1; + """); + code.parse().opto(); + assertEquals("return (const)s0;", code.print()); + } + + + @Test + public void testBug8() { + CodeGen code = new CodeGen(""" +int v2=0; +while(0) +while(0) {} +{ + { + { + int v36=0; + { + while(0) { + { + while(-v2) { + { + while(v36) { + while(v2) return 0; + break; + } } + if(-v2) break; + } + } + } + } + } } +} +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testBug9() { + CodeGen code = new CodeGen(""" +int v0=arg==0; +while(v0) continue; +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java new file mode 100644 index 00000000..f6539ac4 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java @@ -0,0 +1,551 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class Chapter11Test { + + // A placeholder test used to rapidly rotate through fuzzer produced issues + @Test + public void testFuzzer() { + CodeGen code = new CodeGen( +""" +while(---- -arg) { + while(-arg) { + while(-----arg*---0) { + int N=--false==false<--arg; + while(N<-4==N) { + N=N+N; + } + } + { + int kzEl_r7EYz=3*----0!=-arg<(37*--97!=0/arg==arg)<=-(--arg!=0); + while(kzEl_r7EYz<---1) { + kzEl_r7EYz=kzEl_r7EYz+kzEl_r7EYz*arg>=24; + if(7) continue; + kzEl_r7EYz=false; + if(-kzEl_r7EYz+arg<-3) continue; + else { + while(kzEl_r7EYz) continue; + arg=----3; + kzEl_r7EYz=-(-----arg*1>-kzEl_r7EYz); + int v2uF_4ba=9; + } + } + } + int pjcp7rv=arg; + { + int m2ya3=--arg-pjcp7rv; + while(m2ya3<-----pjcp7rv>=pjcp7rv+--pjcp7rv!=---pjcp7rv!=-arg<---(-m2ya3)>m2ya3<=-----true>14!=--m2ya3) { + m2ya3=m2ya3+-(--0<=false<pjcp7rv/arg<=-true); + while((--m2ya3)<--arg<-----arg*false!=arg<pjcp7rv*arg) { + pjcp7rv=-3+-arg; + break; + } + while(-3) { + while(67+0-61) m2ya3=m2ya3; + if(----pjcp7rv==---(---arg)) pjcp7rv=0!=-23; + else pjcp7rv=----89; + while(m2ya3!=m2ya3<pjcp7rv) m2ya3=1; + while(--arg<=pjcp7rv) pjcp7rv=-32<=-m2ya3+62; + } + break; + } + } + { + int arg=-1<-9!=(-arg>=-pjcp7rv>=arg<=arg); + while(arg<-arg) { + arg=arg+--arg; + { + while((--93)--pjcp7rv*-pjcp7rv+arg) arg=0-(6>=(--(---41/(0))!=--pjcp7rv)>=---2*33); + pjcp7rv=11; + int c=pjcp7rv; + if(--107) break; + else break; + } + } + } + pjcp7rv=pjcp7rv; + int aR0Wkua=-pjcp7rv==pjcp7rv; + { + if(-0!=-0) while(-true) break; + else { + while(aR0Wkua) pjcp7rv=-0/pjcp7rv; + pjcp7rv=pjcp7rv; + } + } + } + if(-arg) { + { + int Isdlt0fr91=0!=arg; + while(Isdlt0fr91<-Isdlt0fr91==-Isdlt0fr91+true) { + Isdlt0fr91=Isdlt0fr91+-1!=true; + if(-arg) if(--arg<=Isdlt0fr91) Isdlt0fr91=-Isdlt0fr91; + int i=---arg<---arg!=--40; + { + int jVX43Ln4Dm=-(6)==-Isdlt0fr91/----0; + while(jVX43Ln4Dm<--(3-3)) { + jVX43Ln4Dm=jVX43Ln4Dm+-0<25; + i=24<--(jVX43Ln4Dm); + } + } + i=(arg!=----false>=7); + int Te=5>-arg; + break; + } + } + arg=arg!=--true/arg; + { + int UX=arg; + while(UX<-3*arg) { + UX=UX+arg/-1; + arg=-3--(-UX- -47<arg==--arg==arg)/arg- -UX>=-arg; + if(---12) while(-UX) break; + break; + } + } + continue; + } + if(arg) arg=--0; + else if(1) continue; + int DtTlhjYq=19==arg; + { + arg=--DtTlhjYq<=(DtTlhjYq/---DtTlhjYq<=-DtTlhjYq); + if(---arg) continue; + else { + int Zsl=40; + while(Zsl<--Zsl--false>((Zsl))==Zsl!=--DtTlhjYq-DtTlhjYq-DtTlhjYq) { + Zsl=Zsl+arg; + } + } + if((arg)) if(-DtTlhjYq) { + } + else while(((2)==3*arg/DtTlhjYq)<=0==---arg) return false; + else { + int lwbF=arg>=0; + while(lwbF<57) { + lwbF=lwbF+--(-arg+lwbF)>=-2>=arg!=arg; + arg=----0+0; + while(49) break; + if(true) DtTlhjYq=-----arg; + DtTlhjYq=0!=-----(0); + } + } + arg=---arg!=DtTlhjYq/--0; + DtTlhjYq=-DtTlhjYq/(--arg<arg); + if(32) { + int q=-DtTlhjYq; + while(q<arg*(0*q)/0) { + q=q+DtTlhjYq; + DtTlhjYq=arg; + } + } + else if(--arg) if(15) break; + else arg=0==arg<=-55>DtTlhjYq; + else { + } + DtTlhjYq=-0; + { + while((0)<-arg) DtTlhjYq=-DtTlhjYq; + } + if(30/--(arg)>=0) DtTlhjYq=-0>=arg; + else { + int Y2wcIQJ=--DtTlhjYq*---11; + if(arg==arg) { + Y2wcIQJ=-----Y2wcIQJ; + Y2wcIQJ=Y2wcIQJ<7; + if(DtTlhjYq) arg=arg; + else break; + while(-arg!=-31==(false<=-15>=-DtTlhjYq+--6*-----Y2wcIQJ<=---arg!=-Y2wcIQJ<=---0<-arg*--Y2wcIQJ)) DtTlhjYq=arg!=3; + } + continue; + } + arg=DtTlhjYq; + arg=--3/(---arg)>=80>=-DtTlhjYq<=DtTlhjYq; + arg=51+---(-true)!=arg!=----1==-DtTlhjYq; + if(11<=arg) if((arg)+79<32+(-arg)) continue; + else if(true==DtTlhjYq) break; + break; + } +} +if(arg!=62==arg) { + while(---arg) { + { + int bhmDacPM=arg/arg; + while(bhmDacPM<arg) { + bhmDacPM=bhmDacPM+80*((-bhmDacPM)/---bhmDacPM>1)<bhmDacPM-2!=7; + arg=-0; + arg=bhmDacPM>=-arg+-arg; + break; + } + } + int arg=true; + arg=arg<=arg; + { + int KlC=30; + while(KlC<-2) { + KlC=KlC+-1!=25<=-KlC>KlC>----0; + int Ij=arg>=-arg<=---10; + { + int LAbgurVa2R=-arg!=Ij; + while(LAbgurVa2R<-true) { + LAbgurVa2R=LAbgurVa2R+Ij; + KlC=-LAbgurVa2R; + Ij=42*(-Ij>true); + while(Ij) arg=KlC; + KlC=-----7>-(--KlC<--7)==-LAbgurVa2R; + } + } + int a=Ij<=-KlC; + if(Ij) { + int r1W=a!=-false; + } + else { + arg=56>=Ij; + while(54) break; + arg=8>=-(a<--Ij)/a>=-a-KlC; + } + int Yt_T=KlC; + arg=KlC; + } + } + if(----false/--arg/12+-----0) if(-9+-arg) while(arg!=(arg)) arg=--1>=-71-arg!=---(--arg*11+102)/--arg; + else if(----arg) continue; + if(-arg-arg) { + arg=-(-arg)*--(-3)<(-7==-arg----arg); + { + int arg=arg; + while(arg<-0>arg==-arg-(-arg>1+11)) { + arg=arg+6-arg!=--0>---30>=---arg<=arg/(3)+---arg; + } + } + arg=-arg+arg; + arg=--47+-arg<1>arg*(-arg==-arg- -0>3); + arg=(----arg)!=arg+--59; + arg=- -4+arg==-(arg)-arg; + } + else { + while(--true-----true*-25+-18>arg>=-----(-arg)---13) { + arg=-0+--true<false; + continue; + } + arg=true<=arg; + break; + } + arg=arg*22<--(0+-arg); + { + continue; + } + } + arg=1*---((3<=true)==---2); + arg=7+-(-1); + return arg+--true; +} +else { + int o3ypEsewxM=-(---arg); + int vzXB=(-(---11<=--o3ypEsewxM>=--0))<=-0/-arg; + int L=-arg; + int B4mHcrf=---vzXB; + int oZWGdzZl=-o3ypEsewxM; + int K6tQqa1=11; + while(0!=--L) { + int sdZfTM=vzXB*true+K6tQqa1; + while(sdZfTM<false) { + sdZfTM=sdZfTM+arg; + sdZfTM=oZWGdzZl; + K6tQqa1=-K6tQqa1; + arg=---K6tQqa1; + while((3+-K6tQqa1)<oZWGdzZl) { + if(-----(-1)) sdZfTM=-false; + {} + } + K6tQqa1=vzXB!=-(B4mHcrf+-7>false); + sdZfTM=vzXB; + } + } + L=false; + while(31) B4mHcrf=arg; + oZWGdzZl=-0<--oZWGdzZl<=--2; + if(K6tQqa1>=20<=46) { + if(-1+-L) if(oZWGdzZl) { + int wmhv=-----0; + } + int arg=0; + o3ypEsewxM=---(oZWGdzZl); + return -oZWGdzZl/0; + } + else if(arg) { + int yKk5=B4mHcrf>=4>K6tQqa1<-B4mHcrf/o3ypEsewxM; + return -K6tQqa1; + } + else { + if(---0) { + {} + {} + } + return o3ypEsewxM; + } +} +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("return 9;", code.print()); + } + + + @Test + public void testPrimes() { + CodeGen code = new CodeGen( +""" +if( arg < 2 ) return 0; +int primeCount = 1; +int prime = 3; +while( prime <= arg ) { + int isPrime = 1; + // Check for even case, so the next loop need only check odds + if( (prime/2)*2 == prime ) + continue; + // Check odds up to sqrt of prime + int j = 3; + while( j*j <= prime ) { + if( (prime/j)*j == prime ) { + isPrime = 0; + break; + } + j = j + 2; + } + if( isPrime ) + primeCount = primeCount + 1; + prime = prime + 2; +} +return primeCount; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print()); + assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below + assertEquals("1", Eval2.eval(code, 2)); // 2 + assertEquals("2", Eval2.eval(code, 3)); // 2, 3 + assertEquals("2", Eval2.eval(code, 4)); // 2, 3 + assertEquals("3", Eval2.eval(code, 5)); // 2, 3, 5 + assertEquals("4", Eval2.eval(code, 10)); // 2, 3, 5, 7 + } + + @Test + public void testAntiDeps1() { + CodeGen code = new CodeGen( +""" +struct S { int f; }; +S !v=new S; +v.f = 2; +int i=new S.f; +i=v.f; +if (arg) v.f=1; +return i; +"""); + code.parse().opto(); + assertEquals("return 2;", code.print()); + assertEquals("2", Eval2.eval(code, 0)); + assertEquals("2", Eval2.eval(code, 1)); + } + + @Test + public void testAntiDeps2() { + CodeGen code = new CodeGen( +""" +struct S { int f; }; +S !v = new S; +v.f = arg; +S !t = new S; +int i = 0; +if (arg) { + if (arg+1) v = t; + i = v.f; +} else { + v.f = 2; +} +return i; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,.f,0);", code.print()); + } + + @Test + public void testAntiDeps3() { + CodeGen code = new CodeGen( +""" +struct S { int f; }; +S !v0 = new S; +S? v1; +if (arg) v1 = new S; +if (v1) { + v0.f = v1.f; +} else { + v0.f = 2; +} +return v0; +"""); + code.parse().opto(); + assertEquals("return S;", code.print()); + } + + + @Test + public void testAntiDeps4() { + CodeGen code = new CodeGen( +""" +struct S { int f; }; +S !v = new S; +v.f = arg; +S t = new S; +int i = v.f; +if (arg+1) arg= 0; +while (arg) v.f = 2; +return i; +"""); + code.parse().opto(); + assertEquals("return arg;", code.print()); + } + + @Test + public void testAntiDeps5() { + CodeGen code = new CodeGen( +""" +struct S { int f; }; +S !v = new S; +while(1) { + while(arg+1) { arg=arg-1; } + if (arg) break; + v.f = 2; +} +return v; +"""); + code.parse().opto(); + assertEquals("return S;", code.print()); + } + + @Test + public void testAntiDeps6() { + CodeGen code = new CodeGen( +""" +struct s { int v; }; +s !ptr=new s; +while( -arg ) + ptr = new s; +while(1) + arg = arg+ptr.v; +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testAntiDeps7() { + CodeGen code = new CodeGen( +""" +struct S { int f; }; +S !v = new S; +S t = new S; +int i = v.f; +while (arg) { + v.f = arg; + arg = arg-1; +} +return i; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testAntiDeps8() { + CodeGen code = new CodeGen( +""" +struct S { int f; }; +S !v = new S; +S t = new S; +while(arg) { + arg=arg-1; + int f = v.f; + v.f = 2; + if (arg) arg = f; + else v.f = 3; +} +return arg; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testAntiDeps9() { + CodeGen code = new CodeGen( +""" +struct S { int f; }; +S !v = new S; +S t = new S; +if (arg) { + v.f=2; + int i=t.f; + v.f=i; +} +return v; +"""); + code.parse().opto(); + assertEquals("return S;", code.print()); + } + + @Test + public void testExample2() { + CodeGen code = new CodeGen( +""" +struct S { int f; }; +S !v = new S; +int i = arg; +while (arg > 0) { + int j = i/3; + if (arg == 5) + v.f = j; + arg = arg - 1; +} +return v; + """); + code.parse().opto(); + //assertEquals("return new S;", code.print()); + } + + @Test + public void testScheduleUse() { + CodeGen code = new CodeGen( +""" +int v0=0; +while(0>=0) { + u1 v1=0; + v1=v0; + if(v1*0) + v0=-v1; +} +return 0; +"""); + code.parse().opto(); + assertEquals("return Top;", code.print()); + } + + @Test + public void testLoopCarriedDep() { + CodeGen code = new CodeGen( +""" +u32 v0=0; +{ + int v1=0; + while(v1) { + v1=1>>>v0!=0; + v0=v1/0; + } + return v1; +} +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java new file mode 100644 index 00000000..8069ddaa --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java @@ -0,0 +1,61 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.StopNode; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter12Test { + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3.14; +"""); + code.parse().opto(); + assertEquals("return 3.14;", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + } + + @Test + public void testFloat() { + CodeGen code = new CodeGen( +""" +return 3.14; +"""); + code.parse().opto(); + assertEquals("return 3.14;", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + } + + @Test + public void testSquareRoot() { + CodeGen code = new CodeGen( +""" +flt guess = arg; +while( 1 ) { + flt next = (arg/guess + guess)/2; + if( next == guess ) break; + guess = next; +} +return guess; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)*0.5f));", code.print()); + assertEquals("3.0", Eval2.eval(code, 9)); + assertEquals("1.414213562373095", Eval2.eval(code, 2)); + } + + @Test + public void testFPOps() { + CodeGen code = new CodeGen( +""" +flt x = arg; +return x+1==x; +"""); + code.parse().opto(); + assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print()); + assertEquals("0", Eval2.eval(code, 1)); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter13Test.java new file mode 100644 index 00000000..6bf45a22 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter13Test.java @@ -0,0 +1,261 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter13Test { + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3.14; +"""); + code.parse().opto(); + assertEquals("return 3.14;", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + } + + @Test + public void testLinkedList0() { + CodeGen code = new CodeGen( +""" +struct LLI { LLI? next; int i; }; +LLI? !head = null; +while( arg ) { + LLI !x = new LLI; + x.next = head; + x.i = arg; + head = x; + arg = arg-1; +} +return head.next.i; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); } + } + + @Test + public void testLinkedList1() { + CodeGen code = new CodeGen( +""" +struct LLI { LLI? next; int i; }; +LLI? !head = null; +while( arg ) { + LLI !x = new LLI; + x.next = head; + x.i = arg; + head = x; + arg = arg-1; +} +if( !head ) return 0; +LLI? next = head.next; +if( next==null ) return 1; +return next.i; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,0,1,.i);", code.print()); + assertEquals("2", Eval2.eval(code, 3)); + } + + @Test + public void testCoRecur() { + CodeGen code = new CodeGen( +""" +struct int0 { int i; flt0? f; }; +struct flt0 { flt f; int0? i; }; +int0 !i0 = new int0; +i0.i = 17; +flt0 !f0 = new flt0; +f0.f = 3.14; +i0.f = f0; +f0.i = i0; +return f0.i.f.i.i; +"""); + code.parse().opto(); + assertEquals("return 17;", code.print()); + } + + @Test + public void testNullRef0() { + CodeGen code = new CodeGen( +""" +struct N { N? next; int i; }; +N n = new N; +return n.next; +"""); + code.parse().opto(); + assertEquals("return null;", code.print()); + } + + @Test + public void testNullRef1() { + CodeGen code = new CodeGen( +""" +struct M { int m; }; +struct N { M next; int i; }; +N n = new N { next = new M; }; +return n.next; +"""); + code.parse().opto(); + assertEquals("return (const)M;", code.print()); + } + + @Test + public void testNullRef2() { + CodeGen code = new CodeGen( +""" +struct M { int m; }; +struct N { M next; int i; }; +N n = new N { next = null; } +return n.next; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Type null is not of declared type *M",e.getMessage()); } + } + + @Test + public void testNullRef3() { + CodeGen code = new CodeGen( +""" +struct N { N? next; int i; }; +N !n = new N; +n.i = 3.14; +return n.i; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Type 3.14 is not of declared type int",e.getMessage()); } + } + + @Test + public void testNullRef4() { + CodeGen code = new CodeGen("-null-5/null-5;"); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); } + } + + @Test public void testNullRef5() { + CodeGen code = new CodeGen("return null+42;"); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); } + } + + @Test + public void testEmpty() { + CodeGen code = new CodeGen( +""" +struct S{}; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testForwardRef0() { + CodeGen code = new CodeGen( +""" +struct S1 { S2? s; }; +return new S2; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); } + } + + @Test + public void testForwardRef1() { + CodeGen code = new CodeGen( +""" +struct S1 { S2? s; }; +struct S2 { int x; }; +return new S1.s=new S2; +"""); + code.parse().opto(); + assertEquals("return S2;", code.print()); + } + + @Test + public void testcheckNull() { + CodeGen code = new CodeGen( +""" +struct I {int i;}; +struct P { I? pi; }; +P !p1 = new P; +P !p2 = new P; +p2.pi = new I; +p2.pi.i = 2; +if (arg) p1 = new P; +return p1.pi.i + 1; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); } + } + + @Test + public void testCoRecur2() { + CodeGen code = new CodeGen( +""" +struct A { L? a; T? b; F? c; }; A? !a = new A; +struct B { M? a; U? b; G? c; }; B? !b = new B; +struct C { N? a; V? b; H? c; }; C? !c = new C; +struct D { O? a; W? b; I? c; }; D? !d = new D; +struct E { P? a; X? b; J? c; }; E? !e = new E; +struct F { Q? a; Y? b; K? c; }; F? !f = new F; +struct G { R? a; Z? b; L? c; }; G? !g = new G; +struct H { S? a; A? b; M? c; }; H? !h = new H; +struct I { T? a; B? b; N? c; }; I? !i = new I; +struct J { U? a; C? b; O? c; }; J? !j = new J; +struct K { V? a; D? b; P? c; }; K? !k = new K; +struct L { W? a; E? b; Q? c; }; L? !l = new L; +struct M { X? a; F? b; R? c; }; M? !m = new M; +struct N { Y? a; G? b; S? c; }; N? !n = new N; +struct O { Z? a; H? b; T? c; }; O? !o = new O; +struct P { A? a; I? b; U? c; }; P? !p = new P; +struct Q { B? a; J? b; V? c; }; Q? !q = new Q; +struct R { C? a; K? b; W? c; }; R? !r = new R; +struct S { D? a; L? b; X? c; }; S? !s = new S; +struct T { E? a; M? b; Y? c; }; T? !t = new T; +struct U { F? a; N? b; Z? c; }; U? !u = new U; +struct V { G? a; O? b; A? c; }; V? !v = new V; +struct W { H? a; P? b; B? c; }; W? !w = new W; +struct X { I? a; Q? b; C? c; }; X? !x = new X; +struct Y { J? a; R? b; D? c; }; Y? !y = new Y; +struct Z { K? a; S? b; E? c; }; Z? !z = new Z; + +a.a=l; a.b=t; a.c=f; +b.a=m; b.b=u; b.c=g; +c.a=n; c.b=v; c.c=h; +d.a=o; d.b=w; d.c=i; +e.a=p; e.b=x; e.c=j; +f.a=q; f.b=y; f.c=k; +g.a=r; g.b=z; g.c=l; +h.a=s; h.b=a; h.c=m; +i.a=t; i.b=b; i.c=n; +j.a=u; j.b=c; j.c=o; +k.a=v; k.b=d; k.c=p; +l.a=w; l.b=e; l.c=q; +m.a=x; m.b=f; m.c=r; +n.a=y; n.b=g; n.c=s; +o.a=z; o.b=h; o.c=t; +p.a=a; p.b=i; p.c=u; +q.a=b; q.b=j; q.c=v; +r.a=c; r.b=k; r.c=w; +s.a=d; s.b=l; s.c=x; +t.a=e; t.b=m; t.c=y; +u.a=f; u.b=n; u.c=z; +v.a=g; v.b=o; v.c=a; +w.a=h; w.b=p; w.c=b; +x.a=i; x.b=q; x.c=c; +y.a=j; y.b=r; y.c=d; +z.a=k; z.b=s; z.c=e; + +return a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c; + +"""); + code.parse().opto().typeCheck().GCM().localSched(); + assertEquals("return R;", code._stop.toString()); + assertEquals("R{a=C{a=N{a=Y{a=J{a=U{a=F{a=Q{a=B{a=M{a=X{a=I{a=T{a=E{a=P{a=A{a=L{a=W{a=H{a=S{a=D{a=O{a=Z{a=K{a=V{a=G{a=$cyclic,b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic}", Eval2.eval(code, 0)); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter14Test.java new file mode 100644 index 00000000..7af35b56 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter14Test.java @@ -0,0 +1,226 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.StopNode; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter14Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3.14; +"""); + code.parse().opto(); + assertEquals("return 3.14;", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + } + + @Test + public void testRange() { + CodeGen code = new CodeGen( +""" +int b; +if( arg ) b=1; else b=0; +int c = 99; +if( b < 0 ) c = -1; +if( b > 2 ) c = 1; +return c; +"""); + code.parse().opto(); + assertEquals("return 99;", code.print()); + assertEquals("99", Eval2.eval(code, 0)); + } + + @Test + public void testU8() { + CodeGen code = new CodeGen( +""" +u8 b = 123; +b = b + 456;// Truncate +return b; +"""); + code.parse().opto(); + assertEquals("return 67;", code.print()); + assertEquals("67", Eval2.eval(code, 0)); + } + + + @Test + public void testU8While() { + CodeGen code = new CodeGen( +""" +u8 b = 123; +while( b ) b = b + 456;// Truncate +return b; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testU1() { + CodeGen code = new CodeGen( +""" +bool b = 123; +b = b + 456;// Truncate +u1 c = b; // No more truncate needed +return c; +"""); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testAnd() { + CodeGen code = new CodeGen( +""" +int b = 123; +b = b+456 & 31; // Precedence +return b; +"""); + code.parse().opto(); + assertEquals("return 3;", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testRefLoad() { + CodeGen code = new CodeGen( +""" +struct Foo { u1 b; }; +Foo !f = new Foo; +f.b = 123; +return f.b; +"""); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testSigned() { + CodeGen code = new CodeGen( +""" +i8 b = 255; // Chopped +return b; // Sign extend +"""); + code.parse().opto(); + assertEquals("return -1;", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + } + + @Test + public void testI8() { + CodeGen code = new CodeGen( +""" +i8 b = arg; +b = b + 1;// Truncate +return b; +"""); + code.parse().opto(); + assertEquals("return (((((arg<<56)>>56)+1)<<56)>>56);", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + assertEquals("-128", Eval2.eval(code, 127)); + } + + @Test + public void testMask() { + CodeGen code = new CodeGen( +""" +u16 mask = (1<<16)-1; // AND mask +int c = 123456789 & mask; +return c; // +"""); + code.parse().opto(); + assertEquals("return 52501;", code.print()); + assertEquals("52501", Eval2.eval(code, 0)); + } + + @Test + public void testOr() { + CodeGen code = new CodeGen( +""" +return (arg | 123 ^ 456) >>> 1; +"""); + code.parse().opto(); + assertEquals("return (((arg|123)^456)>>>1);", code.print()); + assertEquals("217", Eval2.eval(code, 0)); + } + + @Test + public void testMaskFloat() { + CodeGen code = new CodeGen( +""" +flt f = arg; +arg = f & 1; +return arg; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot '&' flt",e.getMessage()); } + } + + @Test + public void testCloneAnd() { + CodeGen code = new CodeGen(""" +int v0=0; +u32 v1 = 1&(1<<arg)&(1<<arg); +while(arg) v1=-v0; +while(v1) break; +return v1; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,(((1<<arg)&1)&Shl),0);", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testAndHigh() { + CodeGen code = new CodeGen(""" +int v0=0; +if(0&0>>>0) { + while(0) { + u8 v1=0; + v0=0>>>0; + v1=arg; + while(v1+0) {} + } +} +return v0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testTypes() { + CodeGen code = new CodeGen( +""" +i8 xi8 = 123456789; if( xi8 != 21 ) return -8; +i16 xi16 = 123456789; if( xi16 != -13035 ) return -16; +i32 xi32 = 123456789; if( xi32 != 123456789 ) return -32; +i64 xi64 = 123456789; if( xi64 != 123456789 ) return -64; +int xint = 123456789; if( xint != 123456789 ) return -64; + +u1 ui1 = 123456789; if( ui1 != 1 ) return 1; +u8 ui8 = 123456789; if( ui8 != 21 ) return 8; +u16 ui16 = 123456789; if( ui16 != 52501 ) return 16; +u32 ui32 = 123456789; if( ui32 != 123456789 ) return 32; + +flt fflt = 3.141592653589793; if( fflt != 3.141592653589793 ) return 3; +f64 ff64 = 3.141592653589793; if( ff64 != 3.141592653589793 ) return 3; +f32 ff32 = 3.141592653589793; if( ff32 != 3.1415927410125732) return 5; + +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java new file mode 100644 index 00000000..8b138b46 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java @@ -0,0 +1,354 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter15Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3.14; +"""); + code.parse().opto(); + assertEquals("return 3.14;", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + } + + @Test + public void testCyclic() { + CodeGen code = new CodeGen( +""" +struct C { C? l; }; +C !c = new C; +c.l = c; +return c; +"""); + code.parse().opto(); + assertEquals("return C;", code.print()); + assertEquals("C{l=$cyclic}", Eval2.eval(code, 0)); + } + + @Test + public void testSafetyCheck() { + CodeGen code = new CodeGen( +""" +u8[] old = new u8[0]; +u8[] !output = new u8[1]; +int i = 0; +while (i < old#) { + output[i] = old[i]; + i = i + 1; +} +output[i] = 1; +return output; +"""); + code.parse().opto(); + assertEquals("return [u8];", code.print()); + assertEquals("\u0001", Eval2.eval(code, 0)); + } + + @Test + public void testBasic1() { + CodeGen code = new CodeGen( +""" +int[] is = new int[2]; +return is[1]; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testBasic2() { + CodeGen code = new CodeGen( +""" +int[] is = new int[2]; +int[] is2 = new int[2]; +return is[1]; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testBasic3() { + CodeGen code = new CodeGen( +""" +int[] !a = new int[2]; +a[0] = 1; +a[1] = 2; +return a[0]; +"""); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testBasic4() { + CodeGen code = new CodeGen( +""" +struct A { int i; }; +A?[] !a = new A?[2]; +return a; +"""); + code.parse().opto(); + assertEquals("return [*A?];", code.print()); + assertEquals("*A {int !i; }?[ null,null]", Eval2.eval(code, 0)); + } + + @Test + public void testBasic5() { + CodeGen code = new CodeGen( +""" +struct S { int x; flt y; }; +// A new S +S !s = new S; s.x=99; s.y = 3.14; + +// Double-d array of Ss. Fill in one row. +S?[]?[] !iss = new S?[]?[2]; +iss[0] = new S?[7]; +iss[0][2] = s; + +// Now pull out the filled-in value, with null checks +flt rez; +S?[]? is = iss[arg]; +if( !is ) rez = 1.2; +else { + S? i = is[2]; + if( !i ) rez = 2.3; + else rez = i.y; +} +return rez; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1.2,.y,2.3);", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + assertEquals("1.2", Eval2.eval(code, 1)); + } + + @Test + public void testBasic6() { + CodeGen code = new CodeGen( +""" +struct S { int x; flt y; }; +// A new S +S !s = new S; s.x=99; s.y = 3.14; + +// Double-d array of Ss. Fill in one row. +S?[]?[] !iss = new S?[]?[2]; +iss[0] = new S?[7]; +iss[0][2] = s; + +// Now pull out the filled-in value, with null checks +flt rez = 1.2; +if( iss[arg] ) + if( iss[arg][2] ) + rez = iss[arg][2].y; +return rez;"""); + code.parse().opto().typeCheck(); + assertEquals("return Phi(Region,1.2,1.2,.y);", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks + catch( Exception e ) { assertEquals("Index 10 out of bounds for length 2",e.getMessage()); } + } + + @Test + public void testTree() { + CodeGen code = new CodeGen( +""" +// Can we define a forward-reference array? +struct Tree { Tree?[]? _kids; }; +Tree !root = new Tree; +root._kids = new Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES???? +root._kids[0] = new Tree; +return root; +"""); + code.parse().opto(); + assertEquals("return Tree;", code.print()); + assertEquals("Tree{_kids=*Tree?[ Tree{_kids=null},null]}", Eval2.eval(code, 0)); + } + + @Test + public void testNestedStructAddMemProj() { + CodeGen code = new CodeGen( +""" +struct S { int a; int[] b; }; +return 0; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + @Test + public void testRollingSum() { + CodeGen code = new CodeGen( +""" +int[] !ary = new int[arg]; +// Fill [0,1,2,3,4,...] +int i=0; +while( i < ary# ) { + ary[i] = i; + i = i+1; +} +// Fill [0,1,3,6,10,...] +i=0; +while( i < ary# - 1 ) { + ary[i+1] = ary[i+1] + ary[i]; + i = i+1; +} +return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 +"""); + code.parse().opto(); + assertEquals("return (.[]+(.[]*1000));", code.print()); + assertEquals("1006", Eval2.eval(code, 4)); + } + + @Test + public void sieveOEratosthenes() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp")); + CodeGen code = new CodeGen(src+"return sieve(arg);"); + code.parse().opto(); + assertEquals("return [u32];", code.print()); + assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); + } + + @Test + public void testNewNodeInit() { + CodeGen code = new CodeGen( +""" +struct S {int i; flt f;}; +S !s1 = new S; +S !s2 = new S; +s2.i = 3; +s2.f = 2.0; +if (arg) s1 = new S; +return s1.i + s1.f; +"""); + code.parse().opto(); + assertEquals("return ((flt).i+.f);", code.print()); + assertEquals("0.0", Eval2.eval(code, 0)); + } + + + @Test + public void testBad0() { + CodeGen code = new CodeGen( +""" +return new flt; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); } + } + + @Test + public void testBad1() { + CodeGen code = new CodeGen( +""" +int is = new int[2]; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Type *[int] is not of declared type int",e.getMessage()); } + } + + @Test + public void testBad2() { + CodeGen code = new CodeGen( +""" +int[] is = new int[3.14]; +return is[1]; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Cannot allocate an array with length 3.14",e.getMessage()); } + } + + @Test + public void testBad3() { + CodeGen code = new CodeGen(""" +int[] is = new int[arg]; +return is[1]; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 4)); + try { Eval2.eval(code,-1); fail(); } + catch( NegativeArraySizeException e ) { assertEquals("-1",e.getMessage()); } + // This test will not pass (i.e., failed range-check) until we have range checks. + // Without the range check, the load `is[1]` optimizes against the `new int[]` + // and assumes it loads a zero then folds away, and the Eval2 never sees an OOB load. + //try { Eval2.eval(code,0); fail(); } + //catch( ArrayIndexOutOfBoundsException e ) { assertEquals("Array index 1 out of bounds for array length 0",e.getMessage()); } + } + + @Test + public void testProgress() { + CodeGen code = new CodeGen(""" +i8 v1=0&0; +u8 v2=0; +byte v4=0; +if(0) {} +while(v2<0) { + v4=0-v1; + break; +} +int v5=0&0; +while(v5+(0&0)) { + int v7=0&0; + while(v7) + v4=0>>>v5; + while(v1) + return 0; +} +return v1; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testSharpNot() { + CodeGen code = new CodeGen( +""" +if(0>>0) {} +while(0) {} +u32 v7=0; +int v8=0; +while(0<--1>>>---(v7*0==v8)) {} +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } + + + @Test + public void testProgress2() { + CodeGen code = new CodeGen( +""" +if(1) {} +else { + while(arg>>>0&0>>>0) {} + byte v3=0>>>0; + while(0) {} + int v7=0>>>0; + while(v7<0>>>0) { + while(0+v7<=0) if(1) arg=-12; + if(arg) { + v3=arg+v3+0; + arg=0; + } + } +} +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter16Test.java new file mode 100644 index 00000000..930624e5 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter16Test.java @@ -0,0 +1,261 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.evaluator.Evaluator; +import com.seaofnodes.simple.node.StopNode; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter16Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 3.14; +"""); + code.parse().opto(); + assertEquals("return 3.14;", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + } + + + @Test + public void testMulti0() { + CodeGen code = new CodeGen( +""" +int x, y; +return x+y; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + @Test + public void testMulti1() { + CodeGen code = new CodeGen( +""" +int x=2, y=x+1; +return x+y; +"""); + code.parse().opto(); + assertEquals("return 5;", code.print()); + assertEquals("5", Eval2.eval(code, 0)); + } + +// @Test +// public void testFinal0() { +// CodeGen code = new CodeGen( +//""" +//int !x=2; +//x=3; +//return x; +//"""); +// try { code.parse().opto(); fail(); } +// catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } +// } + + @Test + public void testFinal1() { + CodeGen code = new CodeGen( +""" +int x=2, y=3; +if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x +return y; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,9,3);", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + assertEquals("9", Eval2.eval(code, 1)); + } + + @Test + public void testConstruct0() { + CodeGen code = new CodeGen(""" +struct X { int x=3; }; +X z = new X; +return z.x; +"""); + code.parse().opto(); + assertEquals("return 3;", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testConstruct1() { + CodeGen code = new CodeGen(""" +struct X { int !x; }; +X z = new X { x=3; }; +return z.x; +"""); + code.parse().opto(); + assertEquals("return 3;", code.print()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testConstruct2() { + CodeGen code = new CodeGen(""" +struct X { int x=3; }; +X z = new X { x = 4; }; +return z.x; +"""); + code.parse().opto(); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + + @Test + public void testStructFinal0() { + CodeGen code = new CodeGen(""" +struct Point { int !x, !y; }; +Point p = new Point { x=3; y=4; }; +return p; +"""); + code.parse().opto(); + assertEquals("return (const)Point;", code.print()); + assertEquals("Point{x=3,y=4}", Eval2.eval(code, 0)); + } + + @Test + public void testStructFinal1() { + CodeGen code = new CodeGen(""" +struct Point { int x=3, y=4; }; +Point p = new Point { x=5; y=6; }; +p.x++; +return p; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test + public void testStructFinal2() { + CodeGen code = new CodeGen(""" +struct Point { int x=3, y=4; }; +Point p = new Point; +p.x++; +return p; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test + public void testStructFinal3() { + CodeGen code = new CodeGen(""" +struct Point { var x; var y; }; +Point p = new Point; +p.x++; +return p; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); } + } + + @Test + public void testStructFinal4() { + CodeGen code = new CodeGen(""" +struct Point { val x=3; val y=4; }; +Point p = new Point; +p.x++; +return p; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } + } + + @Test + public void testStructFinal5() { + CodeGen code = new CodeGen(""" +struct Point { var x=3; var y=4; }; +Point !p = new Point; +p.x++; +return p; +"""); + code.parse().opto(); + assertEquals("return Point;", code.print()); + assertEquals("Point{x=4,y=4}", Eval2.eval(code, 0)); + } + + // Same as the Chapter13 test with the same name, but using the new + // constructor syntax + @Test + public void testLinkedList1() { + CodeGen code = new CodeGen( +""" +struct LLI { LLI? next; int i; }; +LLI? !head = null; +while( arg ) { + head = new LLI { next=head; i=arg; }; + arg = arg-1; +} +if( !head ) return 0; +LLI? next = head.next; +if( !next ) return 1; +return next.i; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,0,1,.i);", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + assertEquals("2", Eval2.eval(code, 3)); + } + + @Test + public void testLinkedList2() { + CodeGen code = new CodeGen( +""" +struct LLI { LLI? next; int i; }; +LLI? !head = null; +while( arg ) { + head = new LLI { + next=head; + // Any old code in the constructor + int !tmp=arg; + while( arg > 10 ) { + tmp = tmp + arg; + arg = arg - 1; + } + i=tmp; + }; + arg = arg-1; +} +if( !head ) return 0; +LLI? next = head.next; +if( !next ) return 1; +return next.i; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,0,1,.i);", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + assertEquals("2", Eval2.eval(code, 11)); + } + + @Test + public void testSquare() { + CodeGen code = new CodeGen( +""" +struct Square { + flt !side = arg; + // Newtons approximation to the square root, computed in a constructor. + // The actual allocation will copy in this result as the initial + // value for 'diag'. + flt !diag = arg*arg/2; + while( 1 ) { + flt next = (side/diag + diag)/2; + if( next == diag ) break; + diag = next; + } +}; +return new Square; +"""); + code.parse().opto(); + assertEquals("return Square;", code.print()); + assertEquals("Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3)); + assertEquals("Square{side=4.0,diag=2.0}", Eval2.eval(code, 4)); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter17Test.java new file mode 100644 index 00000000..78515083 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter17Test.java @@ -0,0 +1,555 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter17Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 1; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + + // --------------------------------------------------------------- + @Test + public void testInc0() { + CodeGen code = new CodeGen(""" +return arg++; +"""); + code.parse().opto(); + assertEquals("return arg;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testInc1() { + CodeGen code = new CodeGen(""" +return arg+++arg++; +"""); + code.parse().opto(); + assertEquals("return ((arg<<1)+1);", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testInc2() { + CodeGen code = new CodeGen(""" +// -(arg--)-(arg--) +return -arg---arg--; +"""); + code.parse().opto(); + assertEquals("return (-((arg<<1)+-1));", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testInc3() { + CodeGen code = new CodeGen(""" +int[] !xs = new int[arg]; +xs[0]++; +xs[1]++; +return xs[0]; +"""); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 2)); + } + + @Test + public void testInc4() { + CodeGen code = new CodeGen(""" +u8[] !xs = new u8[1]; +xs[0]--; +return xs[0]; +"""); + code.parse().opto(); + assertEquals("return 255;", code.print()); + assertEquals("255", Eval2.eval(code, 2)); + } + + @Test + public void testInc5() { + CodeGen code = new CodeGen(""" +struct S { u16 x; }; +S !s = new S; +s.x--; +return s.x; +"""); + code.parse().opto(); + assertEquals("return 65535;", code.print()); + assertEquals("65535", Eval2.eval(code, 2)); + } + + @Test + public void testInc6() { + CodeGen code = new CodeGen("return --arg;"); + code.parse().opto(); + assertEquals("return (arg+-1);", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + } + + @Test + public void testInc7() { + CodeGen code = new CodeGen("u8 x=0; return --x;"); + code.parse().opto(); + assertEquals("return 255;", code.print()); + assertEquals("255", Eval2.eval(code, 0)); + } + + @Test + public void testInc8() { + CodeGen code = new CodeGen("int x; x+=2; return x+=3;"); + code.parse().opto(); + assertEquals("return 5;", code.print()); + assertEquals("5", Eval2.eval(code, 0)); + } + + @Test + public void testInc9() { + CodeGen code = new CodeGen("flt x = 1.5; x++; return x;"); + code.parse().opto(); + assertEquals("return 2.5f;", code.print()); + assertEquals("2.5", Eval2.eval(code, 0)); + } + + @Test public void testInt10() { + CodeGen code = new CodeGen("flt x = 1.5; x++; int i = x; return i;"); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Type 2.5f is not of declared type int",e.getMessage()); } + } + + + // --------------------------------------------------------------- + @Test public void testVar0() { + CodeGen code = new CodeGen("var d; return d;"); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); } + } + + @Test public void testVar1() { + CodeGen code = new CodeGen("val d; return d;"); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); } + } + + @Test public void testVar2() { + CodeGen code = new CodeGen("int x; x=3; x++; return x; // Ok, no initializer so x is mutable "); + code.parse().opto(); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + @Test public void testVar3() { + CodeGen code = new CodeGen("int x=3; x++; return x; // Ok, primitive so x is mutable despite initializer "); + code.parse().opto(); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + @Test public void testVar4() { + CodeGen code = new CodeGen("struct S{int x;}; S? s; s=new S; s.x++; return s.x; // Ok, no initializer so x is mutable "); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test public void testVar5() { + CodeGen code = new CodeGen("struct S{int x;}; S? s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable "); + code.parse().opto(); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + @Test public void testVar6() { + CodeGen code = new CodeGen("struct S{int x;}; S s=new S; s.x++; return s.x; // Error initializer so x is immutable "); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar7() { + CodeGen code = new CodeGen("struct S{int x;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable "); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar8() { + CodeGen code = new CodeGen("struct S{int x;}; S !s=new S; s.x++; return s.x; // Ok, has '!' so s.x is mutable "); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test public void testVar9() { + CodeGen code = new CodeGen("struct S{int x;}; var s=new S; s.x++; return s.x; // Ok, has var so s.x is mutable "); + code.parse().opto(); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test public void testVar10() { + CodeGen code = new CodeGen("struct S{int x;}; val s=new S; s.x++; return s.x; // Error, has val so x is immutable "); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar11() { + CodeGen code = new CodeGen(""" +struct Bar { int x; }; +Bar !bar = new Bar; +bar.x = 3; // Ok, bar is mutable + +struct Foo { Bar? !bar; int y; }; +Foo !foo = new Foo { bar = bar; }; +foo.bar = bar; // Ok foo is mutable +foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable + +val xfoo = foo; // Throw away mutability +xfoo.bar.x++; // Error, cannot mutate through xfoo + """); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar12() { + CodeGen code = new CodeGen(""" +struct Bar { int x; }; +Bar !bar = new Bar; +bar.x = 3; // Ok, bar is mutable + +struct Foo { Bar? !bar; int y; }; +Foo !foo = new Foo; +foo.bar = bar; // Ok bar is mutable +foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable + +val xfoo = foo; // Throw away mutability +int x4 = xfoo.bar.x; // Ok to read through xfoo, gets 4 +foo.bar.x++; // Bumps to 5 +int x5 = xfoo.bar.x; // Ok to read through xfoo, gets 5 +return x4*10+x5; +"""); + code.parse().opto(); + assertEquals("return 45;", code.print()); + assertEquals("45", Eval2.eval(code, 0)); + } + + @Test + public void testVar13() { + CodeGen code = new CodeGen(""" +int i,i++; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); } + } + + + @Test + public void testVar14() { + CodeGen code = new CodeGen(""" +struct B {}; +struct A { B b; }; +A x = new A { + return b; // read before init + b = new B; +}; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); } + } + + @Test + public void testVar15() { + CodeGen code = new CodeGen(""" +struct B {}; +struct A { B b; }; +return new A { + if (arg) b = new B; // Constructor ends with partial init of b +}.b; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); } + } + + @Test + public void testVar16() { + CodeGen code = new CodeGen( +""" +struct S{}; +val x = 1; +val s = new S{x = 2;}; +return x; +""" +); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } + } + + @Test + public void testVar17() { + CodeGen code = new CodeGen( +""" +struct Person { + bool focused; +}; +val focus = { Person p -> + p.focused = true; +}; +Person me; // Person !me = new Person; +focus(me); +return me; +""" +); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); } + } + + // --------------------------------------------------------------- + @Test + public void testTrinary0() { + CodeGen code = new CodeGen(""" +return arg ? 1 : 2; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1,2);", code.print()); + assertEquals("2", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + } + + @Test + public void testTrinary1() { + CodeGen code = new CodeGen(""" +return arg ? 0 : arg; +"""); + code.parse().opto(); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testTrinary2() { + CodeGen code = new CodeGen(""" +struct Bar { int x; }; +var b = arg ? new Bar : null; +return b ? b.x++ + b.x++ : -1; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + } + + @Test + public void testTrinary3() { + CodeGen code = new CodeGen(""" +struct Bar { int x; }; +var b = arg ? new Bar; +return b ? b.x++ + b.x++ : -1; +"""); + code.parse().opto(); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + } + + @Test + public void testTrinary4() { + // This test case will benefit from an unzipping transformation + CodeGen code = new CodeGen(""" +struct Bar { Bar? next; int x; }; +var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=1; }; +return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0" +"""); + code.parse().opto(); + assertEquals("return Phi(Region,.x,0);", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + assertEquals("2", Eval2.eval(code, 2)); + } + + @Test + public void testTrinary5() { + CodeGen code = new CodeGen(""" +flt f=arg?1:1.2; +return f; // missing widening +"""); + code.parse().opto(); + assertEquals("return Phi(Region,1.0f,1.2);", code.print()); + assertEquals("1.2", Eval2.eval(code, 0)); + } + + + @Test + public void testTrinary6() { + CodeGen code = new CodeGen(""" +struct S{}; +return arg ? 7 : new S; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); } + } + + // --------------------------------------------------------------- + @Test + public void testFor0() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; i<arg; i++ ) + sum += i; +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor1() { + CodeGen code = new CodeGen(""" +int sum=0, i=0; +for( ; i<arg; i++ ) + sum += i; +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor2() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; ; i++ ) { + if( i>=arg ) break; + sum += i; +} +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor3() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; i<arg; ) + sum += i++; +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor4() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; i<arg; i++ ) + sum += i; +return i; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); } + } + @Test + public void testFor5() { + CodeGen code = new CodeGen(""" +for(;;arg++;) {} +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); } + } + + + + // --------------------------------------------------------------- + @Test + public void testForward0() { + CodeGen code = new CodeGen(""" +struct A{ + B? f1; + B? f2; +}; +return new A; +"""); + code.parse().opto(); + assertEquals("return A;", code.print()); + assertEquals("A{f1=null,f2=null}", Eval2.eval(code,0)); + } + + @Test + public void testForward1() { + CodeGen code = new CodeGen(""" +struct A{ + B?[]? nil_array_of_b; + B?[] not_array_of_b = new B?[0]; +}; +return new A.not_array_of_b; +"""); + code.parse().opto(); + assertEquals("return (const)[*B?];", code.print()); + assertEquals("*B?[]", Eval2.eval(code,0)); + } + + // --------------------------------------------------------------- + @Test + public void testLinkedList2() { + CodeGen code = new CodeGen(""" +struct LLI { LLI? next; int i; }; +LLI? !head = null; +while( arg-- ) + head = new LLI { next=head; i=arg; }; +int sum=0; +var ptr = head; // A read-only ptr, to be assigned from read-only next fields +for( ; ptr; ptr = ptr.next ) + sum += ptr.i; +return sum; +"""); + code.parse().opto(); + assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print()); + assertEquals("45", Eval2.eval(code, 10)); + } + + @Test + public void sieveOfEratosthenes() { + CodeGen code = new CodeGen( +""" +var ary = new bool[arg], primes = new int[arg]; +var nprimes=0, p=0; +// Find primes while p^2 < arg +for( p=2; p*p < arg; p++ ) { + // skip marked non-primes + while( ary[p] ) p++; + // p is now a prime + primes[nprimes++] = p; + // Mark out the rest non-primes + for( int i = p + p; i < ary#; i += p ) + ary[i] = true; +} +// Now just collect the remaining primes, no more marking +for( ; p < arg; p++ ) + if( !ary[p] ) + primes[nprimes++] = p; +// Copy/shrink the result array +var !rez = new int[nprimes]; +for( int j=0; j<nprimes; j++ ) + rez[j] = primes[j]; +return rez; +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("return [int];", code.print()); + assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java new file mode 100644 index 00000000..c0e28412 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -0,0 +1,370 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen.Phase; +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.junit.Assert.fail; + +public class Chapter18Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 0; +"""); + code.driver(Phase.LocalSched); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 2)); + } + + @Test + public void testPhiParalleAssign() { + CodeGen code = new CodeGen( +""" +int a = 1; +int b = 2; +while(arg--) { + int t = a; + a = b; + b = t; +} +return a; +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString()); + assertEquals("1", Eval2.eval(code, 0)); + assertEquals("2", Eval2.eval(code, 1)); + assertEquals("1", Eval2.eval(code, 2)); + assertEquals("2", Eval2.eval(code, 3)); + } + + + // --------------------------------------------------------------- + @Test + public void testType0() { + CodeGen code = new CodeGen( +""" +{int -> int}? x2 = null; // null function ptr +return x2; +"""); + code.parse().opto(); + assertEquals("return null;", code._stop.toString()); + assertEquals("null", Eval2.eval(code, 0 ) ); + } + + @Test + public void testFcn0() { + CodeGen code = new CodeGen( +""" +{int -> int}? sq = { int x -> + x*x; +}; +"""); + code.parse().opto(); + assertEquals("return (Parm_x(sq,int)*x);", code._stop.toString()); + //assertEquals("{ int -> int #1}", Eval2.eval(code, 3)); + } + + @Test + public void testFcn1() { + CodeGen code = new CodeGen( +""" +var sq = { int x -> + x*x; +}; +return sq(arg)+sq(3); +"""); + code.driver(Phase.LocalSched); + assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,int)*x); ]", code._stop.toString()); + assertEquals("13", Eval2.eval(code, 2)); + } + + // Function scope test + @Test + public void testFcn2() { + CodeGen code = new CodeGen( +""" +int cnt=1; +return { -> cnt; }; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); } + } + + // Function scope test + @Test + public void testFcn3() { + CodeGen code = new CodeGen( +""" +val cnt=2; +return { -> cnt; }(); +"""); + code.parse().opto(); + assertEquals("return 2;", code._stop.toString()); + assertEquals("2", Eval2.eval(code, 0)); + } + + // Function variables + @Test + public void testFcn4() { + CodeGen code = new CodeGen( +""" +var fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; +return fcn(3); +"""); + code.parse().opto(); + assertEquals("Stop[ return #2; return (Parm_x($fun1,int,3)*x); return (Parm_x($fun2,int,3)<<1); ]", code._stop.toString()); + assertEquals("6", Eval2.eval(code, 0)); + assertEquals("9", Eval2.eval(code, 1)); + } + + // Recursive factorial test + @Test + public void testFcn5() { + CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);"); + code.parse().opto().typeCheck(); + assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); ]", code._stop.toString()); + assertEquals( "1", Eval2.eval(code, 0)); + assertEquals( "1", Eval2.eval(code, 1)); + assertEquals( "2", Eval2.eval(code, 2)); + assertEquals( "6", Eval2.eval(code, 3)); + assertEquals("24", Eval2.eval(code, 4)); + } + + @Test + public void testFcn6() { + CodeGen code = new CodeGen( +""" +struct S { int i; }; +val newS = { int x -> return new S { i=x; }; }; +return newS(1).i; +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("return 1;", code._stop.toString()); + assertEquals("1", Eval2.eval(code, 0)); + } + + // Double forward reference + @Test + public void testFcn7() { + CodeGen code = new CodeGen( +""" +if( arg ? f : g ) return 1; +val f = {->1;}; +val g = {->2;}; +return 2; +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testFcn8() { + CodeGen code = new CodeGen( +""" +{int -> int}? i2i = null; +var id = {{int->int} f-> return f;}; +for(;;) { + if (i2i) return i2i(arg); + var x = {int i-> return i;}; + arg = x(3); + i2i = id(x); +} +"""); + code.driver(Phase.LocalSched); + assertEquals("Stop[ return #2; return Parm_i(x,int); ]", code._stop.toString()); + assertEquals("3", Eval2.eval(code, 0)); + } + + @Test + public void testFcn9() { + CodeGen code = new CodeGen( +""" +{int -> int}? i2i = null; +for(;;) { + if (i2i) return i2i(arg); + var x = {int i-> return i;}; + arg = x(3); +} +"""); + code.driver(Phase.LocalSched); + assertEquals("return Top;", code._stop.toString()); + assertEquals("null", Eval2.eval(code, 0)); + } + + + @Test + public void testFcn10() { + CodeGen code = new CodeGen( +""" +struct Person { + int age; +}; + +val fcn = { Person?[] ps, int x -> + val tmp = ps[x]; + if( ps[x] ) + ps[x].age++; +}; + +var ps = new Person?[2]; +ps[0] = new Person; +ps[1] = new Person; +fcn(ps,1); +"""); + code.driver(Phase.LocalSched); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 0)); + } + + // Function break + @Test + public void testErr1() { + CodeGen code = new CodeGen( +""" +for(;;) { + val f = { -> + break; + }; + f(); + return 2; +} +return 1; +"""); + try { code.parse().opto(); fail(); } + catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); } + } + + // Calling and inlining a null function + @Test + public void testErr2() { + CodeGen code = new CodeGen( +""" +{int -> int}? !i2i = { int i -> return i; }; +for(;;) { + if (i2i(2) == arg) break; + i2i = null; +} +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); } + } + + + @Test + public void testErr3() { + CodeGen code = new CodeGen( +""" +val f = { int i, int j -> return i+j; }; +return f(); +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); } + } + + + @Test + public void testErr4() { + CodeGen code = new CodeGen( +""" +struct S { + {int} f = { -> x(); return 0; }; // Do not let fref x be a field +}; +val x = { -> return 1; }; +S? s = null; +for(;;) { + if (s) return s.x; +} +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); } + } + + @Test + public void testErr5() { + CodeGen code = new CodeGen( +""" +val g = { -> + g(); +}; +return 0; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); } + } + + + // Mutual recursion. Fails without SCCP to lift the recursive return types. + @Ignore @Test + public void testFcnMutRec() { + CodeGen code = new CodeGen( +""" +val is_even = { int x -> x ? is_odd (x-1) : true ; }; +val is_odd = { int x -> x ? is_even(x-1) : false; }; +return is_even(arg); +"""); + code.parse().opto(); + assertEquals("Stop[ return is_even( arg); return Phi(Region,Phi(Region,is_even( ((Parm_x(is_even,int,arg,Sub)-1)-1)),0),1); ]", code._stop.toString()); + assertEquals("1", Eval2.eval(code, 0)); + assertEquals("0", Eval2.eval(code, 1)); + assertEquals("1", Eval2.eval(code, 2)); + assertEquals("0", Eval2.eval(code, 3)); + } + + // Forward ref to 'x' means that + @Ignore @Test + public void testForwardRef1() { + CodeGen code = new CodeGen( +""" +struct S { + { int } f = { -> return x(); }; +}; +val x = { -> return 1; }; +S? s = null; +for(;;) { + if (s) return s.f; +} +"""); + code.parse().opto().typeCheck().GCM(); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 0)); + } + + // Inline hidden called more than once + @Test + public void testInline() { + CodeGen code = new CodeGen( +""" +{int->int}?! i2i = {int i->return i;}; +{{int->int}->{int->int}}! f2f = {{int->int} f->return f;}; +val o = i2i; +if (arg) i2i = null; +if (i2i) return i2i(arg); +return f2f(o)(1); +"""); + code.driver(Phase.LocalSched); + assertEquals("Stop[ return Phi(Region,#2,#2); return Parm_i(o,int); ]", code._stop.toString()); + assertEquals("1", Eval2.eval(code, 2)); + } + + @Test + public void testOperField() { + CodeGen code = new CodeGen( +""" +struct Person { + int coffee_count; +}; +Person !p = new Person; +p.coffee_count += 1; +return p.coffee_count; +"""); + code.driver(Phase.LocalSched); + assertEquals("return 1;", code._stop.toString()); + assertEquals("1", Eval2.eval(code, 2)); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java new file mode 100644 index 00000000..bce75c81 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -0,0 +1,331 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen.Phase; +import com.seaofnodes.simple.codegen.CodeGen; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter19Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen(""" +return 0; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 2)); + } + + + @Test + public void testString() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp")); + CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched(); + assertEquals("Stop[ return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); return Phi(Region,1,0,0,1); ]", code._stop.toString()); + //assertEquals("-4898613127354160978", Eval2.eval(code, 2)); + } + + @Test + public void testBasic0() { + CodeGen code = new CodeGen("return 0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return 0;", code._stop.toString()); + } + + @Test + public void testBasic1() { + CodeGen code = new CodeGen("return arg+1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (inc,arg);", code._stop.toString()); + } + + @Test + public void testBasic2() { + CodeGen code = new CodeGen("return -17;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return -17;", code._stop.toString()); + } + + + @Test + public void testBasic3() { + CodeGen code = new CodeGen("return arg==1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (set==,(cmp,arg));", code._stop.toString()); + } + + @Test + public void testBasic4() { + CodeGen code = new CodeGen("return arg<<1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (shli,arg);", code._stop.toString()); + } + + @Test + public void testBasic5() { + CodeGen code = new CodeGen("return arg >> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (sari,arg);", code._stop.toString()); + } + + @Test + public void testBasic6() { + CodeGen code = new CodeGen("return arg >>> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (shri,arg);", code._stop.toString()); + } + + @Test + public void testBasic7() { + CodeGen code = new CodeGen("return arg / 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (div,arg,2);", code._stop.toString()); + } + + @Test + public void testBasic8() { + CodeGen code = new CodeGen("return arg * 6;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (muli,arg);", code._stop.toString()); + } + + @Test + public void testBasic9() { + CodeGen code = new CodeGen("return arg & 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (andi,arg);", code._stop.toString()); + } + + @Test + public void testBasic10() { + CodeGen code = new CodeGen("return arg | 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (ori,arg);", code._stop.toString()); + } + + @Test + public void testBasic11() { + CodeGen code = new CodeGen("return arg ^ 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (xori,arg);", code._stop.toString()); + } + + @Test + public void testBasic12() { + CodeGen code = new CodeGen("return arg + 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (addf,(cvtf,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic13() { + CodeGen code = new CodeGen("return arg - 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (subf,(cvtf,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic14() { + CodeGen code = new CodeGen("return arg * 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (mulf,(cvtf,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic15() { + CodeGen code = new CodeGen("return arg / 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (mulf,(cvtf,arg),0.5f);", code._stop.toString()); + } + + @Test + public void testBasic16() { + CodeGen code = new CodeGen( +""" +int arg1 = arg + 1; +return arg1 / arg;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (div,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testBasic17() { + CodeGen code = new CodeGen( +""" +int arg1 = arg + 1; +return arg1 * arg; +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (mul,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testToFloat() { + CodeGen code = new CodeGen(""" +int a = arg; +return a + 2.0; +""" + ).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (addf,(cvtf,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testIfStmt() { + CodeGen code = new CodeGen( +""" +int a = 1; +if (arg == 1) + a = arg+2; +else { + a = arg-3; +} +return a;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print()); + } + + @Test + public void testIfMerge2() { + CodeGen code = new CodeGen( +""" +int a=arg+1; +int b=arg+2; +if( arg==1 ) + b=b+a; +else + a=b+1; +return a+b;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print()); + } + + @Test + public void testLoop() { + CodeGen code = new CodeGen( +""" +int sum=0; +for( int i=0; i<arg; i++ ) + sum += i; +return sum;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print()); + } + + @Test + public void testAlloc1() { + CodeGen code = new CodeGen( +""" +struct S { int a; S? c; }; +return new S;"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return S;", code.print()); + } + + @Test + public void testLea1() { + CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (lea,arg,(div,arg,3));", code.print()); + } + + @Test + public void testLea2() { + CodeGen code = new CodeGen("int x = arg/3; return arg+x*4+7;"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (lea,arg,(div,arg,3));", code.print()); + } + + @Test + public void testLea3() { + CodeGen code = new CodeGen("int x = arg/3; return x*4+arg;"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (lea,arg,(div,arg,3));", code.print()); + } + + @Test + public void testAlloc2() { + CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return .[];", code.print()); + } + + @Test + public void testAlloc3() { + CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1]+3;"); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return .[];", code.print()); + } + + @Test + public void testArray1() { + CodeGen code = new CodeGen( +""" +int[] !ary = new int[arg]; +// Fill [0,1,2,3,4,...] +for( int i=0; i<ary#; i++ ) + ary[i] = i; +// Fill [0,1,3,6,10,...] +for( int i=0; i<ary#-1; i++ ) + ary[i+1] += ary[i]; +return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return .[];", code.print()); + } + + @Test + public void testArray2() { + CodeGen code = new CodeGen( +""" +flt[] !A = new flt[arg], !B = new flt[arg]; +// Fill [0,1,2,3,4,...] +for( int i=0; i<A#; i++ ) + A[i] = i; +for( int i=0; i<A#; i++ ) + B[i] += A[i]; +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return 0;", code.print()); + } + + @Test + public void testArray3() { + CodeGen code = new CodeGen( +""" +byte[] !A = new byte[arg]; +for( int i=0; i<A#; i++ ) + A[i]++; +return A[1]; +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return .[];", code.print()); + } + + @Test + public void testNewton() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp")) + + "flt farg = arg; return test_sqrt(farg);"; + CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return Phi(Loop,(cvtf,arg),(mulf,(addf,(divf,cvtf,Phi_guess),Phi_guess),0.5f));", code.print()); + }; + + + @Test + public void sieveOfEratosthenes() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp")); + CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return [u32];", code.print()); + //assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); + } + + + @Test + public void testFcn1() { + CodeGen code = new CodeGen( +""" +val fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; +return fcn(2)*10 + fcn(3); +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun1,int),x); return (shli,Parm_x($fun2,int)); ]", code.print()); + } + + @Test + public void testFcn2() { + CodeGen code = new CodeGen( +""" +val sq = { int x -> x*x; }; +return sq(arg) + sq(3); +"""); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("Stop[ return (add,#2,#2); return (mul,Parm_x(sq,int),x); ]", code.print()); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java new file mode 100644 index 00000000..5b33d1e7 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java @@ -0,0 +1,151 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.print.ASMPrinter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter20Test { + + @Test + public void testJig() { + CodeGen code = new CodeGen("return 0;"); + code.parse().opto().typeCheck(); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 2)); + } + + static void testCPU( String src, String cpu, String os, int spills, String stop ) { + CodeGen code = new CodeGen(src); + code.driver(CodeGen.Phase.RegAlloc,cpu,os); + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + if( stop != null ) + assertEquals(stop, code._stop.toString()); + } + + private static void testAllCPUs( String src, int spills, String stop ) { + testCPU(src,"x86_64_v2", "SystemV",spills,stop); + testCPU(src,"riscv" , "SystemV",spills,stop); + testCPU(src,"arm" , "SystemV",spills,stop); + } + + @Test public void testAlloc0() { + testCPU("return new u8[arg];","x86_64_v2", "SystemV",3,"return [u8];"); + testCPU("return new u8[arg];","riscv" , "SystemV",5,"return [u8];"); + testCPU("return new u8[arg];","arm" , "SystemV",5,"return [u8];"); + } + + @Test public void testBasic1() { + String src = "return arg | 2;"; + testCPU(src,"x86_64_v2", "SystemV",1,"return (ori,mov(arg));"); + testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );"); + testCPU(src,"arm" , "SystemV",0,"return (ori,arg);"); + } + + @Test + public void testNewtonInteger() { + String src = +""" +// Newtons approximation to the square root +val sqrt = { int x -> + int guess = x; + while( 1 ) { + int next = (x/guess + guess)/2; + if( next == guess ) return guess; + guess = next; + } +}; +return sqrt(arg) + sqrt(arg+2); +"""; + testCPU(src,"x86_64_v2", "Win64" ,23,null); + testCPU(src,"riscv" , "SystemV",19,null); + testCPU(src,"arm" , "SystemV",19,null); + } + + @Test + public void testNewtonFloat() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp")) + + "flt farg = arg; return test_sqrt(farg) + test_sqrt(farg+2.0);"; + testCPU(src,"x86_64_v2", "SystemV",39,null); + testCPU(src,"riscv" , "SystemV",17,null); + testCPU(src,"arm" , "SystemV",18,null); + } + + @Test + public void testAlloc2() { + String src = "int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];"; + testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];"); + testCPU(src,"riscv" , "SystemV", 6,"return .[];"); + testCPU(src,"arm" , "SystemV", 6,"return .[];"); + } + + + + @Test + public void testArray1() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/array1.smp")); + testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];"); + testCPU(src,"riscv" , "SystemV", 8,"return (add,.[],(mul,.[],1000));"); + testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));"); + } + + + @Test + public void testString() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp")); + testCPU(src,"x86_64_v2", "SystemV", 9,null); + testCPU(src,"riscv" , "SystemV", 5,null); + testCPU(src,"arm" , "SystemV", 6,null); + } + + @Test + public void testCast() { + String src = "struct Bar { int x; }; var b = arg ? new Bar; return b ? b.x++ + b.x++ : -1;"; + testCPU(src,"x86_64_v2", "SystemV",2,null); + testCPU(src,"riscv" , "SystemV",2,null); + testCPU(src,"arm" , "SystemV",2,null); + } + + @Test + public void testFltArg() { + String src = "return {int i, flt f, int j->return i+f+j;};"; + testAllCPUs(src,0,null); + } + + @Test + public void testFlags1() { + String src = """ +bool b1 = arg == 1; +bool b2 = arg == 2; +if (b2) if (b1) return 1; +if (b1) return 2; +return 0; +"""; + testCPU(src,"x86_64_v2", "SystemV",0,"return Phi(Region,1,2,0);"); + testCPU(src,"riscv" , "SystemV",0,"return Phi(Region,1,2,0);"); + testCPU(src,"arm" , "SystemV",0,"return Phi(Region,1,2,0);"); + } + + @Test + public void testFlags2() { + String src = """ +bool b1 = arg == 1; +while (arg > 0) { + arg--; + if (b1) arg--; +} +return arg; +"""; + testCPU(src,"x86_64_v2", "SystemV",3,null); + testCPU(src,"riscv" , "SystemV",2,null); + testCPU(src,"arm" , "SystemV",2,null); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java new file mode 100644 index 00000000..ea23728c --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -0,0 +1,276 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter21Test { + + @Test + public void testJig() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp")); + testCPU(src,"x86_64_v2", "Win64" ,-1,null); + testCPU(src,"riscv" , "SystemV",-1,null); + testCPU(src,"arm" , "SystemV",-1,null); + } + + static void testCPU( String src, String cpu, String os, int spills, String stop ) { + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + if( stop != null ) + assertEquals(stop, code._stop.toString()); + } + + + @Test public void testBasic1() { + String src = "return arg | 2;"; + testCPU(src,"x86_64_v2", "SystemV",1,"return (ori,mov(arg));"); + testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );"); + testCPU(src,"arm" , "SystemV",0,"return (ori,arg);"); + } + + @Test public void testInfinite() { + String src = "struct S { int i; }; S !s = new S; while(1) s.i++;"; + testCPU(src,"x86_64_v2", "SystemV",0,"return Top;"); + testCPU(src,"riscv" , "SystemV",2,"return Top;"); + testCPU(src,"arm" , "SystemV",2,"return Top;"); + } + + @Test + public void testArray1() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/array1.smp")); + testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];"); + testCPU(src,"riscv" , "SystemV", 7,"return (add,.[],(mul,.[],1000));"); + testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));"); + } + + @Test + public void testAntiDeps1() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/antiDep1.smp")); + testCPU(src,"x86_64_v2", "SystemV", 7,"return mov(mov(S));"); + testCPU(src,"riscv" , "SystemV",10,"return mov(mov(S));"); + testCPU(src,"arm" , "SystemV",10,"return mov(mov(S));"); + } + + @Test + public void testString() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp")); + testCPU(src,"x86_64_v2", "SystemV", 9,null); + testCPU(src,"riscv" , "SystemV", 5,null); + testCPU(src,"arm" , "SystemV", 6,null); + } + + @Test public void testStringExport() throws IOException { + TestC.run("stringHash", 9); + } + + @Test public void testLoop2() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/loop2.smp")); + testCPU(src,"x86_64_v2", "Win64" ,0,"return (inc,Phi(Loop,0,inc));"); + testCPU(src,"riscv" , "SystemV",0,"return ( Phi(Loop,0,addi) + #1 );"); + testCPU(src,"arm" , "SystemV",0,"return (inc,Phi(Loop,0,inc));"); + } + + @Test public void testNewtonExport() throws IOException { + String result = """ +0 0.000000 (0) +1 1.000000 (0) +2 1.414214 (2.22045e-16) +3 1.732051 (0) +4 2.000000 (0) +5 2.236068 (0) +6 2.449490 (0) +7 2.645751 (0) +8 2.828427 (4.44089e-16) +9 3.000000 (0) +"""; + TestC.run("newtonFloat",result,34); + + EvalRisc5 R5 = TestRisc5.build("newtonFloat", 0, 10, false); + R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 3.0; + int trap_r5 = R5.step(1000); + assertEquals(0,trap_r5); + // Return register A0 holds fib(8)==55 + assertEquals(1.732051,R5.fregs[riscv.FA0 - riscv.F_OFFSET], 0.00001); + + // arm + EvalArm64 A5 = TestArm64.build("newtonFloat", 0, 10, false); + A5.fregs[arm.D0 - arm.D_OFFSET] = 3.0; + int trap_arm = A5.step(1000); + assertEquals(0,trap_arm); + assertEquals(1.732051, A5.fregs[arm.D0 - arm.D_OFFSET], 0.00001); + } + + + @Test public void testSieve() throws IOException { + // The primes + int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, }; + SB sb = new SB().p(primes.length).p("["); + for( int prime : primes ) + sb.p(prime).p(", "); + String sprimes = sb.p("]").toString(); + + // Compile, link against native C; expect the above string of primes to be printed out by C + TestC.run("sieve",sprimes, 257); + + // Evaluate on RISC5 emulator; expect return of an array of primes in + // the simulated heap. + EvalRisc5 R5 = TestRisc5.build("sieve", 100, 160, false); + int trap = R5.step(10000); + assertEquals(0,trap); + // Return register A0 holds sieve(100) + int ary = (int)R5.regs[riscv.A0]; + // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4) + assertEquals(primes.length, R5.ld4s(ary)); + for( int i=0; i<primes.length; i++ ) + assertEquals(primes[i], R5.ld4s(ary + 4 + i*4)); + + // Evaluate on ARM5 emulator; expect return of an array of primes in + // the simulated heap. + EvalArm64 A5 = TestArm64.build("sieve", 100, 160, false); + int trap_arm = A5.step(10000); + assertEquals(0, trap_arm); + int ary_arm = (int)A5.regs[arm.X0]; + // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4) + assertEquals(primes.length, A5.ld4s(ary_arm)); + for( int i = 0; i<primes.length; i++ ) + assertEquals(primes[i], A5.ld4s(ary_arm + 4 + i * 4)); + } + + @Test public void testFibExport() throws IOException { + String fib = "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]"; + TestC.run("fib", fib, 24); + + EvalRisc5 R5 = TestRisc5.build("fib", 9, 16, false); + int trap = R5.step(100); + assertEquals(0,trap); + // Return register A0 holds fib(8)==55 + assertEquals(55,R5.regs[riscv.A0]); + + // arm + EvalArm64 A5 = TestArm64.build("fib", 9, 16, false); + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + // Return register X0 holds fib(8)==55 + assertEquals(55, A5.regs[arm.X0]); + } + + @Test public void testPerson() throws IOException { + String person = "6\n"; + TestC.run("person", person, 0); + + // Memory layout starting at PS: + int ps = 1<<16; // Person array pointer starts at heap start + // Person[3] = { len,pad,P0,P1,P2 }; // sizeof = 4*8 + // P0 = { age } // sizeof=8 + int p0 = ps+4*8+0*8; + // P1 = { age } // sizeof=8 + int p1 = ps+4*8+1*8; + // P2 = { age } // sizeof=8 + int p2 = ps+4*8+2*8; + EvalRisc5 R5 = TestRisc5.build("person", ps, 0, false); + R5.regs[riscv.A1] = 1; // Index 1 + R5.st4(ps,3); // Length + R5.st8(ps+1*8,p0); + R5.st8(ps+2*8,p1); + R5.st8(ps+3*8,p2); + R5.st8(p0, 5); // age= 5 + R5.st8(p1,17); // age=17 + R5.st8(p2,60); // age=60 + + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals( 5+0,R5.ld8(p0)); + assertEquals(17+1,R5.ld8(p1)); + assertEquals(60+0,R5.ld8(p2)); + + EvalArm64 A5 = TestArm64.build("person", ps, 0, false); + A5.regs[arm.X1] = 1; // Index 1 + A5.st4(ps, 3); + A5.st8(ps+1*8,p0); + A5.st8(ps+2*8,p1); + A5.st8(ps+3*8,p2); + A5.st8(p0, 5); // age= 5 + A5.st8(p1,17); // age=17 + A5.st8(p2,60); // age=60 + + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + assertEquals( 5+0, A5.ld8(p0)); + assertEquals(17+1, A5.ld8(p1)); + assertEquals(60+0, A5.ld8(p2)); + } + + @Test public void testArgCount() throws IOException { + // Test passes more args than registers in Sys5, which is far far more + // than what Win64 allows - so Win64 gets a lot more spills here. + String arg_count = "191.000000\n"; + TestC.run("arg_count", arg_count, + TestC.CALL_CONVENTION.equals("Win64") ? 42 : 15); + + + EvalRisc5 R5 = TestRisc5.build("no_stack_arg_count", 0, 0, false); + + // Todo: handle stack(imaginary stack in emulator) + // pass in float arguments + R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA1 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA2 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA3 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA4 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA5 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA6 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA7 - riscv.F_OFFSET] = 1.1; + + // a0 is passed in arg + R5.regs[riscv.A1] = 2; + R5.regs[riscv.A2] = 2; + R5.regs[riscv.A3] = 2; + R5.regs[riscv.A4] = 2; + R5.regs[riscv.A5] = 2; + R5.regs[riscv.A6] = 2; + R5.regs[riscv.A7] = 2; + + int trap = R5.step(100); + assertEquals(0,trap); + + double result = R5.fregs[riscv.FA0 - riscv.F_OFFSET]; + + assertEquals(22.8, result, 0.00001); + + // arm + EvalArm64 A5 = TestArm64.build("no_stack_arg_count", 0, 0, false); + + A5.fregs[arm.D0 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D1 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D2 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D3 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D4 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D5 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D6 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D7 - arm.D_OFFSET] = 1.1; + + A5.regs[arm.X1] = 2; + A5.regs[arm.X2] = 2; + A5.regs[arm.X3] = 2; + A5.regs[arm.X4] = 2; + A5.regs[arm.X5] = 2; + A5.regs[arm.X6] = 2; + A5.regs[arm.X7] = 2; + + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + + double result1 = A5.fregs[arm.D0 - arm.D_OFFSET]; + assertEquals(22.8, result1, 0.00001); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Compile.java b/chapter21/src/test/java/com/seaofnodes/simple/Compile.java new file mode 100644 index 00000000..088055f9 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Compile.java @@ -0,0 +1,65 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.FunNode; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Compile { + + private static Process run(String simple, String c) throws IOException, InterruptedException { + boolean USE_WSL = System.getProperty("os.name").startsWith("Windows"); + CodeGen code = new CodeGen(simple); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched().regAlloc().encode(); + if (c != null) for (var n:code._start._outputs) if (n instanceof FunNode f && f._name.equals("main")) f._name = "simple_main"; + code.exportELF("test.o"); + var params = new ArrayList<String>(); + if (USE_WSL) params.add("wsl.exe"); + params.add("gcc"); + params.add("-o"); + params.add("test"); + if (c != null) { + Files.writeString(Path.of("test.c"), c); + params.add("test.c"); + } + params.add("test.o"); + Process p = Runtime.getRuntime().exec(params.toArray(String[]::new)); + assertEquals(0, p.waitFor()); + return Runtime.getRuntime().exec(USE_WSL ? new String[]{"wsl.exe", "./test"}:new String[]{"./test"}); + } + + @Test + @Ignore + public void testCompile() throws IOException, InterruptedException { + var p = run(""" + val testMy = { flt x -> + flt guess = x; + while( 1 ) { + flt next = (x/guess + guess)/2; + if( next == guess ) return guess; + guess = next; + } + }; + """,""" + + #include <stdio.h> + + extern double testMy(double); + + int main(int argc, char** argv) { + printf("%f\\n", testMy(2.0)); + return 0; + } + + """); + assertEquals(0, p.waitFor()); + assertEquals("1.414214\n", new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8)); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java new file mode 100644 index 00000000..ecea97dd --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java @@ -0,0 +1,450 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.IRPrinter; +import com.seaofnodes.simple.type.*; +import java.util.*; + +public abstract class Eval2 { + + // A mapping from Node ID to evaluation data Object. + // Data Objsts are a boxed Long or Double; or an Object[] of fields, which + // themselves are just more Objects. + private static class Frame { + private static int UID=1; + + private final IdentityHashMap<Node,Object> _data; + private Frame _prior; // Prior frame in this sequence, means when making a new frame we can find old stuff + private int _refcnt; // TODO: Use this in a sentence (free/recycle unused stack frames) + final int _uid = UID++; // Debugging aid + Frame(Frame prior) { + _prior = prior; + if( prior!=null ) prior._refcnt++; + _data = new IdentityHashMap<>(); + _refcnt = 1; + } + + // Dec ref count + void dec() { + assert _refcnt>0; + if( --_refcnt == 0 ) { + Frame prior = _prior; + _prior = null; + _data.clear(); + // TODO: FREE LIST? + if( prior!=null ) + prior.dec(); + } + } + + // Put a mapping from Node->Object + Node put(Node n, Object d) { + assert d!=null; + return put0(n,d); + } + // Put a mapping from Node->Object? + Node put0(Node n, Object d) { + assert _refcnt>0; + _data.put(n,d); + return n; + } + // Get a mapped Node, perhaps using the prior stack frame crawl + Object get(Node n) { + assert _refcnt>0; + Object d = _data.get(n); + return d != null ? d + : _prior == null ? null + : _prior.get(n); + } + + // Short debugging print + public String p() { + return "#"+_uid + (_prior==null ? "" : " >> " + _prior.p()); + } + // Long debugging print + @Override public String toString() { + String s = ""; + if( _prior != null ) s+= _prior+" << "; + s += "#"+_uid+"[ "; + for( Node n : _data.keySet() ) + s += n + ":" + _data.get(n) + ","; + s += "]"; + + return s; + } + } + + // Classic Closure; an environment (stack of frames) and an execution point + private static class Closure { + // Where to return too; either a CallEnd or a Function + final CFGNode _cc; + + // Stack frame at the execution point. Includes Closures which + // recursively include other stack frames + private Frame _frame; + + Closure(CFGNode cc, Frame frame) { + _cc = cc; // One of Fun, CallEnd or Stop (null) + _frame = frame; + assert _frame._refcnt >=0; + _frame._refcnt++; + } + + FunNode fun() { return (FunNode)_cc; } + CallEndNode cend() { return (CallEndNode)_cc; } + boolean isStop() { return _cc instanceof StopNode; } // or Stop + + // + void pushFrame() { + _frame = new Frame(_frame); + _frame._refcnt++; + } + + @Override public String toString() { + return _cc.toString(); + } + } + + // CodeGen has the linker tables + public static CodeGen CODE; + + // Current active Frame, to avoid having to pass it everywhere + public static Frame F; + + public static String eval( CodeGen code, long arg ) { return eval(code,arg,1000); } + public static String eval( CodeGen code, long arg, int timeout ) { + SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on + // Force local scheduling phase + if( code._phase.ordinal() < CodeGen.Phase.TypeCheck .ordinal() ) code.typeCheck(); + if( code._phase.ordinal() < CodeGen.Phase.Schedule .ordinal() ) code.GCM(); + if( code._phase.ordinal() < CodeGen.Phase.LocalSched.ordinal() ) code.localSched(); + // Set global, so don't have to pass everywhere + CODE = code; + + // Timer for when we run too long. Ticks once per backedge or function call + int loopCnt = 0; + + // Current value for each Node; boxed Long or Double; or an Object[] of + // fields, which themselves are just more Objects. + F = new Frame(null); + + // Start at the START + CFGNode BB = code._start, prior = null; + // Return from main hits Stop + F.put(BB,new Closure(code._stop,F)); + + + // ------------ + // Evaluate until exit or timeout. Each outer loop step computes + // all data nodes under some new Control. + while( true ) { + assert BB!=null; + traceCtrl(BB,trace); + if( trace!=null ) System.out.println(F.p()); + + // Parallel assign Phis + int i = BB instanceof RegionNode r + ? parallelAssignPhis(r,prior,arg,trace) + : 0; + + // Compute the data nodes until BB control + for( ; i < BB.nOuts(); i++ ) + if( !(BB.out(i) instanceof CFGNode) ) + step(BB.out(i), trace); + + // Find the next control target + prior = BB; + BB = BB.uctrl(); // Next unique control target + switch( prior ) { + case StartNode start: F = new Frame(F); break; // Frame for the call to main, as-if Called + case CallNode call : BB = call(call); break; + case IfNode iff : Object p = val(iff.pred()); BB = iff.cproj( p==null || (p instanceof Long x && x==0L) ? 1 : 0); break; + case ReturnNode ret : if( clj(ret.rpc()).isStop() ) return exit(ret); else BB = ret(ret); break; + case FunNode fun : if( loopCnt++ > timeout ) return null; break; // Timeout + case LoopNode loop : if( loopCnt++ > timeout ) return null; break; // Timeout + case CallEndNode cend: break; + case CProjNode cproj: break; + case RegionNode r : break; + default: throw Utils.TODO(); + } + assert !BB.isDead(); + } + } + + + // Phi cache; size is limited to number of Phis at a merge point + private static final Object[] PHICACHE = new Object[256]; + static int parallelAssignPhis(RegionNode r, CFGNode prior, long arg, SB trace) { + + // Compute input path on Phis for this Region + int path = r._inputs.find( prior ); + // Parameters read from prior frame, Phis from local frame + Frame frame = (r instanceof FunNode ? F._prior : F); + boolean isMain = r instanceof FunNode fun && fun.sig().isa(CodeGen.CODE._main); + + // Parallel assign Phis. First parallel read and cache + int i; + for( i = 0; i < r.nOuts(); i++ ) { + if( !(r.out(i) instanceof PhiNode phi) ) break; + if( isMain && phi instanceof ParmNode parm && parm._idx==2 ) + PHICACHE[i] = arg; // Reading the initial arguments to main() + else { + Node n = phi instanceof ParmNode parm + // RPC reads the Call directly; + // Parms may not be linked, so read call args directly + ? (parm._idx==0 ? prior : prior.in(parm._idx)) + // Phis read from path input + : phi.in(path); + PHICACHE[i] = frame.get(n); + } + } + // Parallel assign; might assign before read, so read from cache + for( int j=0; j < i; j++ ) + traceData(F.put0(r.out(j),PHICACHE[j]),trace); + // Return point in basic block past last Phi + return i; + } + + + // Make a call. + private static CFGNode call( CallNode call ) { + // Set up the Return PC and return frame (which is just the current frame). + // Really making a Continuation, but it's just a Closure + Closure cont = new Closure(call.cend(),F); + // Set the return continuation into the current frame RPC + F.put(call, cont ); + // Next Frame to use + F = new Frame(F); + // Target function and environment + return CODE.link(tfp(call.fptr())); + } + + private static CFGNode ret( ReturnNode ret ) { + // Fetch return expression value and continuation from current frame + Object rez = val(ret.expr()); + Closure cont = clj(ret.rpc()); + // Lower Frame reference count; might delete frame + F.dec(); // Lower ref count + // Install replacement Frame; set return result over the CallEnd + F = cont._frame; + F.put0(cont._cc,rez); // Set return value into CallEnd + return cont._cc; // Return CallEnd as the new control + } + + private static String exit( ReturnNode ret ) { + return prettyPrint(ret.expr()._type,val(ret.expr())); + } + + // Take a worklist step: one data op from the current Control is updated. + private static void step(Node n, SB trace) { + if( n instanceof PhiNode ) return; + // Compute new value + F.put0(n,compute(n)); + traceData(n,trace); + // Also do any following projections, which are not in the local schedule otherwise + if( n instanceof MultiNode ) + for( Node use : n._outputs ) + step(use,trace); + } + + // From here down shamelessly copied from Evaluator, written by @Xmilia + private static Object compute( Node n ) { + return switch( n ) { + case AddFNode adf -> d(adf.in(1)) + d(adf.in(2)); + case AddNode add -> x(add.in(1)) + x(add.in(2)); + case AndNode and -> x(and.in(1)) & x(and.in(2)); + case BoolNode.EQF eqf -> d(eqf.in(1)) == d(eqf.in(2)) ? 1L : 0L; + case BoolNode.LEF lef -> d(lef.in(1)) <= d(lef.in(2)) ? 1L : 0L; + case BoolNode.LTF ltf -> d(ltf.in(1)) < d(ltf.in(2)) ? 1L : 0L; + case BoolNode.EQ eq -> Objects.equals(val(eq.in(1)), val(eq.in(2))) ? 1L : 0L; // Bool EQ supports pointers, nil, and integers + case BoolNode.LE le -> x(le .in(1)) <= x(le .in(2)) ? 1L : 0L; + case BoolNode.LT lt -> x(lt .in(1)) < x(lt .in(2)) ? 1L : 0L; + case CastNode cast -> val(cast.in(1)); + case ConstantNode con -> con(con._con); + case DivFNode dvf -> d(dvf.in(2))==0 ? 0D : d(dvf.in(1)) / d(dvf.in(2)); + case DivNode div -> x(div.in(2))==0 ? 0L : x(div.in(1)) / x(div.in(2)); + case LoadNode ld -> load(ld); + case MinusFNode mnf -> - d(mnf.in(1)); + case MinusNode sub -> - x(sub.in(1)); + case MulFNode mlf -> d(mlf.in(1)) * d(mlf.in(2)); + case MulNode mul -> x(mul.in(1)) * x(mul.in(2)); + case NewNode alloc-> alloc(alloc); + case NotNode not -> x(not.in(1)) == 0 ? 1L : 0L; + case OrNode or -> x(or .in(1)) | x(or .in(2)); + case ProjNode proj -> proj._type instanceof TypeMem ? "$mem" : val(proj.in(0)); + case ReadOnlyNode read -> val(read.in(1)); + case SarNode sar -> x(sar.in(1)) >> x(sar.in(2)); + case MemMergeNode merge-> "$mem"; + case ShlNode shl -> x(shl.in(1)) << x(shl.in(2)); + case ShrNode shr -> x(shr.in(1)) >>>x(shr.in(2)); + case StoreNode st -> store(st); + case SubNode sub -> x(sub.in(1)) - x(sub.in(2)); + case SubFNode sbf -> d(sbf.in(1)) - d(sbf.in(2)); + case ToFloatNode toflt-> (double)x(toflt.in(1)); + case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2)); + default -> throw Utils.TODO(); + }; + } + + // Fetch without unboxing, searching up-Frame + static Object val( Node n ) { return F.get(n); } + // Fetch and unbox as primitive long + static long x( Node n ) { Object d = F.get(n); return d==null ? 0 : (Long) F.get(n); } + // Fetch and unbox as primitive double + static double d( Node n ) { Object d = F.get(n); return d==null ? 0 : (Double)F.get(n); } + // Fetch and unbox a function constant + static TypeFunPtr tfp(Node n) { return (TypeFunPtr)F.get(n); } + // Fetch and unbox a closure + static Closure clj(Node n) { return (Closure)F.get(n); } + + // Java box ints and floats. Other things can just be null or some informative string + private static Object con( Type t ) { + return switch ( t ) { + case TypeInteger i -> i.isConstant() ? (Long )i.value() : "INT"; + case TypeFloat f -> f.isConstant() ? (Double)f.value() : "FLT"; + case TypeMemPtr tmp -> tmp; + case TypeFunPtr tfp -> tfp; + case TypeMem mem -> "MEM"; + default -> null; + }; + } + + // Convert array size to array element count + private static int offToIdx( long off, TypeStruct t) { + off -= t.aryBase(); + int scale = t.aryScale(); + long mask = (1L << scale)-1; + assert (off & mask) == 0; + return (int)(off>>scale); + } + + // Builds and returns a pointer Object + private static Object alloc(NewNode alloc) { + TypeStruct type = alloc._ptr._obj; + if( type.isAry() ) { + long sz = (Long)val(alloc.in(1)); + long x = offToIdx(sz, type); + int n = (int)x; + if( n!=x || n<0 ) + throw new NegativeArraySizeException(""+n); + Object[] ary = new Object[n]; // Array body + var elem = type._fields[1]._type; + if (elem instanceof TypeInteger) { + Arrays.fill( ary, 0L ); + } else if (elem instanceof TypeFloat) { + Arrays.fill( ary, 0D ); + } else { + assert elem instanceof TypeMemPtr || elem instanceof TypeFunPtr; + } + return ary; + } + + int num = type._fields.length; + Object[] ptr = new Object[num]; + for( int i=0; i<num; i++ ) + ptr[i] = con(type._fields[i]._type.makeZero()); + return ptr; + } + + private static Object load( LoadNode ld ) { + TypeMemPtr tmp = (TypeMemPtr)ld.ptr()._type; + Object[] fs = (Object[])val(ld.ptr()); + if( ld._name.equals("#") ) + return (long)fs.length; + int idx = tmp._obj.isAry() + ? offToIdx(x(ld.off()),tmp._obj) + : tmp._obj.find(ld._name); + return fs[idx]; + } + + private static Object store( StoreNode st ) { + if( st._name.equals("#") ) + return "$mem"; // Eval stores the length in the java object[], no need to set it now + TypeMemPtr tmp = (TypeMemPtr)st.ptr()._type; + Object[] fs = (Object[])val(st.ptr()); + int idx = tmp._obj.isAry() + ? offToIdx(x(st.off()),tmp._obj) + : tmp._obj.find(st._name); + Object val = val(st.val()); + fs[idx] = val; + return "$mem"; + } + + + // ------------------------------------------------------------------------ + private static final IdentityHashMap<Object,Object> VISIT = new IdentityHashMap<>(); + + + static void traceCtrl( CFGNode BB, SB sb ) { + if( sb==null ) return; + IRPrinter.printLine(BB,sb.p("--- ")); + System.out.print(sb); + sb.clear(); + } + static void traceData( Node n, SB sb ) { + if( sb==null ) return; + IRPrinter.printLine(n,sb).unchar(); + _print( n._type, F.get(n), sb.p('\t'), VISIT ); + System.out.println(sb); + sb.clear(); + VISIT.clear(); + } + + // Use type to print x as a string. Uses a new VISIT table so the debugger + // doesn't try to reuse VISIT if stopping mid-print. The trace code reuses + // VISIT endlessly for some minor speed hope + static String prettyPrint( Type t, Object x ) { return _print(t,x,new SB(), new IdentityHashMap<>()).toString(); } + static SB _print( Type t, Object x, SB sb, IdentityHashMap<Object,Object> visit ) { + if( x instanceof String s ) return sb.p(s); + if( x == null ) return sb.p("null"); + return switch( t ) { + case TypeInteger i -> sb.p( (Long)x); + case TypeFloat f -> sb.p((Double)x); + case TypeMemPtr tmp -> { + if( visit.containsKey(x) ) yield sb.p("$cyclic"); + visit.put(x,x); + // Since never can close the type cycle, without cyclic types, have + // to handle FRefs even here + if( tmp.isFRef() ) + tmp = tmp.makeFrom(((TypeMemPtr)Parser.TYPES.get(tmp._obj._name))._obj); + + Object[] xs = (Object[])x; // Array of fields + if( tmp._obj.isAry() ) { + Type elem = tmp._obj._fields[1]._type; + if( elem == TypeInteger.U8 ) { + // Shortcut u8[] as a String + for( Object o : xs ) + sb.p((char)(long)(Long)o); + } else { + // Array of elements + elem.print(sb).p("[ "); + for( Object o : xs ) + _print( elem, o, sb, visit ).p( "," ); + sb.unchar().p("]"); + } + } else { + sb.p(tmp._obj._name).p("{"); + Field[] flds = tmp._obj._fields; + for( int i=0; i<flds.length; i++ ) + _print( flds[i]._type,xs[i], sb.p(flds[i]._fname).p("="), visit ).p(","); + sb.unchar().p("}"); + } + yield sb; + } + case TypeFunPtr tfp -> sb.p(x.toString()); + case TypeRPC rpc -> sb.p(x.toString()); + case TypeMem mem -> sb.p("$mem"); + case TypeTuple tt -> { + if( tt._types.length>1 && tt._types[1] instanceof TypeMemPtr ) + // Assume a NewNode + yield _print( tt._types[1], x, sb, visit ); + throw Utils.TODO(); + } + case Type tt -> { + if( tt == Type.NIL ) yield sb.p("null"); + throw Utils.TODO(); + } + }; + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java b/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java new file mode 100644 index 00000000..3ea9bea0 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java @@ -0,0 +1,751 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.cpus.arm.arm; + +public class EvalArm64 { + // Memory image, always 0-based + public final byte[] _buf; + + // GPRs + final long[] regs; + // FRs + final double[] fregs; + + int _pc; + int SENTINEL_CALLOC = -4; + // Start of free memory for allocation + int _heap; + // Cycle counters + int _cycle; + + // flags + boolean N = false; + boolean Z = false; + boolean C = false; + boolean V = false; + + EvalArm64(byte[] buf, int stackSize) { + _buf = buf; + regs = new long[32]; + fregs = new double[32]; + // Stack grows down, heap grows up + regs[arm.RSP] = _heap = stackSize; + _pc = 0; + } + + // Little endian + public int ld1s(int x) { return _buf[x ] ; } + public int ld1z(int x) { return _buf[x ]&0xFF; } + public int ld2s(int x) { return ld1z(x) | ld1s(x+1)<< 8; } + public int ld2z(int x) { + return ld1z(x) | ld1z(x+1)<< 8; } + public int ld4s(int x) { return ld2z(x) | ld2s(x+2)<<16; } + public long ld8 (int x) { return (long)ld4s(x)&0xFFFFFFFFL | ((long)ld4s(x+4))<<32; } + + + public float ld4f(int x) { return Float.intBitsToFloat(ld4s(x)); } + public double ld8f(int x) { return Double.longBitsToDouble(ld8(x)); } + + public void st1(int x, int val) { _buf[x] = (byte)val; } + public void st2(int x, int val) { st1(x,val); st1(x+1,val>> 8); } + public void st4(int x, int val) { st2(x,val); st2(x+2,val>>16); } + public void st8(int x, long val) { st4(x,(int)val); st2(x+4,(int)(val>>32)); } + + public int step(int maxops) { + int trap = 0; + long rval = 0; + double frval = 0; + int pc = _pc; + int cycle = _cycle; + boolean is_f = false; + outer: + for(int icount = 0; icount < maxops; icount++) { + int ir = 0; + rval = 0; + frval = 0; + cycle++; + if( pc >= _buf.length ) { + trap = 1 + 1; // Handle access violation on instruction read. + break; + } + + if( (pc & 3)!=0 ) { + trap = 1; // Handle PC-misaligned access + break; + } + + // Load instruction from image buffer at PC + ir = ld4s( pc ); + + int rdid = ir & 0x1f; + // [31:24] + int opcode1 = (ir >> 24) & 0xFF; + // [31:26] + int opcode2 = (ir >> 26) & 0x3F; + + switch(opcode2) { + case 5: { + //b + int imm26 = ir << 6 >> 6; + + pc = pc + (imm26 * 4) - 4; + // won't hit pc += 4 at the bottom because continue + // do it manually + pc += 4; + rdid = -1; + continue ; + } + case 0x25: { + // bl (just calloc) + rval = pc + 4; + regs[arm.X30] = rval; + if(pc + 4 == 0) { + regs[rdid] = rval; + break outer; + } + int imm26 = (ir & 0x03FFFFFF); + int imm = (imm26 << 6) >> 6; + pc = pc + (imm << 2) ; + if (pc == SENTINEL_CALLOC) { + long size = regs[arm.X0]*regs[arm.X1]; + regs[arm.X0] = _heap; + _heap += (int)size; + // unwind pc + pc = (int)rval; + } + continue; + } + } + // for the opcode we just match for bits[31:24]; + switch(opcode1) { + case 0xD2: { + // movz + // get immediate + rval = (ir >> 5) & 0x7FFF; + break; + } + case 0x38: { + // STRB/LDRB(register) + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + // store + if(opcode1 == arm.OP_STORE_R_8) { + st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + rdid = -1; + break; + } + case 0x3: { + // load + if(opcode1 == arm.OP_LOAD_R_8) { + rval = ld1s((int)regs[rn] + (int)regs[rm]); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + break; + } + case 0x39: { + // STRB/LDRB(immediate) + // str/ldr(immediate) + // rn + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_8) { + st1((int)rval, (int)regs[rdid]); + } else trap = (2+1); + rdid = -1; + + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_8) { + rval = ld1s((int)rval); + } else trap = (2+1); + rdid = -1; + break; + } + default: trap = (2+1); + } + break; + } + case 0x78: { + // STRH/LDRH(register) + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + // store + if(opcode1 == arm.OP_STORE_R_16) { + st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + rdid = -1; + break; + } + case 0x3: { + // load + if(opcode1 == arm.OP_LOAD_R_16) { + rval = ld1s((int)regs[rn] + (int)regs[rm]); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + break; + } + + case 0x79: { + // STRH/LDRH(immediate) + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + imm *= 2; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_16) { + st2((int)rval, (int)regs[rdid]); + } else trap = (2+1); + rdid = -1; + + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_16) { + rval = ld2s((int)rval); + } else trap = (2+1); + rdid = -1; + break; + } + default: trap = (2+1); + } + break; + } + + + case 0x5c: { + // ldr + is_f = true; + // address = delta + int address = ir << 8 >> 13; + address *= 4; + if(address >= _buf.length-3) trap = (5 + 1); + frval = ld8f(pc + address); + break; + } + case 0xB9: { + // str/ldr(immediate) 32 bits + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + imm *= 4; + + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_32) { + st4((int)rval, (int)regs[rdid]); + } else trap = (2+1); + + rdid = -1; + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_32) { + rval = ld4s((int) rval); + } else trap = (2+1); + break; + } + default: trap = (2+1); + } + break; + } + case 0xF9: { + // str/ldr(immediate) 64 bits + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + + imm *= 8; + + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_64) { + st8((int)rval, regs[rdid]); + } else trap = (2+1); + + rdid = -1; + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_64) { + rval = ld8((int)rval); + } else trap = (2+1); + break; + } + default: trap = (2+1); + } + break; + } + case 0xf8: { + // LDR/STR (register + index) 64 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OP_STORE_R_64) { + st8((int)regs[rn] + (int)regs[rm], regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) + + rdid = -1; + break; + } + case 0x3: { + // load + if(opcode1 == arm.OP_LOAD_R_64) { + rval = ld8((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + + break; + } + + case 0xB8: { + // LDR/STR (register + index) 32 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OP_STORE_R_32) { + st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) + + rdid = -1; + break; + } + case 0x3: { + // load + if( opcode1 == arm.OP_LOAD_R_32) { + rval = ld4s((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + + break; + } + + case 0xFC: { + // LDR/STR (register + index) float 64 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + is_f = true; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OPF_STORE_R_64) { + st8((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) + + rdid = -1; + break; + } + case 0x3: { + // load + if(opcode1 == arm.OPF_LOAD_R_64) { + frval = ld8f((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + + break; + } + + case 0xBC: { + // LDR/STR (register + index) float 32 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + is_f = true; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OPF_STORE_R_32) { + st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) + + rdid = -1; + break; + } + case 0x3: { + // load + if(opcode1 == arm.OPF_LOAD_R_32) { + frval = ld4f((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); + } + break; + } + default: trap = (2+1); + } + + break; + } + + case 0xBD: { + // LDR/STR(immediate) 32 bits floats + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + + imm *= 4; + is_f = true; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OPF_STORE_IMM_32) { + st4((int)rval, (int)regs[rdid]); + } else trap = (2+1); + + rdid = -1; + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OPF_LOAD_IMM_32) { + frval = ld4s((int)rval); + } else trap = (2+1); + break; + } + default: trap = (2+1); + } + break; + } + case 0xFD: { + // LDR/STR(immediate) 64 bits floats + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + + imm *= 4; + is_f = true; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OPF_STORE_IMM_64) { + st8((int)rval, regs[rdid]); + } else trap = (2+1); + + rdid = -1; + break; + } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OPF_LOAD_IMM_64) { + frval = ld8((int)rval); + } else trap = (2+1); + break; + } + default: trap = (2+1); + } + break; + } + case 0x9b: { + // mul + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + rval = regs[rn] * regs[rm]; + break; + } + case 0x54: { + // b.cond + int imm19 = ir << 8 >> 13; + imm19 = pc + (imm19 * 4) - 4; + rdid = -1; + + switch(ir & 0xF) { + case 0x0: if(Z) pc = imm19; break; // eq + case 0x1: if(!Z) pc = imm19; break; // ne + case 0x2: if(C) pc = imm19; break; // cs + case 0x3: if(!C) pc = imm19; break; + case 0x4: if(N) pc = imm19; break; // mi + case 0x5: if(!N) pc = imm19; break; // pl + case 0x6: if(V) pc = imm19; break; // vs + case 0x7: if(!V) pc = imm19; break; // vc + case 0x8: if(C && !Z) pc = imm19; break; // hi + case 0x9: if(!C || Z) pc = imm19; break; // ls + case 0xA: if(N == V) pc = imm19; break; // ge + case 0xB: if(N != V) pc = imm19; break; // lt + case 0xC: if(!Z && N == V) pc = imm19; break; // gt + case 0xD: if(Z || N != V) pc = imm19; break; // le + case 0xE: pc = imm19; break; // always executed(al) + default: trap = (2+1); + } + break; + } + case 0x9a: { + // conditional select(csel) + int rm = (ir >> 16) & 0x1F; + switch((ir >> 12) & 0xF) { + case 0x0: if(Z) rval = regs[rm]; break; // eq + case 0x1: if(!Z) rval = regs[rm]; break; // ne + case 0x2: if(C) rval = regs[rm]; break; // cs + case 0x3: if(!C) rval = regs[rm]; break; + case 0x4: if(N) rval = regs[rm]; break; // mi + case 0x5: if(!N) rval = regs[rm]; break; // pl + case 0x6: if(V) rval = regs[rm]; break; // vs + case 0x7: if(!V) rval = regs[rm]; break; // vc + case 0x8: if(C && !Z) rval = regs[rm]; break; // hi + case 0x9: if(!C || Z) rval = regs[rm]; break; // ls + case 0xA: if(N == V) rval = regs[rm]; break; // ge + case 0xB: if(N != V) rval = regs[rm]; break; // lt + case 0xC: if(!Z && N == V) rval = regs[rm]; break; // gt + case 0xD: if(Z || N != V) rval = regs[rm]; break; // le + case 0xE: rval = regs[rm]; break; // always executed(al) + default: { + rval = regs[(ir >> 5) & 0x1F]; + } + } + break; + } + case 0xeb: { + // subs(shifted register) + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + long lhs = regs[rn]; + long rhs = regs[rm]; + rval = lhs - rhs; + + // set flags as a side effect + Z = (rval == 0); + N = (rval < 0); + C = lhs >= rhs; + + boolean sign_lhs = (lhs >>> 63) != 0; + boolean sign_rhs = (rhs >>> 63) != 0; + boolean sign_res = (rval >>> 63) != 0; + V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs); + rdid = -1; + break; + } + case 0xF1: { + // subs(immediate) + int rn = (ir >> 5) & 0x1F; + int immediate = ir << 10 >> 20; + long lhs = regs[rn]; + + rval = lhs - immediate; + + // set flags as a side effect + Z = (rval == 0); + N = (rval < 0); + C = lhs >= immediate; + + boolean sign_lhs = (lhs >>> 63) != 0; + boolean sign_rhs = (immediate >>> 63) != 0; + boolean sign_res = (rval >>> 63) != 0; + V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs); + rdid = -1; + break; + } + case 0x8B: { + // add(shifted register) + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + long lhs = regs[rn]; + long rhs = regs[rm]; + rval = lhs + rhs; + break; + } + case 0x91: { + // add(immediate) + int rn = (ir >> 5) & 0x1F; + int immediate = ir << 10 >> 20; + // only hit it twice + rval = regs[rn] + immediate; + break; + } + case 0xD3: { + // lsl(immediate) + int imm = (ir >> 16) & 0x3F; + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] << (64 - imm); + break; + } + case 0x93: { + // asr(immediate) + int imm = (ir >> 16) & 0x3F; + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] >> (imm); + break; + } + case 0x92: { + // and(immediate) + int imm12 = (ir >> 10) & 0x1FFF; + long immediate = arm.decodeImm12(imm12); + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] & immediate; + break; + } + case 0xAA: { + // MOV(register) + int rm = (ir >> 16) & 0x1F; + rval = regs[rm]; + break; + } + case 0xD6: { + rdid = -1; + if(ld4s(pc + 4) == 0) { + break outer; + } + } + // bitwise + case 0x8A: { + // AND + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + rval = regs[rn] & regs[rm]; + break; + } + // floats + case 0x1E: { + is_f = true; + int rs1 = (ir >> 5) & 0x1F; + int rs2 = (ir >> 16) & 0x1F; + + int op = (ir >> 10) & 0x3F; + switch(op) { + case 16: frval = fregs[rs1]; break; // fmov + case 10:frval = fregs[rs1] + fregs[rs2]; break; // fadd + + case 6: if (fregs[rs2] == 0) frval = 0; else frval = fregs[rs1] / fregs[rs2]; break; // fdiv + case 8: { + // fcmp + double lhs = fregs[rs1]; + double rhs = fregs[rs2]; + + Z = (lhs == rhs); + N = (lhs < rhs); + C = lhs >= rhs; + V = Double.isNaN(lhs) || Double.isNaN(rhs); + rdid = -1; + + break; + } + case 2: frval = fregs[rs1] * fregs[rs2]; break; // fmul + case 14: frval = fregs[rs1] - fregs[rs2]; break; // fsub + default: trap = (2+1); + } + break; + } + case 0x9e: { + // scvtf + is_f = true; + int rs1 = (ir >> 5) & 0x1F; + frval = (double)regs[rs1]; + break; + } + default: trap = (2+1); + } + if(trap != 0) { + _pc = pc; + break; + } + if(rdid != -1) { + if(is_f) {fregs[rdid] = frval; is_f = false;} + else regs[rdid] = rval; + } + + pc += 4; + } + // Handle traps and interrupts. + if(trap != 0) { + return trap; + } + _cycle = cycle; + _pc = pc; + return 0; + } + + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/EvalRisc5.java b/chapter21/src/test/java/com/seaofnodes/simple/EvalRisc5.java new file mode 100644 index 00000000..fd953342 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/EvalRisc5.java @@ -0,0 +1,353 @@ + package com.seaofnodes.simple; + +/** Simple RISC5 CPU Emulator + * + * Original works credit Charles Lohr. + * This is a complete rewrite in Java. + * + * Load code into memory and go! + */ + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.cpus.riscv.riscv; + +import java.util.Arrays; + + public class EvalRisc5 { + + // Memory image, always 0-based + public final byte[] _buf; + + // GPRs + final long[] regs; + // FRs + final double[] fregs; + // PC + int _pc; + + // Start of free memory for allocation + int _heap; + + // Cycle counters + int _cycle; + + EvalRisc5( byte[] buf, int stackSize ) { + _buf = buf; + regs = new long[32]; + fregs = new double[32]; + // Stack grows down, heap grows up + regs[riscv.RSP] = _heap = stackSize; + _pc = 0; + } + + // Little endian + public int ld1s(int x) { return _buf[x ] ; } + public int ld1z(int x) { return _buf[x ]&0xFF; } + public int ld2s(int x) { return ld1z(x) | ld1s(x+1)<< 8; } + public int ld2z(int x) { return ld1z(x) | ld1z(x+1)<< 8; } + public int ld4s(int x) { return ld2z(x) | ld2s(x+2)<<16; } + public long ld8 (int x) { return (long)ld4s(x)&0xFFFFFFFFL | ((long)ld4s(x+4))<<32; } + + public float ld4f(int x) { return Float.intBitsToFloat(ld4s(x)); } + public double ld8f(int x) { return Double.longBitsToDouble(ld8(x)); } + + + public void st1(int x, int val) { _buf[x] = (byte)val; } + public void st2(int x, int val) { st1(x,val); st1(x+1,val>> 8); } + public void st4(int x, int val) {st2(x,val); st2(x+2,val>>16); } + public void st8(int x, long val) { st4(x,(int)val); st2(x+4,(int)(val>>32)); } + + // Note: only a few bits are used. (Machine = 3, User = 0) + // Bits 0..1 = privilege. + // Bit 2 = WFI (Wait for interrupt) + // Bit 3+ = Load/Store reservation LSBs. + int _extraflags; + + public int step( int maxops ) { + int trap = 0; + long rval = 0; + double frval = 0; + int pc = _pc; + int cycle = _cycle; + boolean is_f = false; + outer: + for( int icount = 0; icount < maxops; icount++ ) { + int ir = 0; + rval = 0; + cycle++; + + if( pc >= _buf.length ) { + trap = 1 + 1; // Handle access violation on instruction read. + break; + } + if( (pc & 3)!=0 ) { + trap = 1; // Handle PC-misaligned access + break; + } + // Load instruction from image buffer at PC + ir = ld4s( pc ); + int rdid = (ir >> 7) & 0x1f; + + switch( ir & 0x7f ) { + case 0x37: // LUI (0b0110111) + rval = ( ir & 0xfffff000 ); + break; + case 0x17: // AUIPC (0b0010111) + rval = pc + ( ir & 0xfffff000 ); + break; + case 0x6F: { // JAL (0b1101111) + int reladdy = ((ir & 0x80000000)>>11) | ((ir & 0x7fe00000)>>20) | ((ir & 0x00100000)>>9) | ((ir&0x000ff000)); + if( (reladdy & 0x00100000)!=0 ) reladdy |= 0xffe00000; // Sign extension. + rval = pc + 4; + pc = pc + reladdy - 4; + break; + } + case 0x67: { // JALR (0b1100111) + int imm_se = ir >> 20; // Sign extend 12 bits + rval = pc + 4; + pc = ((int)regs[ (ir >> 15) & 0x1f ] + imm_se) & ~1; + // Return from top-level ; exit sim + if( pc==0 ) { + if( rdid!=0 ) regs[rdid] = rval; // Write PC into register before exit + break outer; + } + // Inline CALLOC effect + if( pc == Encoding.SENTINAL_CALLOC ) { + int size = (int)(regs[10]*regs[11]); + regs[10] = _heap; // Return next free address + // Pre-zeroed; epsilon (null) collector, never recycles memory so always zero + // Arrays.fill(_buf,_heap,_heap+size, (byte) 0 ); + _heap += size; + pc = (int)(rval - 4); // Unwind PC, as-if returned from calloc + } else + pc -= 4; + break; + } + case 0x63: { // Branch (0b1100011) + int immm4 = ((ir & 0xf00)>>7) | ((ir & 0x7e000000)>>20) | ((ir & 0x80) << 4) | ((ir >> 31)<<12); + if( (immm4 & 0x1000)!=0 ) immm4 |= 0xffffe000; + long rs1 = regs[(ir >> 15) & 0x1f]; + long rs2 = regs[(ir >> 20) & 0x1f]; + immm4 = pc + immm4 - 4; + rdid = 0; + if( switch( (ir >> 12) & 0x7 ) { + case 0 -> rs1 == rs2; + case 1 -> rs1 != rs2; + case 4 -> rs1 < rs2; + case 5 -> rs1 >= rs2; + case 6 -> Long.compareUnsigned(rs1,rs2) < 0; //BLTU + case 7 -> Long.compareUnsigned(rs1,rs2) >= 0; //BGEU + default -> { trap = (2+1); yield false; } + } ) pc = immm4; + break; + } + case 0x03: { // Load (0b0000011) + int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b + int imm = ir >> 20; + int imm_se = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0 ); + int rsval = rs1 + imm_se; + + if( rsval >= _buf.length-3 ) { + trap = (5+1); + rval = rsval; + } else { + switch( ( ir >> 12 ) & 0x7 ) { + //LB, LH, LW, LBU, LHU + case 0: rval = ld1s( rsval ); break; + case 1: rval = ld2s( rsval ); break; + case 2: rval = ld4s( rsval ); break; + case 3: rval = ld8 ( rsval ); break; + case 4: rval = ld1z( rsval ); break; + case 5: rval = ld2z( rsval ); break; + case 6: rval = ld4s( rsval ); break; + default: trap = (2+1); + } + } + break; + } + // floats + case 0x7: { + is_f = true; + int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b + int imm = ir >> 20; + int imm_se = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0 ); + int rsval = rs1 + imm_se; + + if( rsval >= _buf.length-3 ) { + trap = (5+1); + rval = rsval; + } else { + switch( ( ir >> 12 ) & 0x7 ) { + case 2: frval = ld4f( rsval ); break; + case 3: frval = ld8f( rsval ); break; + default: trap = (2+1); + } + } + break; + } + + case 0x23: { // Store 0b0100011 + int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b + long rs2 = regs[(ir >> 20) & 0x1f]; + int addy = ( ( ir >> 7 ) & 0x1f ) | ( ( ir & 0xfe000000 ) >> 20 ); + if( (addy & 0x800)!=0 ) addy |= 0xfffff000; + addy += rs1; + rdid = 0; + if( addy >= _buf.length-3 ) { + trap = (7+1); // Store access fault. + rval = addy; + } else { + switch( ( ir >> 12 ) & 0x7 ) { //SB, SH, SW + case 0: st1( addy, (int)rs2 ); break; + case 1: st2( addy, (int)rs2 ); break; + case 2: st4( addy, (int)rs2 ); break; + case 3: st8( addy, rs2 ); break; + default: trap = (2+1); + } + } + break; + } + case 0x13: // Op-immediate 0b0010011 + case 0x33: { // Op 0b0110011 + int imm = ir >> 20; + imm = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0); + long rs1 = regs[(ir >> 15) & 0x1f]; + boolean is_reg = (ir & 0x20)!=0; + long rs2 = is_reg ? regs[imm & 0x1f] : imm; + + // Insert the detection here + int rs1id = (ir >> 15) & 0x1f; + int funct3 = (ir >> 12) & 0x7; + + if( is_reg && ( ir & 0x02000000 )!=0 ) { + switch( (ir>>12)&7 ) { //0x02000000 = RV32M + case 0: rval = rs1 * rs2; break; // MUL + case 1: rval = (int)((long)((int)rs1) * (long)((int)rs2)) >> 32; break; // MULH + case 2: rval = (int)((long)((int)rs1) * (long)rs2) >> 32; break; // MULHSU + case 3: rval = (int)((long)rs1 * (long)rs2) >> 32; break; // MULHU + case 4: if( rs2 == 0 ) rval = -1; else rval = ((int)rs1 == Integer.MIN_VALUE && (int)rs2 == -1) ? rs1 : ((int)rs1 / (int)rs2); break; // DIV + case 5: if( rs2 == 0 ) rval = 0xffffffff; else rval = rs1 / rs2; break; // DIVU + case 6: if( rs2 == 0 ) rval = rs1; else rval = ((int)rs1 == Integer.MIN_VALUE && (int)rs2 == -1) ? 0 : ((int)((int)rs1 % (int)rs2)); break; // REM + case 7: if( rs2 == 0 ) rval = rs1; else rval = rs1 % rs2; break; // REMU + } + } else { + rval = switch( (ir >> 12) & 7 ) { // These could be either op-immediate or op commands. Be careful. + case 0 -> (is_reg && (ir & 0x40000000) != 0) ? (rs1 - rs2) : (rs1 + rs2); + case 1 -> rs1 << (rs2 & 0x1F); + case 2 -> (rs1 < rs2) ? 1 : 0; + case 3 -> (rs1 < rs2) ? 1 : 0; + case 4 -> rs1 ^ rs2; + case 5 -> (ir & 0x40000000) != 0 ? (((int) rs1) >> (rs2 & 0x1F)) : (rs1 >> (rs2 & 0x1F)); + case 6 -> rs1 | rs2; + case 7 -> rs1 & rs2; + default -> rval; + }; + } + break; + } + case 0x0f: // 0b0001111 + rdid = 0; // fencetype = (ir >> 12) & 0b111; We ignore fences in this impl. + break; + case 0x73: // Zifencei+Zicsr (0b1110011) + trap = (2+1); // Note micrrop 0b100 == undefined. + break; + + case 0x2f: { // RV32A (0b00101111) + long rs1 = regs[(ir >> 15) & 0x1f]; + long rs2 = regs[(ir >> 20) & 0x1f]; + int irmid = ( ir>>27 ) & 0x1f; + + // We don't implement load/store from UART or CLNT with RV32A here. + if( rs1 >= _buf.length-3 ) { + trap = (7+1); //Store/AMO access fault + rval = rs1; + } else { + rval = ld4s( (int)rs1 ); + + // Referenced a little bit of https://github.com/franzflasch/riscv_em/blob/master/src/core/core.c + boolean dowrite = true; + switch( irmid ) { + case 2: //LR.W (0b00010) + dowrite = false; + _extraflags = (_extraflags & 0x07) | (int)(rs1<<3); + break; + case 3: { //SC.W (0b00011) (Make sure we have a slot, and, it's valid) + boolean xflag = _extraflags >> 3 != ( rs1 & 0x1fffffff ); + rval = xflag ? 1 : 0; // Validate that our reservation slot is OK. + dowrite = !xflag; // Only write if slot is valid. + break; + } + case 0: rs2 += rval; break; // AMOADD.W (0b00000) + case 1: break; // AMOSWAP.W (0b00001) + case 4: rs2 ^= rval; break; // AMOXOR.W (0b00100) + case 8: rs2 |= rval; break; // AMOOR.W (0b01000) + case 12: rs2 &= rval; break; // AMOAND.W (0b01100) + case 16: rs2 = Math.min( rs2, rval ); break; //AMOMIN.W (0b10000) + case 20: rs2 = Math.max( rs2, rval ); break; //AMOMAX.W (0b10100) + case 24: rs2 = Math.min( rs2, rval ); break; //AMOMINU.W (0b11000) + case 28: rs2 = Math.max( rs2, rval ); break; //AMOMAXU.W (0b11100) + default: trap = (2+1); dowrite = false; break; //Not supported. + } + if( dowrite ) st4( (int)rs1, (int)rs2 ); + } + break; + } + + case 0x53: { + is_f = true; + int rs1 = (ir >> 15) & 0x1F; + int rs2 = (ir >> 20) & 0x1F; + double lhs = fregs[rs1]; + double rhs = fregs[rs2]; + int func5 = (ir >> 27) & 0x1F; + int func2 = (ir >> 25) & 0x3; + if( func2 != 1 ) throw Utils.TODO(); // 1==double + switch( func5 ) { + case 0: frval = lhs + rhs; break; // fadd.d + case 1: frval = lhs - rhs; break; // fsub.d + case 2: frval = lhs * rhs; break; // fmul.d + case 3: frval = lhs / rhs; break; // fdiv.d + case 5: frval = Math.min(lhs,rhs); break; // fmin.d, also used for move + case 20: + is_f = false; // Output into a GPR + rval = switch( (ir >> 12) & 7 ) { + case 0 -> lhs <= rhs ? 1 : 0; + case 1 -> lhs == rhs ? 1 : 0; + case 2 -> lhs < rhs ? 1 : 0; + default -> throw Utils.TODO(); + }; + break; + case 26: frval = (double)regs[rs1]; break; // fcvt.w.d + default: + trap = (2+1); // Fault: Invalid opcode. + throw Utils.TODO(); + } + break; + } + + default: trap = (2+1); // Fault: Invalid opcode. + } + + // If there was a trap, do NOT allow register writeback. + if( trap!=0 ) { + _pc = pc; + break; + } + + if( rdid!=0 || is_f) { + if(is_f) {fregs[rdid] = frval; is_f = false;} // Write back register. + else regs[rdid] = rval; // Write back register. + } + + pc += 4; + } + + // Handle traps and interrupts. + if( trap!=0 ) + return trap; + + _cycle = cycle; + _pc = pc; + return 0; + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java new file mode 100644 index 00000000..d67b2065 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -0,0 +1,43 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.fuzzer.Fuzzer; +import java.util.Random; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +/** + * To use the fuzzer run the <code>fuzzPeeps</code> methods. + * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line + * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code> + */ +public class FuzzerWrap { + + @Ignore + @Test + public void fuzzPeeps() { + var fuzzer = new Fuzzer(); + for (int i=0; i<1000000; i++) + fuzzer.fuzzPeeps(i); + assertTrue(fuzzer.noExceptions()); + } + + + @Test + public void fuzzPeepsSmall() { + Random R = new Random(System.currentTimeMillis()); + var fuzzer = new Fuzzer(); + for (int i=0; i<100; i++) + fuzzer.fuzzPeeps( R.nextLong()); + assertTrue(fuzzer.noExceptions()); + } + + @Test + @Ignore + public void fuzzPeepTiming() { + var fuzzer = new Fuzzer(); + int max_nid=0; + for (int i=0; i<1000000; i++) + max_nid = fuzzer.fuzzPeepTiming(i, max_nid); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Main.java b/chapter21/src/test/java/com/seaofnodes/simple/Main.java new file mode 100644 index 00000000..589c043b --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Main.java @@ -0,0 +1,18 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import java.nio.file.Files; +import java.nio.file.Path; + +public class Main { + // Compile and run a simple program + public static void main( String[] args ) throws Exception { + // First arg is file, 2nd+ args are program args + String src = Files.readString(Path.of(args[0])); + int arg = Integer.valueOf(args[1]); + CodeGen code = new CodeGen(src); + code.parse().opto().typeCheck().GCM().localSched(); + System.out.println(code._stop); + System.out.println(Eval2.eval(code,arg,100000)); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/chapter21/src/test/java/com/seaofnodes/simple/MergeSortTest.java new file mode 100644 index 00000000..a1004fb5 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/MergeSortTest.java @@ -0,0 +1,44 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class MergeSortTest { + + @Test public void testMergeSort0() throws IOException { + int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, }; + SB sb = new SB().p(primes.length).p("["); + for( int prime : primes ) + sb.p(prime).p(", "); + String sprimes = sb.p("]").toString(); + TestC.run("sort", sprimes, 40); + + + EvalRisc5 R5 = TestRisc5.build("src/test/java/com/seaofnodes/simple/progs", "sort", "merge_sort", 0, 36, false); + // Allocate an array of primes, filled in reverse order + int ps = R5._heap; + R5._heap += (primes.length+1)*8; // 25 primes, and length + R5.st4(ps,primes.length); + for( int i=0; i<primes.length; i++ ) + R5.st8(ps+(primes.length-1-i+1)*8,primes[i]); + // Pass array base when calling `merge_sort` + R5.regs[riscv.A0] = ps; // Array base to be sorted + + int trap = R5.step(100000); + assertEquals(0,trap); + + assertEquals(primes.length,R5.ld4s(ps)); + for( int i=0; i<primes.length; i++ ) + assertEquals(primes[i], R5.ld8(ps+(i+1)*8)); + + // TODO: replace with ARM Eval + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sort.smp")); + Chapter21Test.testCPU(src,"arm", "SystemV",36,null); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter21/src/test/java/com/seaofnodes/simple/SchedulerTest.java new file mode 100644 index 00000000..ca29bf84 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/SchedulerTest.java @@ -0,0 +1,74 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.evaluator.Evaluator; +import com.seaofnodes.simple.node.StopNode; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SchedulerTest { + + private static void assertObj(Object obj, String name, Object... fields) { + assertEquals(Evaluator.Obj.class, obj.getClass()); + var o = (Evaluator.Obj)obj; + assertEquals(name, o.struct()._name); + Assert.assertArrayEquals(fields, o.fields()); + } + + @Test + @Ignore + public void testJig() { + CodeGen code = new CodeGen(""" +struct S { + int f; +} +S !v=new S; +v.f = 2; +int i=v.f; +if (arg) v.f=1; +return i; +"""); + code.parse();//.opto(); + var eval = new Evaluator(code._stop); + assertEquals(2L, eval.evaluate(1, 10)); + } + + @Test + public void testStoreInIf() { + CodeGen code = new CodeGen(""" +struct S { + int f; +}; +S !v0=new S; +if(arg) v0.f=1; +return v0; +"""); + code.parse().opto(); + var eval = new Evaluator(code._stop); + assertObj(eval.evaluate(0, 10), "S", 0L); + assertObj(eval.evaluate(1, 10), "S", 1L); + } + + @Test + public void testStoreInIf2() { + CodeGen code = new CodeGen(""" +struct S { + int f; +}; +S !v=new S; +v.f = 2; +int i=new S.f; +i=v.f; +if (arg) v.f=1; +return i; +"""); + code.parse().opto(); + var eval = new Evaluator(code._stop); + assertEquals(2L, eval.evaluate(0, 10)); + assertEquals(2L, eval.evaluate(1, 10)); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Simple.java b/chapter21/src/test/java/com/seaofnodes/simple/Simple.java new file mode 100644 index 00000000..9b1e2827 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/Simple.java @@ -0,0 +1,313 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.print.*; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.io.IOException; +import java.util.stream.Collectors; + +public class Simple { + public static final String PORTS = "com.seaofnodes.simple.node.cpus"; + + static final int DUMP_AFTER_PARSE = 1<<0; + static final int DUMP_AFTER_OPTO = 1<<1; + static final int DUMP_AFTER_TYPE_CHECK = 1<<2; + static final int DUMP_AFTER_LOOP_TREE = 1<<3; + static final int DUMP_AFTER_INSTR_SELECT = 1<<4; + static final int DUMP_AFTER_GCM = 1<<5; + static final int DUMP_AFTER_LOCAL_SCHED = 1<<6; + static final int DUMP_AFTER_REG_ALLOC = 1<<7; + static final int DUMP_AFTER_ENCODE = 1<<8; + + static final int DUMP_FINAL = 1<<16; + + static final int DUMP_DOT = 1<<29; + static final int DUMP_PASS_NAME = 1<<30; + static final int DUMP_AFTER_ALL = 0xff | DUMP_PASS_NAME; + static final int DUMP_DOT_AFTER_ALL = 0xff | DUMP_DOT; + + static final int DUMP_DISASSEMBLE = 1<<31; + + static String system_cpu = null; + static String system_abi = null; + + static void usage() { + System.out.println( +""" +simple <input-file> [options] [--eval|--run ...] +Options: + --dump - dump final intermediate representation + --dump-after-parse - dump intermediate representation after parse + --dump-after-opto - dump intermediate representation after opto pass + --dump-after-type-check - dump intermediate representation after type check pass + --dump-after-instr-select - dump intermediate representation after instrution selection pass + --dump-after-gcm - dump intermediate representation after GCM pass + --dump-after-local-sched - dump intermediate representation after local scheduling pass + --dump-after-reg-alloc - dump intermediate representation after register allocation pass + --dump-after-encode - dump intermediate representation after encoding pass + --dump-after-all - dump intermediate representation after all passes + --dot - dump grapical representation of intermediate code into *.dot file(s) + -S - dump generated assembler code + --eval ... - evaluate the compiled code in emulator + --run ... - run the compiled code natively + --dump-size - print the size of generated code + --dump-time - print compilation and execution times + --cpu <cpu-name> - use specific CPU (x86_64_v2, riscv, arm) + --abi <abi-name> - use speific ABI variant (SystemV) + --target - print native CPU and ABI + --version + --help +""" +); + System.exit(0); + } + + static void bad_usage() { + System.err.println("ERROR: Invalid usage (use --help)"); + System.exit(1); + } + + static void target() { + if (system_cpu == null || system_abi == null) { + System.err.println("ERROR: Unknown target"); + System.exit(1); + } + System.out.print(system_cpu); + System.out.print("-"); + System.out.println(system_abi); + System.exit(0); + } + + static void dump(CodeGen code, int dump, int pass) { + if ((dump & pass) != 0) { + if ((dump & DUMP_DOT) != 0) { + String fn = switch (pass) { + case DUMP_AFTER_PARSE -> "01-parse.dot"; + case DUMP_AFTER_OPTO -> "02-opto.dot"; + case DUMP_AFTER_TYPE_CHECK -> "03-type_check.dot"; + case DUMP_AFTER_LOOP_TREE -> "04-loop_tree.dot"; + case DUMP_AFTER_INSTR_SELECT -> "05-instr_select.dot"; + case DUMP_AFTER_GCM -> "06-gcm.dot"; + case DUMP_AFTER_LOCAL_SCHED -> "07-local_sched.dot"; + case DUMP_AFTER_REG_ALLOC -> "08-reg_allos.dot"; + case DUMP_AFTER_ENCODE -> "09-local_sched.dot"; + case DUMP_FINAL -> "10-final.dot"; + default -> throw Utils.TODO(); + }; + + try { + Files.writeString(Path.of(fn), + new GraphVisualizer().generateDotOutput(code._stop, null, null)); + } catch(IOException e) { + System.err.println("ERROR: Cannot write DOT file"); + System.exit(1); + } + } else { + if ((dump & DUMP_PASS_NAME) != 0) { + System.err.println(switch (pass) { + case DUMP_AFTER_PARSE -> "After Parse:"; + case DUMP_AFTER_OPTO -> "After OPTO:"; + case DUMP_AFTER_TYPE_CHECK -> "After Type Check:"; + case DUMP_AFTER_LOOP_TREE -> "After Loop Tree:"; + case DUMP_AFTER_INSTR_SELECT -> "After Instruction Selection:"; + case DUMP_AFTER_GCM -> "After GCM:"; + case DUMP_AFTER_LOCAL_SCHED -> "After Local Scheduling:"; + case DUMP_AFTER_REG_ALLOC -> "After Register Allocation:"; + case DUMP_AFTER_ENCODE -> "After Instruction Encoding:"; + case DUMP_FINAL -> "Final:"; + default -> throw Utils.TODO(); + }); + } + + System.err.println(IRPrinter.prettyPrint(code._stop, 9999)); + } + } + } + + static void print_compilation_times(CodeGen code) { + double t, total = 0; + + total += t = code._tParse / 1e3; + System.out.println(String.format("Parsing Time: %.3f sec", t)); + total += t = code._tOpto / 1e3; + System.out.println(String.format("Optimization Time: %.3f sec", t)); + total += t = code._tTypeCheck / 1e3; + System.out.println(String.format("Type Checking Time: %.3f sec", t)); + total += t = code._tLoopTree / 1e3; + System.out.println(String.format("Loop Tree Time: %.3f sec", t)); + total += t = code._tInsSel / 1e3; + System.out.println(String.format("Instruction Selection Time: %.3f sec", t)); + total += t = code._tGCM / 1e3; + System.out.println(String.format("GCM Time: %.3f sec", t)); + total += t = code._tLocal / 1e3; + System.out.println(String.format("Local Scheduling Time: %.3f sec", t)); + total += t = code._tRegAlloc / 1e3; + System.out.println(String.format("Register Allocation Time: %.3f sec", t)); + total += t = code._tEncode / 1e3; + System.out.println(String.format("Instruction Encoding Time: %.3f sec", t)); + System.out.println(String.format("TOTAL COMPILATION TIME: %.3f sec", total)); + } + + static String getInput() { + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + return reader.lines().collect(Collectors.joining("\n")); + } + + public static void main(String[] args) throws Exception { + String input_filename = null; + boolean use_stdin = false; + boolean do_eval = false; + boolean do_run = false; + boolean do_codegen = false; + boolean do_print_size = false; + boolean do_print_time = false; + int dump = 0; + int first_arg = 0; + String src = null; + String cpu = null; + String abi = null; + + // TODO: autodetect + system_cpu = "x86_64_v2"; + system_abi = "SystemV"; + + // Parse command line +loop: for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if (arg.charAt(0) == '-') { + switch (arg) { + case "-": use_stdin = true; break; + case "--dump": dump |= DUMP_FINAL; break; + case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break; + case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break; + case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break; + case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break; + case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break; + case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break; + case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break; + case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break; + case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break; + case "--dump-after-all": dump |= DUMP_AFTER_ALL; break; + case "--dot": dump |= DUMP_DOT; break; + case "-S": dump |= DUMP_DISASSEMBLE; break; + case "--eval": do_eval = true; first_arg = i + 1; break loop; + case "--run": do_run = true; first_arg = i + 1; break loop; + case "--dump-size": do_print_size = true; break; + case "--dump-time": do_print_time = true; break; + case "--cpu": if (cpu != null + || i + 1 >= args.length + || args[i + 1].charAt(0) == '-') bad_usage(); + cpu = args[i + 1]; + i++; + break; + case "--abi": if (abi != null + || i + 1 >= args.length + || args[i + 1].charAt(0) == '-') bad_usage(); + abi = args[i + 1]; + i++; + break; + case "--target": target(); + case "--version": throw Utils.TODO(); + case "--help": usage(); + default: bad_usage(); + } + } else { + if (input_filename != null || use_stdin) bad_usage(); + input_filename = arg; + } + } + + if (input_filename == null && !use_stdin) { + System.err.println("ERROR: no input file' (use --help)"); + System.exit(1); + } + + if (do_run || (dump & DUMP_DISASSEMBLE) != 0 || do_print_size) { + if (cpu == null) cpu = system_cpu; + if (abi == null) abi = system_abi; + if (cpu == null || abi == null) { + System.err.println("ERROR: Cannot compile for unknown target"); + System.exit(1); + } + do_codegen = true; + } + + // Read input file + try { + src = use_stdin ? getInput() : Files.readString(Path.of(input_filename)); + } catch(IOException e) { + System.err.println("ERROR: Cannot read input file"); + System.exit(1); + } + + // Compilation pipeline + CodeGen code = new CodeGen(src); + + code.parse(); + dump(code, dump, DUMP_AFTER_PARSE); + + code.opto(); + dump(code, dump, DUMP_AFTER_OPTO); + + code.typeCheck(); + dump(code, dump, DUMP_AFTER_TYPE_CHECK); + + code.loopTree(); + dump(code, dump, DUMP_AFTER_LOOP_TREE); + + if (do_codegen) { + code.instSelect(PORTS, cpu, abi); + dump(code, dump, DUMP_AFTER_INSTR_SELECT); + } + + code.GCM(); + dump(code, dump, DUMP_AFTER_GCM); + + code.localSched(); + dump(code, dump, DUMP_AFTER_LOCAL_SCHED); + + if (do_codegen) { + code.regAlloc(); + dump(code, dump, DUMP_AFTER_REG_ALLOC); + + code.encode(); + dump(code, dump, DUMP_AFTER_ENCODE); + } + + dump(code, dump, DUMP_FINAL); + + if (do_codegen && (dump & DUMP_DISASSEMBLE) != 0) { + System.out.println(code.asm()); + } + + if (do_print_size) { + System.out.println(String.format("Code Size: %d", code._encoding._bits.size())); + } + + if (do_print_time) { + print_compilation_times(code); + } + + if (do_eval) { + // TODO: Support for evaluation of functions with different argument numbers and types + long t = System.currentTimeMillis(); + long arg = (first_arg < args.length) ? Integer.valueOf(args[first_arg]) : 0; + System.out.println(Eval2.eval(code, arg, 100000)); + if (do_print_time) { + System.out.println(String.format("EXECUTION TIME: %.3f sec", + (System.currentTimeMillis() - t) / 1e3)); + } + } else if (do_run) { + if (cpu != system_cpu || abi != system_abi) { + System.err.println("ERROR: cannot run code on not native target"); + System.exit(1); + } + + throw Utils.TODO(); + } + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java b/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java new file mode 100644 index 00000000..5d7d1361 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java @@ -0,0 +1,42 @@ +package com.seaofnodes.simple; + + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.arm.arm; +import junit.framework.Assert; +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +// Runs 32-bit ARM code in an emulator + +public class TestArm64 { + public static EvalArm64 build( String file, int arg, int spills, boolean print ) throws IOException { + return build("src/test/java/com/seaofnodes/simple/progs",file, arg, spills, print); + } + + // Compile and run a simple program + public static EvalArm64 build( String dir, String file, int arg, int spills, boolean print ) throws IOException { + // Compile and export Simple + String src = Files.readString(Path.of(dir+"/"+file+".smp")); + CodeGen code = new CodeGen(src).driver("arm", "SystemV",null); + if( print ) { code.print_as_hex(); System.out.print(code.asm()); } + + // Allocation quality not degraded + int delta = spills>>3; + if( delta == 0 ) delta =1; + if( spills != -1 ) assertEquals("Expect spills:", spills, code._regAlloc._spillScaled, delta); + + // Image + byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes) + EvalArm64 ARM = new EvalArm64(image, 1<<16); + // Code at offset 0 + System.arraycopy(code._encoding.bits(), 0, image, 0, code._encoding.bits().length); + // Initial incoming int arg + ARM.regs[arm.X0] = arg; + // malloc memory starts at 64K and goes up + return ARM; + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/TestC.java b/chapter21/src/test/java/com/seaofnodes/simple/TestC.java new file mode 100644 index 00000000..5609758b --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/TestC.java @@ -0,0 +1,89 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.InputStream; +import java.io.IOException; +import static org.junit.Assert.*; + +// Call a C driver program, which calls a Simple-generated .o, and self-checks. +// Expects GCC from the default CLI, compiles C, compiles Simple, links and runs. +public abstract class TestC { + + public static final String OS = System.getProperty("os.name"); + public static final String CPU = System.getProperty("os.arch"); + + public static final String CALL_CONVENTION = OS.startsWith("Windows") ? "Win64" : "SystemV"; + public static final String CPU_PORT = switch( CPU ) { + case "amd64" -> "x86_64_v2"; + default -> throw Utils.TODO("Map Yer CPU Port Here"); + }; + + public static void run( String file, int spills ) throws IOException { run(file,"",spills); } + + public static void run( String file, String expected, int spills ) throws IOException { + run("src/test/java/com/seaofnodes/simple/progs",file,expected,spills); + } + + // Compile and run a simple program + public static void run( String dir, String file, String expected, int spills ) throws IOException { + // Files + String cfile = dir+"/"+file+".c" ; + String sfile = dir+"/"+file+".smp"; + String efile = "build/objs/"+file; + + // Compile and export Simple + String src = Files.readString(Path.of(sfile)); + _run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills); + } + + static void _run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException { + String bin = efile+xtn; + String obj = bin+".o"; + // Compile simple, emit ELF + CodeGen code = new CodeGen(src).driver( CPU_PORT, simple_conv, obj); + + // Compile the C program + var params = new String[] { + //if (USE_WSL) "wsl.exe"; + "gcc", + cfile, + obj, + "-lm", // Picks up 'sqrt' for newtonFloat tests to compare + "-g", + "-o", + bin, + "-D", + "CALL_CONV="+c_conv, + }; + Process gcc = new ProcessBuilder(params).redirectErrorStream(true).start(); + byte error; + try { error = (byte)gcc.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } + String result = new String(gcc.getInputStream().readAllBytes()); + if( error!=0 ) { + System.err.println("gcc error code: "+error); + System.err.println(result); + } + assertEquals( 0, error ); + //assertTrue(result.isEmpty()); // No data in error stream + + // Execute results + Process smp = new ProcessBuilder(bin).redirectErrorStream(true).start(); + try { error = (byte)smp.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } + result = new String(smp.getInputStream().readAllBytes()); + if( error!=0 ) { + System.err.println("exec error code: "+error); + System.err.println(result); + } + assertEquals( 0, error ); + assertEquals(expected,result); + + // Allocation quality not degraded + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/TestRisc5.java b/chapter21/src/test/java/com/seaofnodes/simple/TestRisc5.java new file mode 100644 index 00000000..041ed833 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/TestRisc5.java @@ -0,0 +1,50 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import static org.junit.Assert.*; + +// Runs 32-bit R5 code in an emulator +public abstract class TestRisc5 { + + public static EvalRisc5 build( String file, int arg, int spills, boolean print ) throws IOException { + return build("src/test/java/com/seaofnodes/simple/progs",file, null, arg, spills, print); + } + + // Compile and run a simple program + public static EvalRisc5 build( String dir, String file, String main, int arg, int spills, boolean print ) throws IOException { + // Compile and export Simple + String src = Files.readString(Path.of(dir+"/"+file+".smp")); + CodeGen code = new CodeGen(src).driver("riscv", "SystemV",null); + if( print ) { code.print_as_hex(); System.out.print(code.asm()); } + + // Allocation quality not degraded + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + + // Image + byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes) + EvalRisc5 R5 = new EvalRisc5(image, 1<<16); + // Code at offset 0 + System.arraycopy(code._encoding.bits(), 0, image, 0, code._encoding.bits().length); + // Initial incoming int arg + R5.regs[riscv.A0] = arg; + // malloc memory starts at 64K and goes up + + // Look up starting PC or zero if not given + if( main!=null ) + for( Node use : code._start._outputs ) + if( use instanceof FunNode fun && main.equals(fun._name) ) + R5._pc = code._encoding._opStart[fun._nid]; + + return R5; + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/TypeTest.java b/chapter21/src/test/java/com/seaofnodes/simple/TypeTest.java new file mode 100644 index 00000000..d94f5b5a --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/TypeTest.java @@ -0,0 +1,158 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.type.*; +import org.junit.Assert; +import org.junit.Test; +import static org.junit.Assert.assertSame; + +public class TypeTest { + + // Test basic properties and GLB + @Test + public void testTypeAdHoc() { + Assert.assertEquals( Type.BOTTOM, TypeInteger.TRUE.meet(TypeNil.NIL) ); + Assert.assertEquals( Type.BOTTOM, TypeInteger.TOP.meet(TypeNil.NIL) ); + + + TypeStruct s1 = TypeStruct.make("s1", new Field[]{ + Field.make("a", TypeInteger.BOT,-1, false), + Field.make("b", TypeInteger.BOT,-2, false) }); + TypeStruct s2 = TypeStruct.make("s2", new Field[]{ + Field.make("a", TypeInteger.BOT,-3, false), + Field.make("b", TypeInteger.BOT,-4, false) }); + Assert.assertEquals(s1.makeRO(), s1.glb()); + Assert.assertNotEquals(s1, s1.dual()); + Assert.assertEquals(s1.makeRO(), s1.dual().glb()); + + TypeMem m1 = TypeMem.make(1,TypeNil.NIL); + TypeMem m2 = TypeMem.make(2,TypeInteger.U16); + TypeMem m3 = TypeMem.make(3,TypeFloat.F64); + TypeMem m4 = TypeMem.make(4,TypeInteger.BOT); + + Assert.assertNotEquals(m1, m2); + Assert.assertNotEquals(m2, m3); + Assert.assertNotEquals(m3, m4); + + Assert.assertEquals(TypeStruct.BOT, s1.meet(s2)); + Assert.assertEquals(TypeMem .BOT, m1.meet(m2)); + Assert.assertEquals(TypeMem.make(0,Type.BOTTOM), m1.meet(m3)); + Assert.assertEquals(TypeMem .BOT, m3.meet(m4)); + + Assert.assertEquals(TypeMem.make(1,Type.BOTTOM), m1.glb()); + Assert.assertEquals(TypeMem.make(1,Type.XNIL), m1.dual()); + Assert.assertEquals(m4.dual(), m4.glb().dual()); + + TypeMemPtr ptr1 = TypeMemPtr.make(s1); + Assert.assertEquals(s1, ptr1._obj); + TypeMemPtr ptr2 = TypeMemPtr.make(s2); + Assert.assertEquals(s2, ptr2._obj); + + TypeMemPtr ptr1nil = TypeMemPtr.makeNullable(s1); + Assert.assertEquals(s1, ptr1nil._obj); + Assert.assertTrue( ptr1nil.nullable() ); + Assert.assertNotNull( ptr1nil._obj ); + TypeMemPtr ptr2nil = TypeMemPtr.makeNullable( s2); + Assert.assertEquals(s2, ptr2nil._obj); + + Assert.assertNotEquals(ptr1, ptr2); + Assert.assertNotEquals(ptr1, ptr1.glb()); + Assert.assertEquals(ptr1nil.makeRO(), ptr1.glb()); + + Assert.assertEquals(ptr1, ptr1.dual().dual()); + Assert.assertEquals(ptr1.glb().makeRO(), ptr1.dual().glb()); + Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT), ptr1.meet(ptr2nil)); + Assert.assertEquals(ptr1.glb(), ptr1.meet(TypeNil.NIL).makeRO()); + + TypeMemPtr TOP = TypeMemPtr.TOP; + TypeMemPtr BOT = TypeMemPtr.makeNullable(TypeStruct.BOT); + TypeMemPtr PTR = TypeMemPtr.make(TypeStruct.BOT); + Type NULL = TypeNil.NIL; + Type PTR_meet_NULL = NULL.meet(PTR); + Assert.assertEquals(BOT, PTR_meet_NULL); + Type ptr1_meet_ptr2 = ptr1.meet(ptr2); + Assert.assertEquals(PTR, ptr1_meet_ptr2); + Type NULL_join_ptr1 = NULL.join(ptr1); + Assert.assertEquals(TypePtr.XNPTR, NULL_join_ptr1); + Type NULL_join_PTR = PTR.join(NULL); + Assert.assertEquals(TypePtr.XNPTR, NULL_join_PTR); + Type ptr1_dual = ptr1.dual(); + Type nullableptr1_dual = ptr1nil.dual(); + } + + // Test theoretical properties. + // This is a symmetric complete bounded (ranked) lattice. + // Also, the meet is commutative and associative. + // The lattice has a dual (symmetric), and join is ~(~x meet ~y). + // See https://en.wikipedia.org/wiki/Lattice_(order). + @Test + public void testLatticeTheory() { + Type[] ts = Type.gather(); + + // Confirm commutative & complete + for( Type t0 : ts ) + for( Type t1 : ts ) { + check_commute (t0,t1); + check_symmetric(t0,t1); + } + + // Confirm associative + for( Type t0 : ts ) + for( Type t1 : ts ) + for( Type t2 : ts ) + assoc(t0,t1,t2); + + // Confirm symmetry. If A isa B, then A.join(C) isa B.join(C) + for( Type t0 : ts ) + for( Type t1 : ts ) + if( t0.isa(t1) ) + for( Type t2 : ts ) { + Type t02 = t0.join(t2); + Type t12 = t1.join(t2); + Type mt = t02.meet(t12); + assertSame(mt,t12); + } + } + + // By design in meet, args are already flipped to order _type, which forces + // symmetry for things with badly ordered _type fields. The question is + // still interesting for other orders. + private static void check_commute( Type t0, Type t1 ) { + if( t0==t1 ) return; + if( t0.is_simple() && !t1.is_simple() ) return; // By design, flipped the only allowed order + Type mta = t0.meet(t1); + Type mtb = t1.meet(t0); // Reverse args and try again + assertSame(mta,mtb); + } + + // A & B = MT + // Expect: ~A & ~MT == ~A + // Expect: ~B & ~MT == ~B + private static void check_symmetric( Type t0, Type t1 ) { + if( t1==t0 ) return; + Type mt = t0.meet(t1); + Type dm = mt.dual(); + Type d0 = t0.dual(); + Type d1 = t1.dual(); + Type ta = dm.meet(d1); + Type tb = dm.meet(d0); + assertSame(ta,d1); + assertSame(tb,d0); + } + + private static void assoc( Type t0, Type t1, Type t2 ) { + Type t01 = t0 .meet(t1 ); + Type t12 = t1 .meet(t2 ); + Type t01_2 = t01.meet(t2 ); + Type t0_12 = t0 .meet(t12); + assertSame(t01_2,t0_12); + } + + // Test cyclic types and meets + @Test + public void testCyclic0() { + Type d0 = TypeStruct.S1.dual(); + Type d1 = d0.dual(); + assertSame(TypeStruct.S1,d1); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java new file mode 100644 index 00000000..a3b87e90 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java @@ -0,0 +1,383 @@ +package com.seaofnodes.simple.evaluator; + +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; + +import java.util.*; + +public class Evaluator { + + private static final Object MEMORY = new Object(); + + public record Obj(TypeStruct struct, Object[] fields) { + private void init(IdentityHashMap<Obj, Integer> objs) { + var oid = objs.get(this); + if (oid != null) { + if (oid != 0) objs.put(this, 0); + return; + } + objs.put(this, -1); + for (var obj : fields) { + if (obj instanceof Obj o) o.init(objs); + } + } + private static int p(StringBuilder sb, IdentityHashMap<Obj, Integer> objs, int id, String indentation, String step, String sep, Object obj) { + if (obj instanceof Obj o) return o.p(sb, objs, id, indentation, step, sep); + sb.append(obj); + return id; + } + private int p(StringBuilder sb, IdentityHashMap<Obj, Integer> objs, int id, String indentation, String step, String sep) { + var cid = objs.get(this); + assert cid != null; + if (cid > 0) { + sb.append("obj@").append(cid); + return id; + } + if (struct._name.equals("[u8]")) { + sb.append('"'); + for (int i=struct._fields.length-1; i<fields.length; i++) { + var v = fields[i]; + if (v == null) v = 0; + assert v instanceof Number; + var n = ((Number)v).byteValue() & 0xFF; + if (n >= 0x20 && n < 0x80) { + if (n == 0x22 || n == 0x5C) sb.append("\\"); + sb.append((char)n); + } else { + sb.append("\\x").append("0123456789abcdef".charAt(n >> 4)).append("0123456789abcdef".charAt(n & 0xF)); + } + } + sb.append('"'); + return id; + } + sb.append("Obj<").append(struct._name).append(">"); + if (cid == 0) { + cid = id++; + objs.put(this, cid); + sb.append("@").append(cid); + } + sb.append("{"); + if (struct._fields.length == 0) { + assert !struct.isAry(); + sb.append("}"); + return id; + } + String nextIndent = indentation + step; + int e = struct._fields.length - 1; + for(int i=0; i<e; i++) { + sb.append(nextIndent).append(struct._fields[i]._fname).append("="); + id = p(sb, objs, id, nextIndent, step, sep, fields[i]); + sb.append(sep); + } + sb.append(nextIndent).append(struct._fields[e]._fname).append("="); + if (struct.isAry()) { + sb.append("["); + if (fields.length > e) { + String innerIndent = nextIndent + step; + sb.append(innerIndent); + id = p(sb, objs, id, nextIndent, step, sep, fields[e]); + for (int i = e+1; i < fields.length; i++) { + sb.append(sep).append(innerIndent); + id = p(sb, objs, id, nextIndent, step, sep, fields[i]); + } + sb.append(nextIndent); + } + sb.append("]"); + } else { + id = p(sb, objs, id, nextIndent, step, sep, fields[e]); + } + sb.append(indentation).append("}"); + return id; + } + private StringBuilder p(StringBuilder sb, String indentation, String step, String sep) { + var objs = new IdentityHashMap<Obj, Integer>(); + init(objs); + p(sb, objs, 1, indentation, step, sep); + return sb; + } + @Override + public String toString() { + if (struct._name.equals("[u8]")) { + var sb = new StringBuilder(); + for (int i=struct._fields.length-1; i<fields.length; i++) { + var v = fields[i]; + if (v == null) v = 0; + assert v instanceof Number; + var n = ((Number)v).byteValue() & 0xFF; + sb.append((char)n); + } + return sb.toString(); + } + return p(new StringBuilder(), "", "", ",").toString(); + } + public String pretty() { + return p(new StringBuilder(), "\n", " ", "").toString(); + } + } + + private static int getFieldIndex(TypeStruct struct, MemOpNode memop, long off) { + int idx = struct.find(memop._name); + if( idx >= 0 ) return idx; + throw new AssertionError("Field "+memop._name+" not found in struct " + struct._name); + } + + public enum Status { + TIMEOUT, + FALLTHROUGH + } + + /** + * Find the start node from some node in the graph or null if there is no start node + */ + private static StartNode findStart(BitSet visit, Node node) { + if (node == null) return null; + if( visit.get(node._nid) ) return null; + visit.set(node._nid); + StartNode start = node instanceof StartNode ? (StartNode) node : null; + for( Node def : node._inputs ) { + var res = findStart(visit,def); + if( res != null ) start = res; + } + for( Node use : node._outputs ) { + var res = findStart(visit,use); + if( res != null ) start = res; + } + return start; + } + + private final Object[] values; + private final ArrayList<Object> phiCache = new ArrayList<>(); + private final StartNode start; + private final Scheduler.Block startBlock; + + public Evaluator(Node graph) { + var visited = new BitSet(); + this.start = findStart(visited, graph); + if (this.start == null) { + this.values = null; + this.startBlock = null; + return; + } + this.startBlock = Scheduler.schedule(this.start); + this.values = new Object[visited.length()]; + } + + private long div(DivNode div) { + long in2 = vall(div.in(2)); + return in2 == 0 ? 0 : vall(div.in(1)) / in2; + } + private double div(DivFNode div) { + double in2 = vald(div.in(2)); + return in2 == 0 ? 0 : vald(div.in(1)) / in2; + } + + private static long offToIdx(long off, TypeStruct t) { + off -= t.aryBase(); + int scale = t.aryScale(); + long mask = (1L << scale)-1; + assert (off & mask) == 0; + return off>>scale; + } + + private Object alloc(NewNode alloc) { + TypeStruct type = alloc._ptr._obj; + Object[] body=null; + int num; + if( type.isAry() ) { + long sz = (Long)val(alloc.in(1)); + long n = offToIdx(sz, type); + if( n < 0 ) + throw new NegativeArraySizeException(""+n); + body = new Object[(int)n+1]; // Array body + var elem = type._fields[1]._type; + if (elem instanceof TypeInteger) { + for (int i=0; i<n; i++) body[i+1] = 0L; + } else if (elem instanceof TypeFloat) { + for (int i = 0; i < n; i++) body[i+1] = 0D; + } else { + assert elem instanceof TypeMemPtr; + } + // Length value + body[0] = vall(alloc.in(2+2)); + } else { + body = new Object[num = type._fields.length]; + for( int i=0; i<num; i++ ) + body[i] = switch( alloc._ptr._obj._fields[i]._type ) { + case TypeInteger ti -> 0L; + case TypeFloat tf -> 0; + default -> null; + }; + } + Object[] mems = new Object[type._fields.length+2]; + // mems[0] is control + mems[1] = new Obj(type,body); // the ref + // mems[2+...] are memory aliases + return mems; + } + + private Object load(LoadNode load) { + var from = (Obj)val(load.ptr()); + var off = vall(load.off()); + var idx = getFieldIndex(from.struct, load, (Long)off); + if( idx==from.struct._fields.length-1 && from.struct.isAry() ) { + long len = from.fields.length - from.struct._fields.length + 1; + long i = offToIdx(off, from.struct); + if( i < 0 || i >= len ) + throw new ArrayIndexOutOfBoundsException("Array index out of bounds " + i + " < " + len); + return from.fields[(int)i+from.struct._fields.length-1]; + + } else + return from.fields[idx]; + } + + private Object store(StoreNode store) { + var to = (Obj)val(store.ptr()); + var off = vall(store.off()); + var val = val(store.val()); + var idx = getFieldIndex(to.struct, store, (Long)off); + if( idx==to.struct._fields.length-1 && to.struct.isAry() ) { + long len = to.fields.length - to.struct._fields.length + 1; + long i = offToIdx(off, to.struct); + if( i < 0 || i >= len ) + throw new RuntimeException("Array index out of bounds " + off + " <= " + len); + to.fields[(int)i+to.struct._fields.length-1] = val; + } else + to.fields[idx] = val; + return null; + } + + private static boolean isTrue(Object obj) { + if (obj == null) return false; + if (obj instanceof Number n) return n.longValue() != 0; + return true; + } + + private Object val(Node node) { + return values[node._nid]; + } + + private long vall(Node node) { + var v = val(node); + if (v instanceof Number n) return n.longValue(); + throw new AssertionError("Not a long " + v); + } + private double vald(Node node) { + var v = val(node); + if (v instanceof Number n) return n.doubleValue(); + if( v == null ) return 0; + throw new AssertionError("Not a double " + v); + } + + private Object cons(ConstantNode cons) { + var type = cons.compute(); + if (type instanceof TypeInteger i) return i.isConstant() ? i.value() : Integer.MAX_VALUE; + if (type instanceof TypeFloat i) return i.value(); + return null; + } + + private Object exec(Node node) { + return switch (node) { + case ConstantNode cons -> cons(cons); + case AddNode add -> vall(add.in(1)) + vall(add.in(2)); + case AddFNode add -> vald(add.in(1)) + vald(add.in(2)); + case BoolNode.EQF eq -> vald(eq.in(1)) == vald(eq.in(2)) ? 1L : 0L; + case BoolNode.LEF le -> vald(le.in(1)) <= vald(le.in(2)) ? 1L : 0L; + case BoolNode.LTF lt -> vald(lt.in(1)) < vald(lt.in(2)) ? 1L : 0L; + case BoolNode.EQ eq -> Objects.equals(val(eq.in(1)), val(eq.in(2))) ? 1L : 0L; + case BoolNode.LE le -> vall(le.in(1)) <= vall(le.in(2)) ? 1L : 0L; + case BoolNode.LT lt -> vall(lt.in(1)) < vall(lt.in(2)) ? 1L : 0L; + case DivNode div -> div(div); + case DivFNode div -> div(div); + case MinusNode minus -> -vall(minus.in(1)); + case MinusFNode minus -> -vald(minus.in(1)); + case MulNode mul -> vall(mul.in(1)) * vall(mul.in(2)); + case MulFNode mul -> vald(mul.in(1)) * vald(mul.in(2)); + case NotNode not -> isTrue(val(not.in(1))) ? 0L : 1L; + case SubNode sub -> vall(sub.in(1)) - vall(sub.in(2)); + case SubFNode sub -> vald(sub.in(1)) - vald(sub.in(2)); + case ShlNode shl -> vall(shl.in(1)) << vall(shl.in(2)); + case ShrNode shr -> vall(shr.in(1)) >>> vall(shr.in(2)); + case SarNode sar -> vall(sar.in(1)) >> vall(sar.in(2)); + case AndNode and -> vall(and.in(1)) & vall(and.in(2)); + case OrNode or -> vall(or .in(1)) | vall(or .in(2)); + case XorNode xor -> vall(xor.in(1)) ^ vall(xor.in(2)); + case CastNode cast -> val(cast.in(1)); + case ToFloatNode cast -> (double)vall(cast.in(1)); + case LoadNode load -> load(load); + case StoreNode store -> store(store); + case NewNode alloc -> alloc(alloc); + case CProjNode cproj -> ((Object[])val(cproj.ctrl()))[cproj._idx]; + case ProjNode proj -> ((Object[])val( proj.in(0) ))[ proj._idx]; + case MemMergeNode mem -> null; + case ReadOnlyNode ro -> val(ro.in(1)); + case CallNode call -> Utils.TODO(); // should not reach here, go the IfNode route + default -> throw new AssertionError("Unexpected node " + node); + }; + } + + /** + * Run the graph until either a return is found or the number of loop iterations are done. + */ + public Object evaluate(long parameter, int loops) { + if (start == null) return Status.TIMEOUT; + var s = new Object[start.compute()._types.length]; + s[1] = MEMORY; + s[2] = parameter; + values[start._nid] = s; + int i=0; + Scheduler.Block block = this.startBlock; + for(;;) { + for(; i<block.nodes().length;i++) values[block.nodes()[i]._nid] = exec(block.nodes()[i]); + i=0; + switch (block.exit()) { + case null: + return Status.FALLTHROUGH; + case ReturnNode ret : return val(ret .expr()); + case IfNode ifn: + block = block.next()[isTrue(val(ifn.in(1))) ? 0 : 1]; + if (block == null) return Status.FALLTHROUGH; + break; + case RegionNode region: + if (loops-- <= 0) return Status.TIMEOUT; + int exit = block.exitId(); + assert exit > 0 && region.nIns() > exit; + block = block.next()[0]; + assert block != null; + for (; i < block.nodes().length; i++) { + if (!(block.nodes()[i] instanceof PhiNode phi)) break; + var val = region instanceof FunNode fun && "main".equals(fun._name) && ((ParmNode)phi)._idx==2 && exit==1 + ? parameter + : val(phi.in(exit)); + phiCache.add(val); + } + for (i=0; i<phiCache.size(); i++) { + values[block.nodes()[i]._nid] = phiCache.get(i); + } + phiCache.clear(); + break; + default: + throw Utils.TODO("Unexpected control node " + block.exit()); + } + } + } + + public static Object evaluate(StopNode graph) { + return evaluate(graph, 0); + } + + public static Object evaluate(StopNode graph, long parameter) { + return evaluate(graph, parameter, 1000); + } + + public static Object evaluate(StopNode graph, long parameter, int loops) { + var res = evaluateWithResult(graph, parameter, loops); + if (res == Status.TIMEOUT) throw new RuntimeException("Timeout"); + return res; + } + + public static Object evaluateWithResult(StopNode graph, long parameter, int loops) { + return new Evaluator(graph).evaluate(parameter, loops); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java new file mode 100644 index 00000000..7ba9272f --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java @@ -0,0 +1,606 @@ +package com.seaofnodes.simple.evaluator; + +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.Type; + +import java.util.*; + +/** + * A late scheduler for test cases. + * <br> + * <br> + * This scheduler is used for tests and is not the final scheduler. + * <br> + * <br> + * The scheduler schedules not as late as possible. Since it is just for test cases this is fine. + * A better scheduler will try to place nodes outside of loops. Due to the schedule late strategy + * this scheduler might place initialization code in loops. + * <br> + * We schedule late here since memory edges have some implicit information. There can only be one + * memory edge (for a field) at a time. However, before <code>if</code> statements this might split. + * When scheduling early a store might be moved out of the loop but then there are two edges alive. + * The one through the store node and the adjacent one. This is an invalid schedule and instead of + * handling this case a late scheduler does not have this problem as memory edges are combined + * through phi nodes. + * <br> + * <br> + * A bit about this scheduler. It first discovers all alive nodes and allocates side data for them. + * Then the control flow graph is build. Then, all phis are placed and finally all the remaining nodes + * in an order where a node is placed when all alive uses are placed. Finally, the output is produced. + */ +public class Scheduler { + + /** + * A basic block with a schedule of the containing nodes. + * + * @param nodes The nodes in the final schedule of this block + * @param exit The exit node of this block. + * @param exitId In case the next block is a region this gives the entry id into the region. + * @param next The next blocks. + */ + public record Block(Node[] nodes, Node exit, int exitId, Block[] next) {} + + /** + * Definition of a block in the building process. + */ + private static class BasicBlock { + + /** + * Dominator of this block + */ + final BasicBlock dom; + /** + * Id of this block where <code>a.depth < b.depth</code> means that a is before b or + * both are in different branches. + */ + final int depth; + /** + * Previous blocks + */ + final BasicBlock[] prev; + /** + * Entry to this block + */ + final Node entry; + /** + * Schedules node in reverse order. + */ + final ArrayList<Node> reverseSchedule = new ArrayList<>(); + + /** + * Initialize the block. + * @param entry The node that started this block + * @param depth The depth of this node. All previous nodes need to have a lower value. + * @param prev The previous blocks. + */ + BasicBlock(Node entry, int depth, BasicBlock... prev) { + BasicBlock dom = null; + int max = -1; + for(var bb: prev) { + max = Math.max(max, bb.depth); + dom = dom==null?bb:dom(dom, bb); + } + assert depth > max; + this.dom = dom; + this.depth = depth; + this.entry = entry; + this.prev = prev; + } + BasicBlock(Node entry, BasicBlock prev) { + this(entry,prev.depth+1,prev); + } + + } + + /** + * Ancillary data for nodes used during the scheduling process. + */ + private static class NodeData { + + /** + * The node this ancillary data is used for. + */ + final Node node; + + /** + * Number of alive users not yet scheduled. + */ + int users = 1; + + /** + * Block into which the node should be placed so far. + * This will be updated with every placed use. + */ + BasicBlock block; + + /** + * Initialize ancillary data for a node + * @param node The node this ancillary data is associated with. + */ + NodeData(Node node) { + this.node = node; + } + + } + + /** + * Ancillary data for nodes. + */ + private final IdentityHashMap<Node, NodeData> data = new IdentityHashMap<>(); + /** + * List of nodes which can be placed since all users are placed. + */ + private final Stack<NodeData> scheduleQueue = new Stack<>(); + + /** + * Get the ancillary data for an alive node. + * @param node The node for which the ancillary data is requested. + * @return The ancillary data for the node. + */ + NodeData d(Node node) { + return od(node).orElseThrow(AssertionError::new); + } + + /** + * Get the ancillary data for a potentially dead node. + * @param node The node for which the ancillary data is requested. + * @return The ancillary data for the node an alive node or + * <code>Optional.empty()</code> for <code>null</code> or dead nodes. + */ + Optional<NodeData> od(Node node) { + return Optional.ofNullable(data.get(node)); + } + + /** + * Checks a node for validity + * @param data The node to check. + * @return true if all placed inputs are before this node. + */ + private boolean isValid(NodeData data) { + return data.node._inputs.stream() + .map(i->i==null?null:d(i)) + .map(d->d==null||d.users>0?null:d.block) + .allMatch(d->d==null||dom(d, data.block)==d); + } + + /** + * Checks that node is not XCtrl. + * @param node The node to check. + * @return true if the node is not XCtrl. + */ + private static boolean isNotXCtrl(Node node) { + return !(node instanceof ConstantNode c && c.compute() == Type.XCONTROL) && !(node instanceof XCtrlNode); + } + + /** + * Return the dominator of <code>a</code> and <code>b</code> + * @param a Block a + * @param b Block b + * @return The dominator of <code>a</code> and <code>b</code> + */ + private static BasicBlock dom(BasicBlock a, BasicBlock b) { + while (a!=b) { + if (a.depth >= b.depth) a=a.dom; + if (b.depth > a.depth) b=b.dom; + } + return a; + } + + /** + * Return if a is before b. + * @param a Block a + * @param b Block b + * @return true is a is before b. + */ + private static boolean isBefore(BasicBlock a, BasicBlock b) { + while (b.depth > a.depth) { + while (b.dom.depth > a.depth) b = b.dom; + for (int i=0; i<b.prev.length-1; i++) { + if (isBefore(a, b.prev[i])) return true; + } + b = b.prev[b.prev.length-1]; + } + return a == b; + } + + /** + * Is node placed during the control flow graph build. + * @param data Node to check. + * @return true if node is placed during the control flow graph build. + */ + private static boolean isPinnedNode(NodeData data) { + return data.node instanceof CFGNode || data.node instanceof PhiNode; + } + + /** + * Refine placement of a node. + * @param data The node to refine. + * @param block The block before the node should be scheduled. + */ + private void refinePlacement(NodeData data, BasicBlock block) { + assert !isPinnedNode(data); + data.block = data.block == null ? block : dom(data.block, block); + assert isValid(data); + } + + /** + * Refine placement if before is happening before data + * @param data The node for which the placement should be refined. + * @param before The node before which the data node should be placed. + */ + private void optionalRefinePlacement(NodeData data, Node before) { + od(before).ifPresent(d->{ + // before might be in a different branch. So check this case with isBefore + // and only refine the placement if it is not in a different branch. + if (isBefore(d.block, data.block)) refinePlacement(data, d.block); + }); + } + + /** + * Schedule all nodes not yet scheduled. + */ + private void doSchedule() { + while (!scheduleQueue.isEmpty()) { + var data = scheduleQueue.pop(); + assert !isPinnedNode(data); + + assert data.block != null; + assert data.users == 0; + + if (data.node instanceof MemOpNode l && l._isLoad) { + assert isValid(data); + // Handle anti-deps of load nodes. + // At this point all anti-dep nodes are scheduled, + // but they did not refine the placement + // so do that now. + var mem = data.node.in(1); + for(var out : mem._outputs) { + if (out instanceof PhiNode p) { + var r = p.in(0); + for (int i = 1; i < p.nIns(); i++) { + if (p.in(i) == mem) optionalRefinePlacement(data, r.in(i)); + } + } else if (!(out instanceof MemOpNode ld && ld._isLoad)) { + optionalRefinePlacement(data, out); + } + } + } + + assert isValid(data); + data.block.reverseSchedule.add(data.node); + + for(var in:data.node._inputs) { + if (in!=null) update(d(in), data.block); + } + if (data.node instanceof MemOpNode s && !s._isLoad) { + // Store nodes have anti-deps to load nodes. + // So decrease the uses of these loads when the store is placed. + for (var out: ((Node)s).in(1)._outputs) { + if (out instanceof MemOpNode ld && ld._isLoad) od(out).ifPresent(this::decUsers); + } + } + } + + // Now all nodes should be placed and have a block assigned + assert this.data.values().stream().noneMatch(d->d.block==null); + } + + /** + * Decrement the not-yet-placed users of a block. + * @param data The node for which the users should be decremented. + */ + private void decUsers(NodeData data) { + assert data.users > 0; + if (--data.users == 0) { + assert data.block != null; + assert isValid(data); + + // When all users are gone this node can be scheduled. + scheduleQueue.push(data); + } + } + + /** + * Update the placement of a node + * @param data The node to update + * @param block The block before which the node should happen. + */ + private void update(NodeData data, BasicBlock block) { + assert block != null; + if (data.users == 0) { + assert isPinnedNode(data); + return; + } + refinePlacement(data, block); + decUsers(data); + } + + /** + * Checks if a CFG node is ready to be placed. This is used for regions joining branches. + * @param node The CFG node to check + * @return true if all parents of a CFG node are placed. + */ + private boolean isCFGNodeReady(Node node) { + if (node instanceof LoopNode l) { + assert d(l.in(1)).block != null; + } else if (node instanceof RegionNode r) { + for(int i=1; i<r.nIns(); i++) { + if (od(r.in(i)).map(d->d.block==null).orElse(false)) return false; + } + } else if (node instanceof XCtrlNode) { + return false; + } else { + assert d(node.in(0)).block != null; + } + return true; + } + + /** + * Schedule all phi nodes. + * @param phiQueue List of all the phi nodes to schedule. + */ + private void schedulePhis(Stack<NodeData> phiQueue) { + while(!phiQueue.empty()) { + var data = phiQueue.pop(); + var phi = (PhiNode) data.node; + var r = phi.in(0); + data.block = d(r).block; + for(int i=1; i<phi.nIns(); i++) { + var in = phi.in(i); + od(r.in(i)).ifPresent(d->update(d(in), d.block)); + } + } + } + + /** + * Build the control flow graph. + * @param start The start node. + */ + private void doBuildCTF(StartNode start) { + var queue = new Stack<NodeData>(); + var phiQueue = new Stack<NodeData>(); + queue.push(d(start)); + while (!queue.isEmpty()) { + var data = queue.pop(); + var node = data.node; + assert node instanceof CFGNode; + BasicBlock block = switch (node) { + case StartNode s -> new BasicBlock(s, 0); + case LoopNode l -> new BasicBlock(l, d(l.in(1)).block); + case RegionNode r -> { + var prev = new ArrayList<BasicBlock>(); + int depth = 0; + for(int i=1; i<r.nIns(); i++) { + if( !od(r.in(i)).isPresent() ) continue; + BasicBlock bb = d(r.in(i)).block; + prev.add(bb); + depth = Math.max(depth,bb.depth); + } + yield new BasicBlock(r, depth+1, prev.toArray(BasicBlock[]::new)); + } + case IfNode i -> d(i.in(0)).block; + case ReturnNode ret -> d(ret .in(0)).block; + case CallNode call -> throw Utils.TODO(); + default -> new BasicBlock(node, d(node.in(0)).block); + }; + data.block = block; + if (node instanceof RegionNode r) { + // Regions might have phis which need to be scheduled. + // Put them on a list for later scheduling. + for (var out:r._outputs) { + if (out instanceof PhiNode p) { + var d = this.data.get(p); + if (d!=null) { + d.users=0; + phiQueue.push(d); + } + } + } + } + if( node instanceof FunNode fun ) { + // Cannot find CFG user from fun via a naive search, because always find + // Return but Return is not always the one CFG user. + CFGNode n = fun.uctrl(); + assert isCFGNodeReady(n) && d(n).block==null; + queue.push(d(n)); + } else if( !(node instanceof ReturnNode) ) { + for (Node n : node._outputs) + if(n instanceof CFGNode && isCFGNodeReady(n) && d(n).block == null) + queue.push(d(n)); + } + var b = block; + for(var in:data.node._inputs) od(in).ifPresent(d->update(d, b)); + } + + // Schedule all phi nodes. + schedulePhis(phiQueue); + } + + /** + * Mark a node alive and create ancillary data for it. + * @param queue List into which this node should be placed if it needs to be visited. + * @param node Node which should be marked alive. + * @param cfg If this node is a CFG node. + */ + private void markAlive(Stack<NodeData> queue, Node node, boolean cfg) { + var nd = od(node).orElse(null); + if (nd != null) { + // Node was already visited, just increase the users. + if (nd.users>0) nd.users++; + return; + } + assert node instanceof CFGNode == cfg; + assert isNotXCtrl(node); + nd = new NodeData(node); + if (cfg) nd.users=0; + data.put(node, nd); + queue.push(nd); + } + + /** + * Mark all alive nodes. + * First all CFG nodes are marked, then all other nodes. + * @param node THe start node. + */ + private void doMarkAlive(Node node) { + var cfgQueue = new Stack<NodeData>(); + var dataQueue = new Stack<NodeData>(); + var mem = new Stack<NodeData>(); + markAlive(cfgQueue, node, true); + // Mark all CFG nodes. + while (!cfgQueue.isEmpty()) { + var data = cfgQueue.pop(); + node = data.node; + assert node instanceof CFGNode; + if (!(node instanceof ReturnNode) ) { + for (var out : node._outputs) if (out!=null && out instanceof CFGNode && isNotXCtrl(out)) markAlive(cfgQueue, out, true); + } + for (var in : node._inputs) if(in!=null && !(in instanceof CFGNode) && isNotXCtrl(in)) markAlive(dataQueue, in, false); + } + // Mark all other nodes. + while (!dataQueue.isEmpty()) { + var data = dataQueue.pop(); + node = data.node; + assert !(node instanceof CFGNode); + if (node instanceof PhiNode phi) { + var r = phi.in(0); + for (int i=1; i<phi.nIns(); i++) { + if (od(r.in(i)).isPresent()) markAlive(dataQueue, phi.in(i), false); + } + } else { + for (var in : node._inputs) if (in != null && !(in instanceof CFGNode)) markAlive(dataQueue, in, false); + } + if (node instanceof MemOpNode s && !s._isLoad) mem.push(data); + } + // Handle store nodes and increase load with an anti-dep to the store. + while (!mem.isEmpty()) { + var data = mem.pop(); + node = data.node; + for(var out:node.in(1)._outputs) { + if (out instanceof MemOpNode ld && ld._isLoad) od(out).ifPresent(d->d.users++); + } + } + } + + /** + * Helper function to append the nodes of a block to an array of nodes + * @param arr The array the nodes of a block should be appended to. + * @param node A potentially separator node + * @param block The block from which the nodes should be appended. + * @return The combined new array. + */ + private static Node[] appendNodes(Node[] arr, Node node, BasicBlock block) { + var idx = arr.length; + arr = Arrays.copyOf(arr, arr.length+block.reverseSchedule.size()+(node==null?0:1)); + if (node != null) arr[idx++] = node; + for(int i=block.reverseSchedule.size()-1;i>=0;i--) { + arr[idx++] = block.reverseSchedule.get(i); + } + return arr; + } + + /** + * Find the CFG output of a node + * @param node The node + * @return The CFG output of the node. + */ + private static CFGNode findSingleCFGOut(Node node) { + if (node instanceof StartNode) { + for(var n:node._outputs) + if (n instanceof FunNode fun && "main".equals(fun._name) ) + return fun; + return null; + } + if( node instanceof FunNode fun ) return fun.uctrl(); + assert node._outputs.stream().filter(x -> x instanceof CFGNode).limit(2).count()<=1; + for(var n:node._outputs) if(n instanceof CFGNode cfg) return cfg; + return null; + } + + /** + * Build the final data structure after all the scheduling happened. + * @param start The start node + * @return The final schedule. + */ + private Block build(Node start) { + var blocks = new IdentityHashMap<Node, Block>(); + var queue = new Stack<NodeData>(); + queue.push(d(start)); + + // Visit all CFG nodes and create blocks for them. + // This can combine blocks. + while(!queue.isEmpty()) { + var data = queue.pop(); + var first = data.node; + Node prev; + var last = first; + Node[] arr = new Node[0]; + if (first instanceof RegionNode) { + var nodes = new ArrayList<Node>(); + for(var out: first._outputs) { + if (out instanceof PhiNode) { + if(od(out).isPresent()) nodes.add(out); + } + } + arr = nodes.toArray(arr); + } + arr = appendNodes(arr, null, data.block); + assert !(first instanceof IfNode); + for(;;) { + prev = last; + last = findSingleCFGOut(last); + if (last == null) break; + if (last instanceof RegionNode) { + if (blocks.get(last) == null && last != first) + queue.push(d(last)); + break; + } + if (last instanceof ReturnNode) break; + if (last instanceof IfNode if_) { + for(var out:if_._outputs) if (out instanceof CFGNode && blocks.get(out) == null) queue.push(d(out)); + break; + } + data = d(last); + arr = appendNodes(arr, data.node, data.block); + } + blocks.put(first, switch(last) { + case null -> new Block(arr, null, -1, new Block[0]); + case IfNode i -> new Block(arr, i, -1, new Block[2]); + case RegionNode r -> new Block(arr, r, r._inputs.find(prev), new Block[1]); + case ReturnNode r -> new Block(arr, r , -1, new Block[0]); + default -> throw new AssertionError("Unexpected block exit node"); + }); + } + // Update the next pointer of all blocks. + for (var block: blocks.values()) { + switch(block.exit) { + case null: break; + case IfNode i: + for(var out:i._outputs) if (out instanceof CProjNode p) block.next[p._idx] = blocks.get(p); + break; + case RegionNode r: + block.next[0] = blocks.get(r); + break; + default: + assert block.exit instanceof ReturnNode; + } + } + // And return the block for the start node. + return blocks.get(start); + } + + /** + * Create a schedule for the program reachable from start. + * @param start The start node + * @return The final schedule. + */ + public static Scheduler.Block schedule(StartNode start) { + var scheduler = new Scheduler(); + scheduler.doMarkAlive(start); + scheduler.doBuildCTF(start); + scheduler.doSchedule(); + return scheduler.build(start); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java new file mode 100644 index 00000000..30a548ad --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -0,0 +1,174 @@ +package com.seaofnodes.simple.fuzzer; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.Eval2; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.Node; +import java.util.ArrayList; +import java.util.Objects; +import java.util.Random; +import java.util.function.Consumer; + +/** + * Implementation of the fuzzer. + * Random testcases are generated using the ScriptGenerator. + * This will generate a script similar to how the parser parses it but instead of parsing it will + * randomly choose a variation the parser would parse and generate it. + * These scripts are the parsed by the parser and all exceptions are caught and filtered + * to only show one occurrence of one problem. This includes script which generate different + * results when the compiled graph with and without peeps is executed. + * To aid debugging scripts that cause errors are then reduced by applying rules and checking + * that the same issue persists. + */ +public class Fuzzer { + + private static final int EVAL_TIMEOUT = 1000; + + /** + * List of exceptions already encountered. + * This is used to filter new exceptions and discard them if they are already found. + */ + private final ArrayList<Throwable> exceptions = new ArrayList<>(); + + /** + * Filter and record an exception in a script. + * @param e The exception caused by the script + * @param script The script causing the exception + * @param reproducer Reproducer which should be used to reproduce the test case. + */ + private void recordException(Throwable e, String script, Consumer<String> reproducer, long seed) { + for (var ex : exceptions) { + if (FuzzerUtils.isExceptionFromSameCause(ex, e)) return; + } + exceptions.add(e); + System.out.println("========== Stack =========="); + e.printStackTrace(System.out); + System.out.println("========== Seed =========="); + System.out.println(seed); + //System.out.println("========== Code ==========="); + //System.out.println(script); + System.out.println("========= Reduced ========="); + System.out.println(Reducer.reduce(script, e, reproducer)); + System.out.println("==========================="); + System.out.flush(); + } + + private static boolean neq(Object a, Object b) { + if (Objects.equals(a, b)) return false; + return true; + } + + /** + * Check that two graphs result in the same output when supplied with a value + * @param e1 Evaluator for the first graph + * @param e2 Evaluator for the second graph + * @param in The input value to both graphs to test for an equal output + */ + private static void checkGraphs(CodeGen code1, CodeGen code2, long in) { + var r1 = Eval2.eval(code1, in, EVAL_TIMEOUT); + var r2 = Eval2.eval(code2, in, EVAL_TIMEOUT); + if( r1 == null || r2 == null ) return; // Timeout test invalid + if( neq(r1, r2) ) + throw new RuntimeException("Different calculations values " + r1 + " vs " + r2); + } + + /** + * Run checks for script. Compile the script with peeps enabled and disabled. + * Check that exceptions raised in the parser by both methods are the same and only happens if the script may be invalid. + * If the script was successfully parsed check that both version behave the same. + * @param script The script to test + * @param valid If the script is definitely valid. If not some exceptions may be suppressed. + */ + private static void runCheck(String script, boolean valid) { + CodeGen code1; + try { + code1 = FuzzerUtils.parse(script, 123); + } catch (RuntimeException e1) { + try { + FuzzerUtils.parse(script, 456); + } catch (RuntimeException e2) { + if (FuzzerUtils.isExceptionFromSameCause(e1, e2)) { + if (!valid || e1.getClass() == Parser.ParseException.class) return; + } else { + e1.addSuppressed(e2); + } + } + throw e1; + } + CodeGen code2 = FuzzerUtils.parse(script, 456); + checkGraphs(code1, code2, 0); + checkGraphs(code1, code2, 1); + checkGraphs(code1, code2, 10); + } + + /** + * Check that the script does not result in exceptions. + * @param script The script to test. + * @param valid If the script is valid or if it might contain syntax errors. + */ + private void check(String script, boolean valid, long seed) { + try { + runCheck(script, valid); + } catch (Throwable e) { + recordException(e, script, s->runCheck(s, valid), seed); + } + } + + /** + * Run one test with the given seed, and check for peephole counts. + * @param seed The seed to use for generating this test case + */ + public int fuzzPeepTiming(long seed, int max_nids) { + var rand = new Random(seed); + var sb = new StringBuilder(); + new ScriptGenerator(rand, sb, true).genProgram(); + try { + var code = new CodeGen(sb.toString()); + + code.parse(); + int nids = code.UID(); + if( nids <= max_nids ) return max_nids; // Smaller, looking for trends as we grow + int parse_peeps= code._iter_cnt; + int parse_nops = code._iter_nop_cnt; + double parse_nop_ratio = (double)parse_nops/parse_peeps; + double parse_peeps_per_node = (double)parse_peeps/nids; + + code.opto(); + int iter_peeps= code._iter_cnt; + int iter_nops = code._iter_nop_cnt; + double iter_nop_ratio = (double)iter_nops/iter_peeps; + double iter_peeps_per_node = (double)iter_peeps/nids; + + System.out.printf("%6d | Parsing: peeps: %5d, nops: %5d, ratio: %5.3f, last UID: %5d ps/node: %5.3f | Iter: peeps: %6d, nops: %6d, ratio: %5.3f, ps/node: %5.3f\n", + seed, + parse_peeps,parse_nops,parse_nop_ratio,nids,parse_peeps_per_node, + iter_peeps , iter_nops, iter_nop_ratio, iter_peeps_per_node); + return nids; + } catch (Throwable e) { + // ignore exceptions, let the normal fullPeeps catch and reduce, + // we're interested in valid program optimizations + } + return max_nids; + } + + + /** + * Run one test with the given seed. + * @param seed The seed to use for generating this test case + */ + public void fuzzPeeps(long seed) { + var rand = new Random(seed); + var sb = new StringBuilder(); + var valid = new ScriptGenerator(rand, sb, true).genProgram(); + check(sb.toString(), valid, seed); + } + + + /** + * Check that no exceptions happened. + * @return true if no exception happened + */ + public boolean noExceptions() { + return exceptions.isEmpty(); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java new file mode 100644 index 00000000..b90b663d --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java @@ -0,0 +1,111 @@ +package com.seaofnodes.simple.fuzzer; + +import com.seaofnodes.simple.IterPeeps; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.StopNode; +import com.seaofnodes.simple.type.TypeInteger; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.util.BitSet; + +/** + * Some utilities for the fuzzer. + */ +class FuzzerUtils { + + /** + * Get the val of a private field + */ + @SuppressWarnings("unchecked") + public static <T> T getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + return (T) (obj instanceof Class<?> clz ? getField(clz, name).get(null) : getField(obj.getClass(), name).get(obj)); + } + + /** + * Get access to a private field + */ + public static Field getField(Class<?> clz, String name) throws NoSuchFieldException { + var field = clz.getDeclaredField(name); + try { + field.setAccessible(true); + } catch (Throwable e) { + // Intentionally left empty + } + return field; + } + + /** + * Try to rethrow an exception or create a runtime exception and throw it. + */ + public static RuntimeException rethrow(Throwable e) { + if (e instanceof RuntimeException re) return re; + if (e instanceof Error er) throw er; + return new RuntimeException(e); + } + + /** + * Some problems might trigger debug output. Suppress this output with a null print stream during fuzzing. + */ + public static final PrintStream NULL_PRINT_STREAM = new PrintStream(OutputStream.nullOutputStream()); + + /** + * Check that the messages are similar. + */ + private static boolean similarMessages(String a, String b) { + if (a == null || b == null) return true; + a = a.replaceAll("'\\w+'", "'normalized'").replaceAll("variable \\w+", "variable normalized"); + b = b.replaceAll("'\\w+'", "'normalized'").replaceAll("variable \\w+", "variable normalized"); + return a.equals(b); + } + + /** + * Check if both stack traces have the same first entry. Empty stack traces can happen with NPE optimization + * and allow the other to be any stack trace. + */ + private static boolean sameStackTraceOrigin(StackTraceElement[] a, StackTraceElement[] b) { + if (a == null || a.length == 0 || b == null || b.length == 0) return true; // One or both stack traces are missing. + return a[0].equals(b[0]); + } + + /** + * Checks if two exceptions are caused by the same problem. + */ + public static boolean isExceptionFromSameCause(Throwable a, Throwable b) { + if (a == null || b == null) return a == b; // Handle the null case + if (a.getClass() != b.getClass()) return false; // Different classes + if (!similarMessages(a.getMessage(), b.getMessage())) return false; // Different messages. Null is allowed as wildcard + var s1 = a.getStackTrace(); + var s2 = b.getStackTrace(); + if (!sameStackTraceOrigin(s1, s2)) return false; // Different stack trace origins + if (!isExceptionFromSameCause(a.getCause(), b.getCause())) return false; // Different causes + var o1 = a.getSuppressed(); + var o2 = b.getSuppressed(); + if (o1 == null || o2 == null || o1.length != o2.length) return o1 == o2; // Different suppression + for (int i=0; i<o1.length; i++) { + if (!isExceptionFromSameCause(o1[i], o2[i])) return false; // Different suppression + } + return true; + } + + /** + * Parse script with peepholes enabled or disabled + */ + public static CodeGen parse(String script, long workListSeed) { + var err = System.err; + var out = System.out; + try { + System.setErr(NULL_PRINT_STREAM); + System.setOut(NULL_PRINT_STREAM); + var code = new CodeGen(script, TypeInteger.BOT, workListSeed); + return code.driver(CodeGen.Phase.LocalSched); + } finally { + System.setErr(err); + System.setOut(out); + } + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java new file mode 100644 index 00000000..3731b40c --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java @@ -0,0 +1,176 @@ +package com.seaofnodes.simple.fuzzer; + +import java.util.Arrays; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class is used to reduce test cases created by the fuzzer. + * It applies rewrite rules to the test case and checks that the + * test still produces the same exception. + * This is done in a fixpoint iteration until every rewrite produces + * no or a different exception. + */ +public class Reducer { + + private interface Replacer extends BiConsumer<StringBuilder, Matcher> {} + + /** + * Holder for the rewrite rules + */ + private record Replace(Pattern pattern, boolean iterate, Replacer... replacements) { + Replace(Pattern p, Object... replacements) { + this(p, replacements[0] == Boolean.TRUE, Arrays.stream(replacements).filter(o->!(o instanceof Boolean)).map(Replace::from).toArray(Replacer[]::new)); + } + private static Replacer from(Object o) { + if (o instanceof Replacer r) return r; + if (o instanceof Integer i) return (s, m)->s.append(m.group(i)); + if (o instanceof String r) return (s, m)->s.append(r); + throw new IllegalArgumentException(); + } + } + + /** + * List of rewrite rules. + */ + private static final Replace[] REPLACEMENTS = { + new Replace(Pattern.compile("\\b(?:[^\\W0]|\\w\\w+)\\b"), "0", "1"), + new Replace(Pattern.compile("(?<=\\n|^)[^\\n]+(?=\\n|$)"), true, ""), + new Replace(Pattern.compile("(?<=[+\\-*/<>=(])--"), ""), + new Replace(Pattern.compile("\\b--"), "+"), + new Replace(Pattern.compile("-"), ""), + new Replace(Pattern.compile("\\b\\+-"), "-"), + new Replace(Pattern.compile("(?<![+\\-*/<>=])(?:-|\\*|/|>=|<=|>|<|!=|==)(?![+\\-*/<>=])"), "+"), + new Replace(Pattern.compile("\\{((?:[^{}]|\\{})+)(?:\\n[ \\t]*)?}"), "", "{}", 1), + new Replace(Pattern.compile("(?<=\\n|^)([ \\t]*)\\{\\n(.*?)\\n\\1}", Pattern.DOTALL), 2), + new Replace(Pattern.compile("\\(([^()]+)\\)"), 1), + new Replace(Pattern.compile("\\d+\\+"), ""), + new Replace(Pattern.compile("\\+\\d+"), ""), + new Replace(Pattern.compile("\\n\\n+"), "\n"), + new Replace(Pattern.compile("^\\n+|\\n+$"), ""), + new Replace(Pattern.compile("else *"), ""), + new Replace(Pattern.compile("\\bif\\([^()]+\\) *"), ""), + new Replace(Pattern.compile("\\bwhile\\([^()]+\\) *"), ""), + new Replace(Pattern.compile("\\bstruct \\w+ \\{[^{}]*}"), "") + }; + + /** + * Predicate to test if the reduced script generates the same error. + */ + private final Predicate<String> tester; + + /** + * Create a reducer with a test function. + * @param tester Test function which returns true when the same issue is found in the script. + */ + private Reducer(Predicate<String> tester) { + this.tester = tester; + } + + /** + * Rewrites the script with one rewrite rule. + */ + private String doReplacement(String script, Pattern pattern, Replacer[] replacements) { + var matcher = pattern.matcher(script); + var sb = new StringBuilder(); + var last = 0; + var skip = 0; + outer: + while (matcher.find(last + skip)) { + sb.append(script, last, matcher.start()); + last = matcher.end(); + skip = last == matcher.start() ? 1 : 0; + var pos = sb.length(); + for (var replacement : replacements) { + replacement.accept(sb, matcher); + var tail = sb.length(); + sb.append(script, last, script.length()); + if (tester.test(sb.toString())) { + sb.setLength(tail); + continue outer; + } + sb.setLength(pos); + } + last = matcher.start(); + skip = 1; + } + sb.append(script, last, script.length()); + return sb.toString(); + } + + /** + * Clean up variable names from the random ones generated by the fuzzer. + */ + private String cleanVariables(String script) { + var matcher = Pattern.compile("\\b([0-9a-zA-Z_]+) ([0-9a-zA-Z_]+)").matcher(script); + int[] num = {0, 0}; + while (matcher.find()) { + var type = matcher.group(1); + var var = matcher.group(2); + if (type.equals("new")) continue; + String prefix = "v"; + int t = 0; + if (type.equals("struct")) { + t = 1; + prefix = "s"; + } + if (var.matches(prefix+"\\d+")) continue; + while (script.contains(prefix+num[t])) num[t]++; + var n = script.replaceAll("\\b"+var+"\\b", prefix + num[t]); + if (tester.test(n)) { + script = n; + } + } + return script; + } + + /** + * Run all rewrite rules once. + */ + private String doAllReplacements(String script) { + for (var replace: REPLACEMENTS) { + String old; + do { + old = script; + script = doReplacement(script, replace.pattern, replace.replacements); + } while (replace.iterate && !old.equals(script)); + } + return script; + } + + /** + * Reduce the script to a minimal version which produces the same problem with a test function. + * @param tester Test function which returns true when the same issue is found in the script. + */ + public static String reduce(String script, Predicate<String> tester) { + var reducer = new Reducer(tester); + String old; + do { + old = script; + script = reducer.doAllReplacements(script); + script = reducer.cleanVariables(script); + } while (!script.equals(old)); + return script; + } + + /** + * Reduce the script to a minimal version which produces the same problem with a reproducer. + * @param ex The exception originally caused by the script + * @param reproducer Test function which throws an exception which will be checked against ex. + */ + public static String reduce(String script, Throwable ex, Consumer<String> reproducer) { + return reduce(script, s->{ + Throwable nex = null; + try { + reproducer.accept(s); + } catch (Throwable e) { + nex = e; + } + return FuzzerUtils.isExceptionFromSameCause(nex, ex); + }); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java new file mode 100644 index 00000000..f119fa1a --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java @@ -0,0 +1,869 @@ +package com.seaofnodes.simple.fuzzer; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.node.ScopeNode; +import com.seaofnodes.simple.type.TypeInteger; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.function.Predicate; +import static com.seaofnodes.simple.Parser.KEYWORDS; + + +/** + * Generate a pseudo random script. + * This generator will generate the same script for two random number generators initialized with the same seed. + * To generate valid code it implements the parser but instead of parsing it emits the statements and expressions. + * It is guaranteed to terminate. + */ +public class ScriptGenerator { + + private static final int MAX_STATEMENTS_PER_BLOCK = 5; + private static final int MAX_BINARY_EXPRESSIONS_PER_EXPRESSION = 10; + private static final int MAX_UNARY_EXPRESSIONS_PER_EXPRESSION = 6; + private static final int MAX_NAME_LENGTH = 10; + private static final int MAX_BLOCK_DEPTH = 4; + private static final int MAX_EXPRESSION_DEPTH = 10; + private static final int MAX_SUFFIX_RECURSIVE_DEPTH = 10; + + /** + * Number of spaces per indentation + */ + private static final int INDENTATION = 4; + /** + * Valid characters for identifiers. The last 10 are not valid for the first character. + */ + private static final String VAR_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"; + + /** + * Flag for a statement that it does not pass on control flow to the next statement. + */ + private static final int FLAG_STOP = 0x01; + /** + * Flag for a statement that it contains a final if statement without an else branch. + */ + private static final int FLAG_IF_WITHOUT_ELSE = 0x02; + /** + * Binary operators. + */ + private static final String[] BINARY_OP = {"==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/", "&", ">>", ">>>", "<<"}; + /** + * Unary operators. + */ + private static final String[] UNARY_OP = {"-"}; + + private static class Type { + final String name; + Type(String name) {this.name=name;} + boolean isa(Type other) { return this == other; } + } + + private static class TypeStruct extends Type { + record Field (String name, Type type, TypeStruct struct) {} + Field[] fields; + final TypeNullable nullable = new TypeNullable(this); + TypeStruct(String name) { + super(name); + } + boolean isa(Type other) { return this == other || other == nullable; } + } + + private static class TypeNullable extends Type { + + final Type base; + + TypeNullable(Type base) { + super(base.name+"?"); + this.base = base; + } + + } + + private static final Type TYPE_FLT = new Type("flt"); + + private static class TypeInt extends Type { + + TypeInt(String name) { + super(name); + } + + @Override + boolean isa(Type other) { + return super.isa(other) || other == TYPE_FLT || other instanceof TypeInt; + } + } + + private static final List<TypeInt> INTTYPES = new ArrayList<>(); + + static { + for( var e : Parser.defaultTypes().entrySet() ) { + if( e.getValue() instanceof TypeInteger ) { + INTTYPES.add(new TypeInt(e.getKey())); + } + } + } + + private static final Type TYPE_INT = INTTYPES.stream().filter(t->t.name.equals("int")).findAny().get(); + private static final Type TYPE_BOOL = INTTYPES.stream().filter(t->t.name.equals("bool")).findAny().get(); + + private static class Variable { + final String name; + final Type declared; + final Variable shadowing; + Type type; + boolean shadowed = false; + + Variable(String name, Type type, Variable var) { + this.name = name; + this.declared = type; + this.type = type; + this.shadowing = var; + } + } + + /** + * The random number generator used for random decisions. + */ + private final Random random; + /** + * Indentation depth. + */ + private int indentation = 0; + /** + * Output string builder + */ + private final StringBuilder sb; + /** + * Number of nested loops + */ + private int loopDepth = 0; + /** + * Current scope start in the variables list. + */ + private int currScopeStart = 0; + /** + * The depth of blocks allowed. + */ + private int depth = MAX_BLOCK_DEPTH; + /** + * The depth of expressions allowed. + */ + private int exprDepth = MAX_EXPRESSION_DEPTH; + /** + * Current variables in scope. + */ + private final ArrayList<Variable> variables = new ArrayList<>(); + /** + * All defined structs. + */ + private final ArrayList<TypeStruct> structs = new ArrayList<>(); + /** + * All forward declared structs + */ + private final ArrayList<TypeStruct> forwardStructs = new ArrayList<>(); + /** + * Allow to declare structs. + */ + private boolean allowStructs = true; + /** + * If this needs to generate valid scripts or is also allowed to generate invalid ones. + */ + private boolean generateValid; + /** + * If this script might be invalid. + */ + private boolean maybeInvalid = false; + + + /** + * + * @param random the random number generator. + * @param sb the script will be written to this string builder. + * @param generateValid if the script should be guaranteed valid or if it is allowed to contain bugs. + */ + public ScriptGenerator(Random random, StringBuilder sb, boolean generateValid) { + this.random = random; + this.sb = sb; + this.generateValid = generateValid; + } + + /** + * Random number with log distribution + * @param max The maximum value + * @return A random number in the range [0, max) with higher numbers being more uncommon. + */ + private int randLog(int max) { + max--; + int n = 1<<max; + var num = random.nextInt(n); + for (int i=max-1; i>=0; i--) { + if ((num & (1<<i))!=0) return max-i-1; + } + return max; + } + + /** + * Helper function to print indentation. + * @return sb for chaining + */ + private StringBuilder printIndentation() { + return sb.repeat(' ', indentation); + } + + /** + * Generate a random name for variables. This might be invalid when it is a keyword. + * @return The random name generated + */ + private StringBuilder getRandomName() { + int len = random.nextInt(MAX_NAME_LENGTH) + 1; + StringBuilder sb = new StringBuilder(len); + sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()-10))); + for (int i=1; i<len; i++) + sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()))); + return sb; + } + + /** + * Should invalid code be generated? + * @return True if invalid code should be generated + */ + private boolean generateInvalid() { + if (generateValid || random.nextInt(1000)>2) return false; + maybeInvalid = true; + return true; + } + + /** + * Lookup a variable by name + * @param name name of the variable + * @return The index of the variable in variables or -1 + */ + private int lookup(String name) { + for (int i=variables.size()-1; i>=0; i--) { + if (variables.get(i).name.equals(name)) return i; + } + return -1; + } + + /** + * Lookup a variable by name + * @param name name of the variable + * @return THe variable of null if not found + */ + private Variable lookupVar(String name) { + int idx = lookup(name); + return idx == -1 ? null : variables.get(idx); + } + + /** + * Add a variable to the current scope + * @param name The name of the variable + * @param type The declared type of the variable + */ + private void addVariable(String name, Type type) { + var old = lookupVar(name); + variables.add(new Variable(name, type, old)); + if (old != null) old.shadowed = true; + } + + /** + * Pop variables to level to + * @param to Level until which variables should be popped + */ + private void popVariables(int to) { + while (variables.size() > to) { + var v = variables.removeLast(); + if (v.shadowing != null) v.shadowing.shadowed = false; + } + } + + /** + * Get a random visible variable patching pred + * @param pred The predicate the variable needs to match + * @return A random visible variable matching pred or null if no variable mached pred. + */ + private Variable findVisibleVariablesMatching(Predicate<Variable> pred) { + int num=0; + for (var v:variables) { + if (!v.shadowed && pred.test(v)) num++; + } + if (num == 0) return null; + num = random.nextInt(num); + for (var v:variables) { + if (!v.shadowed && pred.test(v) && num-- == 0) return v; + } + throw new AssertionError(); + } + + /** + * Get a structure by name + * @param name Name of the structure + * @return The structure of null + */ + private TypeStruct getStruct(String name) { + for (var s:structs) { + if (s.name.equals(name)) return s; + } + for (var s:forwardStructs) if (s.name.equals(name)) return s; + return null; + } + + /** + * Generate a variable name. This might be new random name or a name from an outer scope. + * If the program is allowed to be invalid this can return keywords and already used variable names in the current scope. + * @return The variable name. + */ + private String getVarName() { + if (currScopeStart > 0 && random.nextInt(10) > 7) { + // Use a variable outside the current scope. + var v = variables.get(random.nextInt(currScopeStart)); + if (lookup(v.name) < currScopeStart) return v.name; + if (!generateValid) { + maybeInvalid = true; + return v.name; + } + } + // Generate a new random variable name + StringBuilder sb = getRandomName(); + var v = sb.toString(); + if (!generateValid && (KEYWORDS.contains(v) || lookup(v) >= currScopeStart)) { + maybeInvalid = true; + return v; + } + while (KEYWORDS.contains(v) || lookup(v) >= currScopeStart) { + sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()))); + v = sb.toString(); + } + return v; + } + + private <T> T randFromList(List<T> list) { + return list.get(random.nextInt(list.size())); + } + + /** + * Get a random structure name + * @return A random structure name + */ + private String getStructName() { + if (!structs.isEmpty() && generateInvalid()) return randFromList(structs).name; + if (!forwardStructs.isEmpty() && generateInvalid()) return randFromList(forwardStructs).name; + + // Generate a new random struct name + StringBuilder sb = getRandomName(); + var v = sb.toString(); + if (!generateValid && (KEYWORDS.contains(v) || getStruct(v) != null)) { + maybeInvalid = true; + return v; + } + while (KEYWORDS.contains(v) || getStruct(v) != null) { + sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()))); + v = sb.toString(); + } + return v; + } + + public TypeInt getIntType() { + return randFromList(INTTYPES); + } + + /** + * Get a random type + * @return A random type + */ + private Type getType(boolean allowForward) { + int t = random.nextInt(10); + if( t<5 || structs.isEmpty() ) return getIntType(); + if( t<7 ) return TYPE_FLT; + if (allowForward && random.nextInt(10)==0) { + int i=random.nextInt(forwardStructs.size()+1); + if (i>0) return forwardStructs.get(i-1).nullable; + var name = getStructName(); + var struct = new TypeStruct(name); + forwardStructs.add(struct); + return struct.nullable; + } + TypeStruct struct = randFromList(structs); + if (random.nextBoolean()) return struct.nullable; + return struct; + } + + private Type getType() { + return getType(false); + } + + /** + * Generates a program and writes it into the string builder supplied in the constructor. + * @return If the program is allowed to be invalid returns if something potentially invalid was generated. + */ + public boolean genProgram() { + var oldGenerateValid = generateValid; + if (!generateValid && random.nextBoolean()) generateValid = true; + this.maybeInvalid = false; + addVariable(ScopeNode.ARG0, TYPE_INT); + currScopeStart = variables.size(); + if ((genStatements() & FLAG_STOP) == 0) { + if (generateValid || random.nextInt(10)<7) { + genReturn(); + } else { + this.maybeInvalid = true; + } + } + generateValid = oldGenerateValid; + return !this.maybeInvalid; + } + + /** + * Generate a list of statements. + * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the last statement generated + */ + public int genStatements() { + var num = random.nextInt(MAX_STATEMENTS_PER_BLOCK * (depth+1)); + for (int i=0; i<num; i++) { + printIndentation(); + var stop = genStatement(); + sb.append("\n"); + if ((stop & FLAG_STOP) != 0) return stop; + } + return 0; + } + + /** + * Generate a single statement + * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement + */ + public int genStatement() { + return switch (random.nextInt(12)) { + case 0 -> allowStructs || generateInvalid() ? genStruct() : genAssignment(); + case 1 -> genBlock(); + case 2 -> genIf(); + case 3 -> genCountedLoop(); + case 4 -> genWhile(); + case 5, 6 -> genDecl(); + case 7, 8, 9 -> genAssignment(); + case 10 -> genNullCheck(); + default -> genExit(); + }; + } + + /** + * Generate a new structure + * @return 0 + */ + public int genStruct() { + int idx = random.nextInt(forwardStructs.size()+10); + TypeStruct struct; + if (idx < 10) { + var name = getStructName(); + struct = new TypeStruct(name); + forwardStructs.add(struct); + } else { + struct = forwardStructs.get(idx-10); + } + var fields = new TypeStruct.Field[generateInvalid() ? 0 : random.nextInt(10)]; + sb.append("struct ").append(struct.name).append(" {\n"); + indentation += INDENTATION; + for (int i=0; i<fields.length; i++) { + var fieldName = getRandomName(); + var type = getType(true); + printIndentation().append(type.name).append(" ").append(fieldName).append(";\n"); + fields[i] = new TypeStruct.Field(fieldName.toString(), type, struct); + } + indentation -= INDENTATION; + printIndentation().append("};"); + struct.fields = fields; + forwardStructs.remove(struct); + structs.add(struct); + return 0; + } + + /** + * Generate a statement for if, else and while blocks. + * This is special since it disallows declarations and prefers to generate blocks. + * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement + */ + public int genStatementBlock() { + var oldAllowStructs = allowStructs; + allowStructs = false; + indentation += INDENTATION; + int res; + if (depth == 0) { + res = random.nextBoolean() ? genAssignment() : genExit(); + } else { + depth--; + res = switch (random.nextInt(12)) { + case 1, 2, 3, 4, 5 -> { + depth++; + indentation -= INDENTATION; + var ret = genBlock(); + indentation += INDENTATION; + depth--; + yield ret; + } + case 6 -> genIf(); + case 7 -> genCountedLoop(); + case 8 -> genWhile(); + case 9 -> genAssignment(); + case 10 -> genNullCheck(); + default -> genExit(); + }; + depth++; + } + indentation -= INDENTATION; + allowStructs = oldAllowStructs; + return res; + } + + /** + * Generate an exit. This can be return or break and continue when in a loop. + * @return FLAG_STOP + */ + public int genExit() { + if (loopDepth == 0 && generateValid) return genReturn(); + return switch (random.nextInt(7)) { + case 0,1,2 -> { + if (loopDepth == 0) maybeInvalid = true; + sb.append("continue;"); + yield FLAG_STOP; + } + case 3,4,5 -> { + if (loopDepth == 0) maybeInvalid = true; + sb.append("break;"); + yield FLAG_STOP; + } + default -> genReturn(); + }; + } + + /** + * Generate a block statement. After a certain depth only empty blocks are generated to ensure termination of the generator. + * @return flag FLAG_STOP for the generated statement + */ + public int genBlock() { + if (depth == 0) { + sb.append("{}"); + return 0; + } + var oldAllowStructs = allowStructs; + var oldCSS = currScopeStart; + allowStructs = false; + currScopeStart = variables.size(); + depth--; + sb.append("{\n"); + indentation += INDENTATION; + var stop = genStatements(); + indentation -= INDENTATION; + printIndentation().append("}"); + popVariables(currScopeStart); + depth++; + currScopeStart = oldCSS; + allowStructs = oldAllowStructs; + return stop & ~FLAG_IF_WITHOUT_ELSE; + } + + /** + * Generate an if statement with an optional else block. + * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement + */ + public int genIf() { + sb.append("if("); + genExpression(TYPE_BOOL, true); + sb.append(") "); + var stop = genStatementBlock(); + if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) { + sb.append("\n"); + printIndentation().append("else "); + stop &= genStatementBlock(); + } else { + stop = FLAG_IF_WITHOUT_ELSE; + } + return stop; + } + + /** + * Generate a block with a null check + * if (nullable) { handle_nullable_as_non_nullable; } + */ + public int genNullCheck() { + var v = findVisibleVariablesMatching(va->va.declared instanceof TypeNullable); + if (v == null) return genIf(); + var n = (TypeNullable)v.declared; + boolean negate = random.nextBoolean(); + sb.append("if("); + if (negate) sb.append("!"); + sb.append(v.name).append(")"); + if (!negate) v.type = n.base; + var stop = genStatementBlock(); + v.type = n; + if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) { + sb.append("\n"); + printIndentation().append("else "); + if (negate) v.type = n.base; + stop &= genStatementBlock(); + v.type = n; + } else { + stop = FLAG_IF_WITHOUT_ELSE; + } + return stop; + } + + /** + * Generate a while loop. + * @return 0 + */ + public int genWhile() { + sb.append("while("); + genExpression(TYPE_BOOL, true); + sb.append(") "); + loopDepth++; + genStatementBlock(); + loopDepth--; + return 0; + } + + /** + * Generate a counted loop of the form + * <code> + * { + * int varname = start_expr; + * while (varname < end_expr) loop_body; + * } + * </code> + * @return 0 + */ + public int genCountedLoop() { + if (depth == 0) return genWhile(); + var oldCSS = currScopeStart; + currScopeStart = variables.size(); + sb.append("{\n"); + indentation += INDENTATION; + String name = getVarName(); + printIndentation().append("int ").append(name).append("="); + genExpression(TYPE_INT, true); + sb.append(";\n"); + addVariable(name, TYPE_INT); + printIndentation().append("while(").append(name).append("<"); + genExpression(TYPE_INT, true); + sb.append(") {\n"); + indentation += INDENTATION; + depth--; + loopDepth++; + printIndentation().append(name).append("=").append(name).append("+"); + genExpression(TYPE_INT, true); + sb.append(";\n"); + genStatements(); + loopDepth--; + depth++; + indentation -= INDENTATION; + printIndentation().append("}\n"); + indentation -= INDENTATION; + printIndentation().append("}"); + popVariables(currScopeStart); + currScopeStart = oldCSS; + return 0; + } + + /** + * Generate a declaration statement. + * @return 0 + */ + public int genDecl() { + var type = getType(); + var name = getVarName(); + // Always make them mutable + sb.append(generateInvalid() ? getRandomName() : type.name).append(" !").append(name); + if (!(type instanceof TypeNullable) || random.nextBoolean()) { + sb.append("="); + genExpression(type, true); + } + sb.append(";"); + addVariable(name, type); + return 0; + } + + /** + * Generate an assignment statement. + * @return 0 + */ + public int genAssignment() { + Type type; + Type declared; + if (generateInvalid()) { + sb.append(getRandomName()); + type = getType(); + declared = type; + } else { + var v = findVisibleVariablesMatching(va->true); + if (v == null) return genDecl(); + sb.append(v.name); + type = v.type; + declared = v.declared; + } + if (generateInvalid()) { + var name = getRandomName(); + sb.append(".").append(name); + declared = getType(); + } else if (type instanceof TypeStruct s && s.fields.length > 0 && random.nextBoolean()) { + var field = s.fields[random.nextInt(s.fields.length)]; + sb.append(".").append(field.name); + declared = field.type; + } + sb.append("="); + genExpression(declared, true); + sb.append(";"); + return 0; + } + + /** + * Generate a return statement. + * @return FLAG_STOP + */ + public int genReturn() { + var type = getType(); + sb.append("return "); + genExpression(type, true); + sb.append(";"); + return FLAG_STOP; + } + + /** + * Generate a binary expression. + * This method does not care about operator precedence. + */ + public void genExpression(Type type, boolean change) { + if (change && generateInvalid()) type = getType(); + if (!(type instanceof TypeInt) && type != TYPE_FLT) { + genUnary(type, false); + return; + } + var num = randLog(MAX_BINARY_EXPRESSIONS_PER_EXPRESSION); + while(num-->0) { + genUnary(type, false); + sb.append(BINARY_OP[random.nextInt(BINARY_OP.length)]); + } + genUnary(type, false); + } + + /** + * Generate a unary expression. + */ + public void genUnary(Type type, boolean change) { + if (change && generateInvalid()) type = getType(); + if (!(type instanceof TypeInt) && type != TYPE_FLT) { + genSuffix(type, false); + return; + } + var num = randLog(MAX_UNARY_EXPRESSIONS_PER_EXPRESSION); + while(num-->0) { + sb.append(UNARY_OP[random.nextInt(UNARY_OP.length)]); + } + genSuffix(type, false); + } + + public TypeStruct.Field getField(Type type) { + int n=0; + for (var s: structs) { + for (var f: s.fields) { + if (f.type.isa(type)) n++; + } + } + if (n==0) return null; + n = random.nextInt(n); + for (var s: structs) { + for (var f: s.fields) { + if (f.type.isa(type)) if (n--==0) return f; + } + } + throw new AssertionError(); + } + + /** + * Generate + */ + public void genSuffix(Type type, boolean change) { + genSuffixRecursive(type, change, MAX_SUFFIX_RECURSIVE_DEPTH); + } + + public void genSuffixRecursive(Type type, boolean change, int depth) { + if (change && generateInvalid()) type = getType(); + if (depth > 0 && random.nextBoolean()) { + if (generateInvalid()) { + var field = getRandomName(); + var t = getType(); + genSuffixRecursive(t, true, depth-1); + sb.append(".").append(field); + return; + } else { + var field = getField(type); + if (field != null) { + genSuffixRecursive(field.struct, true, depth-1); + sb.append(".").append(field.name); + return; + } + } + } + genPrimary(type, false); + } + + /** + * Generate a primary expression. + */ + public void genPrimary(Type type, boolean change) { + if (change && generateInvalid()) type = getType(); + var rand = random.nextInt(10); + if (rand == 0 && exprDepth != 0) { + sb.append("("); + exprDepth--; + genExpression(type, false); + exprDepth++; + sb.append(")"); + } else if (rand < 6) { + genVariable(type); + } else { + genConst(type); + } + } + + /** + * Generate a constant. + */ + public void genConst(Type type) { + if (generateInvalid()) type = getType(); + if (type instanceof TypeInt || (type == TYPE_FLT && random.nextBoolean())) { + var rand = random.nextInt(10); + switch (rand) { + case 0 -> sb.append("true"); + case 1 -> sb.append("false"); + default -> sb.append(random.nextInt(1<<(rand-2))); + } + } else if (type == TYPE_FLT) { + sb.append(random.nextFloat()); + } else if (type instanceof TypeNullable n) { + if ((n.base instanceof TypeStruct s && s.fields == null) || random.nextBoolean()) { + sb.append("null"); + } else { + sb.append("new ").append(generateInvalid() ? getRandomName() : n.base.name); + } + } else { + sb.append("new ").append(generateInvalid() ? getRandomName() : type.name); + } + } + + /** + * Generate a variable. If there are none generate a number instead. + * If the program is allowed to be invalid a random name can be generated in some cases. + */ + public void genVariable(Type type) { + if (generateInvalid()) { + sb.append(getRandomName()); + return; + } + if (generateInvalid()) type = getType(); + var t = type; + var v = findVisibleVariablesMatching(va->va.type.isa(t)); + if (v == null) genConst(type); + else sb.append(v.name); + } + +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/antiDep1.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/antiDep1.smp new file mode 100644 index 00000000..b18dfd16 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/antiDep1.smp @@ -0,0 +1,11 @@ +// -*- mode: java; -*- +struct S { int f; }; +var v0 = new S; +S? v1; +if (arg) v1 = new S; +if (v1) { + v0.f = v1.f; +} else { + v0.f = 2; +} +return v0; diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/arg_count.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/arg_count.c new file mode 100644 index 00000000..e247e28a --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/arg_count.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +__attribute__ ((CALL_CONV)) +extern double addAll( long long i0, double f1, long long i2, double f3, long long i4, double f5, long long i6, double f7, long long i8, double f9, long long i10, double f11, long long i12, double f13, long long i14, double f15, long long i16, double f17, long long i18, double f19 ); + +int main() { + double result = addAll( 0, 1.1, 2, 3.1, 4, 5.1, 6, 7.1, 8, 9.1, 10, 11.1, 12, 13.1, 14, 15.1, 16, 17.1, 18, 19.1 ); + printf("%f\n", result); + return 0; +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/arg_count.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/arg_count.smp new file mode 100644 index 00000000..6b6bc0ae --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/arg_count.smp @@ -0,0 +1,6 @@ +// -*- mode: java; -*- +val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15, int x16, flt f17 int x18, flt f19 -> + return + i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 + + i10+f11+i12+f13+i14+f15+x16+f17+x18+f19 ; +}; diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/array1.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/array1.smp new file mode 100644 index 00000000..6b10f92c --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/array1.smp @@ -0,0 +1,9 @@ +// -*- mode: java; -*- +int[] !ary = new int[arg]; +// Fill [0,1,2,3,4,...] +for( int i=0; i<ary#; i++ ) + ary[i] = i; +// Fill [0,1,3,6,10,...] +for( int i=0; i<ary#-1; i++ ) + ary[i+1] += ary[i]; +return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c new file mode 100644 index 00000000..127cbe73 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c @@ -0,0 +1,18 @@ +#include <stdio.h> +#include <stdbool.h> + +typedef struct AryInt { + unsigned int len; + unsigned char data[]; +} AryInt; + +__attribute__ ((CALL_CONV)) +extern AryInt* brain_fuck(); + +int main() { + AryInt* ps = brain_fuck(); + + printf("%.*s", ps->len, ps->data); + + return 0; +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp new file mode 100644 index 00000000..ed90cb84 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp @@ -0,0 +1,46 @@ +// -*- mode: java; -*- +val brain_fuck = { -> + u8[] !program = new u8[106];program[0] = 43;program[1] = 43;program[2] = 43;program[3] = 43;program[4] = 43;program[5] = 43;program[6] = 43;program[7] = 43;program[8] = 91;program[9] = 62;program[10] = 43;program[11] = 43;program[12] = 43;program[13] = 43;program[14] = 91;program[15] = 62;program[16] = 43;program[17] = 43;program[18] = 62;program[19] = 43;program[20] = 43;program[21] = 43;program[22] = 62;program[23] = 43;program[24] = 43;program[25] = 43;program[26] = 62;program[27] = 43;program[28] = 60;program[29] = 60;program[30] = 60;program[31] = 60;program[32] = 45;program[33] = 93;program[34] = 62;program[35] = 43;program[36] = 62;program[37] = 43;program[38] = 62;program[39] = 45;program[40] = 62;program[41] = 62;program[42] = 43;program[43] = 91;program[44] = 60;program[45] = 93;program[46] = 60;program[47] = 45;program[48] = 93;program[49] = 62;program[50] = 62;program[51] = 46;program[52] = 62;program[53] = 45;program[54] = 45;program[55] = 45;program[56] = 46;program[57] = 43;program[58] = 43;program[59] = 43;program[60] = 43;program[61] = 43;program[62] = 43;program[63] = 43;program[64] = 46;program[65] = 46;program[66] = 43;program[67] = 43;program[68] = 43;program[69] = 46;program[70] = 62;program[71] = 62;program[72] = 46;program[73] = 60;program[74] = 45;program[75] = 46;program[76] = 60;program[77] = 46;program[78] = 43;program[79] = 43;program[80] = 43;program[81] = 46;program[82] = 45;program[83] = 45;program[84] = 45;program[85] = 45;program[86] = 45;program[87] = 45;program[88] = 46;program[89] = 45;program[90] = 45;program[91] = 45;program[92] = 45;program[93] = 45;program[94] = 45;program[95] = 45;program[96] = 45;program[97] = 46;program[98] = 62;program[99] = 62;program[100] = 43;program[101] = 46;program[102] = 62;program[103] = 43;program[104] = 43;program[105] = 46; + int d = 0; + u8[] !output = new u8[0]; + u8[] !data = new u8[100]; + + for( int pc = 0; pc < program#; pc++ ) { + var command = program[pc]; + if (command == 62) { + d++; + } else if (command == 60) { + d--; + } else if (command == 43) { + data[d]++; + } else if (command == 45) { + data[d]--; + } else if (command == 46) { + // Output a byte; increase the output array size + var old = output; + output = new u8[output# + 1]; + for( int i = 0; i < old#; i++ ) + output[i] = old[i]; + output[old#] = data[d]; // Add the extra byte on the end + } else if (command == 44) { + data[d] = 42; + } else if (command == 91) { + if (data[d] == 0) { + for( int d = 1; d > 0; ) { + command = program[++pc]; + if (command == 91) d++; + if (command == 93) d--; + } + } + } else if (command == 93) { + if (data[d]) { + for( int d = 1; d > 0; ) { + command = program[--pc]; + if (command == 93) d++; + if (command == 91) d--; + } + } + } + } + return output; +}; diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/fib.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/fib.c new file mode 100644 index 00000000..263d2f13 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/fib.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdbool.h> + + +__attribute__ ((CALL_CONV)) +extern int fib(int x); + +int main() { + printf("["); + for (int i = 0; i < 10; i++) { + printf("%d", fib(i)); + if (i < 9) printf(", "); + } + printf("]"); + return 0; +}
\ No newline at end of file diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/fib.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/fib.smp new file mode 100644 index 00000000..1512541b --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/fib.smp @@ -0,0 +1,11 @@ +// -*- mode: java; -*- +val fib = {int n -> + int f1=1; + int f2=1; + while( n-- > 1 ){ + int temp = f1+f2; + f1=f2; + f2=temp; + } + return f2; +};
\ No newline at end of file diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp new file mode 100644 index 00000000..00c2a161 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp @@ -0,0 +1,9 @@ +// -*- mode: java; -*- +struct s0 { + i16 v1; + u8 v2; +}; +s0 !l4Hi9BCnsd = new s0; +if( new s0.v2+arg ) + while(1) l4Hi9BCnsd=new s0; +while(new s0.v2+new s0.v1<=0) {} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/loop2.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/loop2.c new file mode 100644 index 00000000..934f2d89 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/loop2.c @@ -0,0 +1,7 @@ + +__attribute__ ((CALL_CONV)) +extern int loop2(); + +int main() { + return loop2(); +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/loop2.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/loop2.smp new file mode 100644 index 00000000..01ec827e --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/loop2.smp @@ -0,0 +1,10 @@ +// -*- mode: java; -*- +int i = 0; +while(true) { + i += 1; + if( i==0 ) continue; + if( i==1 ) continue; + if( i==2 ) break; + continue; +} +return i; diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c new file mode 100644 index 00000000..7e7e796d --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <math.h> +#include <stdlib.h> + +extern double test_sqrt(double); + +int main( int argc, char** argv ) { + for( int i=0; i<10; i++ ) { + double d = test_sqrt((double)i), expect = sqrt((double)i); + double delta = fabs(d-expect); + printf("%d %f (%g)\n",i,d,delta); + if( delta > 1e-15 ) + return 1; + } + return 0; +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp new file mode 100644 index 00000000..be2dffec --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp @@ -0,0 +1,12 @@ +// -*- mode: java; -*- +// Newtons approximation to the square root +val test_sqrt = { flt x -> + flt epsilon = 1e-15; + flt guess = x; + while( 1 ) { + flt next = (x/guess + guess)/2; + if( guess-epsilon <= next & next <= guess+epsilon ) return guess; + //if( guess==next ) return guess; + guess = next; + } +}; diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp new file mode 100644 index 00000000..20a05445 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp @@ -0,0 +1,6 @@ +// -*- mode: java; -*- +val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15 -> + return + i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 + + i10+f11+i12+f13+i14+f15; +}; diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/person.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/person.c new file mode 100644 index 00000000..df95e1ca --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/person.c @@ -0,0 +1,34 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +typedef struct Person { + int age; +} Person; + +typedef struct PersonArray { + unsigned int length; + int pad; + Person* data[]; +} PersonArray; + +__attribute__ ((CALL_CONV)) +extern void fcn(PersonArray* people, int index); + +int main() { + Person base[5] = {{25},{1},{18},{45},{60}}; + unsigned char buf[8+8*5]; + PersonArray* people_array = (PersonArray*)buf; + people_array->length=5; + for (int i=0; i<people_array->length; i++) people_array->data[i] = &base[i]; + + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + + printf("%d\n", people_array->data[1]->age); // expected: 6 + + return 0; +}
\ No newline at end of file diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/person.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/person.smp new file mode 100644 index 00000000..9f0f5bea --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/person.smp @@ -0,0 +1,9 @@ +// -*-java-*- +struct Person { + int age; +}; + +val fcn = { Person?[] ps, int x -> + if( ps[x] ) + ps[x].age++; +};
\ No newline at end of file diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/sieve.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/sieve.c new file mode 100644 index 00000000..cac4cfa3 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/sieve.c @@ -0,0 +1,19 @@ +#include <stdio.h> +#include <stdbool.h> + +typedef struct AryInt { + unsigned int len; + unsigned int data[]; +} AryInt; + +__attribute__ ((CALL_CONV)) +extern AryInt *sieve(int N); + +int main() { + AryInt* ps = sieve(100); + printf("%d[",ps->len); + for( int i=0; i<ps->len; i++ ) + printf("%d, ",ps->data[i]); + printf("]"); + return 0; +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/sieve.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/sieve.smp new file mode 100644 index 00000000..569dbb96 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/sieve.smp @@ -0,0 +1,31 @@ +// -*- mode: java; -*- +val sieve = { int N -> + // The main Sieve array + bool[] !ary = new bool[N]; + // The primes less than N + u32[] !primes = new u32[N>>1]; + // Number of primes so far, searching at index p + int nprimes = 0, p=2; + // Find primes while p^2 < N + while( p*p < N ) { + // skip marked non-primes + while( ary[p] ) p++; + // p is now a prime + primes[nprimes++] = p; + // Mark out the rest non-primes + for( int i = p + p; i < ary#; i+= p ) + ary[i] = true; + p++; + } + + // Now just collect the remaining primes, no more marking + for( ; p < N; p++ ) + if( !ary[p] ) + primes[nprimes++] = p; + + // Copy/shrink the result array + u32[] !rez = new u32[nprimes]; + for( int j=0; j < nprimes; j++ ) + rez[j] = primes[j]; + return rez; +}; diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/sort.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/sort.c new file mode 100644 index 00000000..ad76cc7f --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/sort.c @@ -0,0 +1,28 @@ +#include <stdio.h> +#include <stdlib.h> + +// Test wrapper to call `sort.smp` + +typedef struct AryInt { + unsigned int len; + long long data[]; +} AryInt; + +__attribute__ ((CALL_CONV)) +extern AryInt* merge_sort(AryInt*); + + +long long primes[] = { 25, 97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 }; + +int main() { + AryInt *as = (AryInt*)primes; + + merge_sort(as); + + printf("%d[",as->len); + for( int i=0; i<as->len; i++ ) + printf("%d, ",as->data[i]); + printf("]"); + + return 0; +}
\ No newline at end of file diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/sort.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/sort.smp new file mode 100644 index 00000000..ae3f4f23 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/sort.smp @@ -0,0 +1,38 @@ +// -*- mode: java; -*- +// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort + +val merge_sort = { int[] a -> + int[] !b = new int[a#]; + copy_array (a, 0, a#, b); + split_merge(a, 0, a#, b); +}; + +val split_merge = { int[] b, int begin, int end, int[] a -> + if (end - begin <= 1) + return 0; + int middle = (end + begin) / 2; + split_merge(a, begin, middle, b); + split_merge(a, middle, end, b); + merge(b, begin, middle, end, a); + return 0; +}; + +val merge = { int[] b, int begin, int middle, int end, int[] a -> + int i = begin, j = middle; + + for (int k = begin; k < end; k++) { + // && and || + bool cond = false; + if (i < middle) { + if (j >= end) cond = true; + else if (a[i] <= a[j]) cond = true; + } + if (cond) b[k] = a[i++]; + else b[k] = a[j++]; + } +}; + +val copy_array = { int[] a, int begin, int end, int[] b -> + for (int k = begin; k < end; k++) + b[k] = a[k]; +}; diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.c new file mode 100644 index 00000000..2c6d0fa9 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.c @@ -0,0 +1,30 @@ +#include <stdio.h> +#include <stdbool.h> + +typedef struct ArrayU8 { + unsigned int length; + unsigned char data[]; +} ArrayU8; +typedef struct String { + ArrayU8 * cs; + long long _hashCode; +} String; + + +__attribute__ ((CALL_CONV)) +extern bool equals(String*st1, String*st2); + +int main() { + String test1 = {.cs=(ArrayU8*)"\x04\x00\x00\x00test"}; + String test2 = {.cs=(ArrayU8*)"\x05\x00\x00\x00test1"}; + + if( equals(&test1, &test2) ) { + printf("Expected test1 and test2 unequal"); + return 1; + } + if( !equals(&test2, &test2) ) { + printf("Expected test2 and test2 equal"); + return 1; + } + return 0; +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.smp new file mode 100644 index 00000000..dafa7456 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.smp @@ -0,0 +1,31 @@ +// -*- mode: java; -*- +struct String { + u8[] cs; + int _hashCode; +}; + +// Compare two Strings +val equals = { String self, String s -> + if( self == s ) return true; + if( self.cs# != s.cs# ) return false; + for( int i=0; i< self.cs#; i++ ) + if( self.cs[i] != s.cs[i] ) + return false; + return true; +}; + +// Return the String hashCode (cached, and never 0) +val hashCode = { String self -> + self._hashCode + ? self._hashCode + : (self._hashCode = _hashCodeString(self)); +}; + +val _hashCodeString = { String self -> + int hash=0; + if( self.cs ) + for( int i=0; i< self.cs#; i++ ) + hash = hash*31 + self.cs[i]; + if( !hash ) hash = 123456789; + return hash; +}; |
