diff options
| author | Tamás Hadházy <85063808+Hels15@users.noreply.github.com> | 2025-04-17 17:54:03 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-17 09:54:03 -0700 |
| commit | 71835b1cc3d597234e7fb50e7ddddf2b20cf10e2 (patch) | |
| tree | 90bc2976defa2b59596d05d9819ab047b20da076 | |
| parent | 38fd3c17c672c42fc768b0f31680b5d9679cac6d (diff) | |
| download | Simple-71835b1cc3d597234e7fb50e7ddddf2b20cf10e2.tar.gz Simple-71835b1cc3d597234e7fb50e7ddddf2b20cf10e2.zip | |
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>
6 files changed, 301 insertions, 27 deletions
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java index 8cc4621d..dbc86f62 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpARM.java @@ -11,7 +11,7 @@ public class CmpARM extends MachConcreteNode implements MachNode { @Override public RegMask outregmap() { return arm.FLAGS_MASK; } // SUBS (shifted register) - @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_CMP); } + @Override public void encoding( Encoding enc ) { arm.r_reg_subs(enc,this,arm.OP_CMP); } // General form: "cmp rs1, rs2" @Override public void asm(CodeGen code, SB sb) { diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java index c0193323..8a4f242a 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CmpIARM.java @@ -28,7 +28,7 @@ public class CmpIARM extends MachConcreteNode implements MachNode { @Override public boolean isClone() { return true; } @Override public Node copy() { return new CmpIARM(this); } - @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,in(1),in(1), arm.OPI_CMP,_imm); } + @Override public void encoding( Encoding enc ) { arm.imm_inst_subs(enc,in(1),in(1), arm.OPI_CMP,_imm); } // General form: "cmp rs1, 1" @Override public void asm(CodeGen code, SB sb) { diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java index ca3d096b..1d9d2efe 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -273,7 +273,7 @@ public class arm extends Machine { return (32-size)<<1 | immr << 6 | imms; } - // sh is encoded in opcdoe + // sh is encoded in opcode public static int imm_inst(int opcode, int imm12, int rn, int rd) { assert 0 <= rn && rn < 32; assert 0 <= rd && rd < 32; @@ -294,6 +294,13 @@ public class arm extends Machine { enc.add4(body); } + public static void imm_inst_subs(Encoding enc, Node n,Node n2, int opcode, int imm12) { + short reg1 = enc.reg(n2); + // 31 = 11111 + int body = imm_inst(opcode, imm12&0xFFF, reg1, 31); + enc.add4(body); + } + // for cases where rs1 and dst are the same, eg add x0, x0, 1 public static void imm_inst(Encoding enc, int opcode, int imm12, int self) { int body = imm_inst(opcode, imm12&0xFFF, self, self); @@ -335,7 +342,7 @@ public class arm extends Machine { assert 0 <= rm && rm < 32; assert 0 <= rn && rn < 32; assert 0 <= rd && rd < 32; - return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) << (rn << 5) | rd; + return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) | (rn << 5) | rd; } public static void r_reg(Encoding enc, Node n, int opcode) { short self = enc.reg(n); @@ -343,6 +350,14 @@ public class arm extends Machine { short reg2 = enc.reg(n.in(2)); int body = r_reg(opcode, 0, reg2, 0, reg1, self >= 32 ? reg1: self); enc.add4(body); + } + + public static void r_reg_subs(Encoding enc, Node n, int opcode) { + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + // 31 = 11111 + int body = r_reg(opcode, 0, reg2, 0, reg1, 31); + enc.add4(body); } public static int shift_reg(int opcode, int rm, int op2, int rn, int rd) { @@ -459,7 +474,8 @@ public class arm extends Machine { public static int f_cmp(int opcode, int ftype, int rm, int rn) { assert 0 <= rn && rn < 32; assert 0 <= rm && rm < 32; - return (opcode << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5) | 8; + // Todo: |8 is not needed + return (opcode << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5); } public static void f_cmp(Encoding enc, Node n) { short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET); @@ -472,19 +488,21 @@ public class arm extends Machine { return switch (bop) { case "==" -> COND.EQ; case "!=" -> COND.NE; - case "<" -> COND.LE; - case "<=" -> COND.LT; - case ">=" -> COND.GT; - case ">" -> COND.GE; + case "<" -> COND.LT; + case "<=" -> COND.LE; + case ">=" -> COND.GE; + case ">" -> COND.GT; default -> throw Utils.TODO(); }; } - // Todo: maybe missing zero here after delta << 5 + public static int b_cond(int opcode, int delta, COND cond) { // 24-5 == 19bits offset range assert -(1<<19) <= delta && delta < (1<<19); + assert (delta&3)==0; + delta>>=2; delta &= (1L<<19)-1; // Zero extend - return (opcode << 24) | (delta << 5) | cond.ordinal(); + return (opcode << 24) | ((delta)<< 5) | cond.ordinal(); } public static int cond_set(int opcode, int rm, COND cond, int rn, int rd) { @@ -500,6 +518,8 @@ public class arm extends Machine { } public static int b(int opcode, int delta) { assert -(1<<26) <= delta && delta < (1<<26); + assert (delta&3)==0; + delta>>=2; delta &= (1L<<26)-1; // Zero extend return (opcode << 26) | delta; } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java index d1db1609..1d37deb0 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -1,6 +1,7 @@ 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; @@ -94,10 +95,12 @@ public class Chapter21Test { // Return register A0 holds fib(8)==55 assertEquals(1.732051,R5.fregs[riscv.FA0 - riscv.F_OFFSET], 0.00001); - // TODO: Run on ARM eval - 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,"arm", "SystemV",19,null); + // arm + EvalArm64 A5 = TestArm64.build("newtonFloat", 0, 10); + 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 { @@ -134,9 +137,12 @@ public class Chapter21Test { // Return register A0 holds fib(8)==55 assertEquals(55,R5.regs[riscv.A0]); - // TODO: run on arm eval - String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/fib.smp")); - testCPU(src,"arm" , "SystemV",16,null); + // arm + EvalArm64 A5 = TestArm64.build("fib", 9, 16); + 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 { @@ -186,6 +192,7 @@ public class Chapter21Test { 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; @@ -198,7 +205,33 @@ public class Chapter21Test { assertEquals(0,trap); double result = R5.fregs[riscv.FA0 - riscv.F_OFFSET]; - // missing a float + assertEquals(22.8, result, 0.00001); + + // arm + EvalArm64 A5 = TestArm64.build("no_stack_arg_count", 0, 0); + + 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/EvalArm64.java b/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java index 55a16f84..370745dd 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java @@ -17,6 +17,12 @@ public class EvalArm64 { // 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]; @@ -26,6 +32,24 @@ public class EvalArm64 { _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; @@ -50,15 +74,205 @@ public class EvalArm64 { } // Load instruction from image buffer at PC - //ir = ld4s( 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 ; + } + } + // for the opcode we just match for bits[31:24]; + switch(opcode1) { + case 0xD2: { + // movz + // get immediate + rval = (ir >> 5) & 0x7FFF; + 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; + int lhs = (int)regs[rn]; + int rhs = (int)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; + int lhs = (int)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; + 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: frval = fregs[rs1] / fregs[rs2]; break; // fdiv + + 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; + } - //int rdid = ir & 0x1f; - //switch(ir & ) + pc += 4; } + // Handle traps and interrupts. if(trap != 0) { return trap; } - _cycle = _cycle; + _cycle = cycle; _pc = pc; return 0; } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java b/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java index 1325f1e1..874beca8 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java @@ -8,26 +8,33 @@ 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) throws IOException { - return build("src/test/java/com/seaofnodes/simple/progs",file, arg); + public static EvalArm64 build(String file, int arg, int spills) throws IOException { + return build("src/test/java/com/seaofnodes/simple/progs",file, arg, spills); } // Compile and run a simple program - public static EvalArm64 build(String dir, String file, int arg) throws IOException { + public static EvalArm64 build(String dir, String file, int arg, int spills) throws IOException { // Compile and export Simple String src = Files.readString(Path.of(dir+"/"+file+".smp")); CodeGen code = new CodeGen(src).driver("arm", "SystemV",null); + // 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.D0] = arg; + ARM.regs[arm.X0] = arg; // malloc memory starts at 64K and goes up return ARM; } |
