Skip to content

Commit

Permalink
RISC-V basic isel (#156)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>
  • Loading branch information
Hels15 and cliffclick authored Feb 11, 2025
1 parent 1c0b1ff commit 47b405d
Show file tree
Hide file tree
Showing 62 changed files with 2,149 additions and 44 deletions.
2 changes: 1 addition & 1 deletion chapter19/src/main/java/com/seaofnodes/simple/CodeGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
// If n is a MachConcrete, then its part of a multi-node expansion.
// It does not need instruction selection (already selected!)
// but it does need its inputs walked.
if( n instanceof MachConcreteNode || n instanceof MemOpX86 ) {
if( n instanceof MachConcreteNode || n instanceof MemOpX86) {
for( int i=0; i < n.nIns(); i++ )
n._inputs.set(i, _instSelect(n.in(i),map) );
return n;
Expand Down
7 changes: 7 additions & 0 deletions chapter19/src/main/java/com/seaofnodes/simple/RegMask.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ public class RegMask {

public RegMask(long x ) { _bits = BitSet.valueOf(new long[]{x}); }
public RegMask(long[] xs) { _bits = BitSet.valueOf( xs); }

public RegMask or(RegMask other) {
BitSet resultBits = (BitSet)this._bits.clone();
resultBits.or(other._bits);
return new RegMask(resultBits);
}

// Internal constructor
RegMask() { _bits = new BitSet(); }
RegMask(BitSet bs) { _bits = bs; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.seaofnodes.simple.node.cpus.riscv;

import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;

public class AddFRISC extends MachConcreteNode implements MachNode{
AddFRISC(Node addf) {super(addf);}

// Register mask allowed on input i.
@Override public RegMask regmap(int i) { assert i==1 || i==2; return riscv.FMASK; }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.FMASK; }
// Output is same register as input#1
@Override public int twoAddress() { return 1; }

// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}

// Default on double precision for now(64 bits)
// General form: "fadd.d rd = src1 + src2
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
}

@Override public String op() { return "addf"; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.seaofnodes.simple.node.cpus.riscv;

import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;

public class AddIRISC extends MachConcreteNode implements MachNode {
final TypeInteger _ti;
AddIRISC( Node add, TypeInteger ti ) {
super(add);
_inputs.pop();
_ti = ti;
}

// Register mask allowed on input i.
@Override public RegMask regmap(int i) {
// assert i== i;
return riscv.RMASK; }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.RMASK; }
// Output is same register as input#1
@Override public int twoAddress() { return 0; }

// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}

// General form: "addi rd = rs1 + imm"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + #");
_ti.print(sb);
}

@Override public String op() {
return _ti.value() == 1 ? "inc" : (_ti.value() == -1 ? "dec" : "addi");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.seaofnodes.simple.node.cpus.riscv;


import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;


public class AddRISC extends MachConcreteNode implements MachNode {

AddRISC( Node add) {super(add); }

// Register mask allowed on input i.
@Override public RegMask regmap(int i) {
assert i== 1 || i == 2 || i == 3;
return riscv.RMASK;
}
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.RMASK; }
// Output is same register as input#1
@Override public int twoAddress() { return 0; }

// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}

// General form: "rd = rs1 + rs2"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" + ").p(code.reg(in(2)));
}

@Override public String op() {
return "add";}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.seaofnodes.simple.node.cpus.riscv;

import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
import java.util.BitSet;
import java.lang.StringBuilder;

public class AndIRISC extends MachConcreteNode implements MachNode{
final TypeInteger _ti;
AndIRISC(AndNode and, TypeInteger ti) {
super(and);
_inputs.pop();
_ti = ti;
}
// Register mask allowed on input i.
// This is the normal calling convention
@Override public RegMask regmap(int i) { assert i==1 || i == 2; return riscv.RMASK; }
// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.RMASK; }

// Output is same register as input#1
@Override public int twoAddress() { return 0; }

// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}

// General form
// General form: "andi rd = rs1 & imm"
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #");
_ti.print(sb);
}

@Override public String op() { return "andi"; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.seaofnodes.simple.node.cpus.riscv;


import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
import java.util.BitSet;
import java.lang.StringBuilder;

public class AndRISC extends MachConcreteNode implements MachNode{
AndRISC(Node and) {
super(and);
}

// Register mask allowed on input i.
// This is the normal calling convention
@Override public RegMask regmap(int i) {
assert i==1 || i==2;
return riscv.RMASK;
}

// Register mask allowed as a result. 0 for no register.
@Override public RegMask outregmap() { return riscv.RMASK; }

// Output is same register as input#1
@Override public int twoAddress() { return 0; }

// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}

// General form
// General form: #rd = rs1 & rs2
@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(this)).p(" = ").p(code.reg(in(1)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.seaofnodes.simple.node.cpus.riscv;


import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;
// Conditional branch such as: BEQ
public class CBranchRISC extends IfNode implements MachNode{
final String _bop;
// label is obtained implicitly
CBranchRISC( IfNode iff, String bop ) {
super(iff);
_bop = bop;
}

@Override public String label() { return op(); }

@Override public void postSelect() {
Node set = in(1);
Node cmp = set.in(1);
// Bypass an expected Set and just reference the cmp directly
if( set instanceof SetRISC)
_inputs.set(1,cmp);
else
throw Utils.TODO();
}

@Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
@Override public RegMask outregmap() { return null; }

// Encoding is appended into the byte array; size is returned

@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}

@Override public void asm(CodeGen code, SB sb) {
String src = code.reg(in(1));
if( src!="FLAGS" ) sb.p(src);
}

@Override public String op() { return "b"+_bop; }

@Override public String comment() {
return "L"+cproj(1)._nid+", L"+cproj(0)._nid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.seaofnodes.simple.node.cpus.riscv;

import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.TypeFunPtr;
import java.io.ByteArrayOutputStream;

public class CallRISC extends CallNode implements MachNode{
final TypeFunPtr _tfp;
final String _name;
CallRISC( CallNode call, TypeFunPtr tfp ) {
super(call);
_inputs.pop(); // Pop constant target
assert tfp.isConstant();
_tfp = tfp;
_name = CodeGen.CODE.link(tfp)._name;
}

@Override public String label() { return op(); }
@Override public RegMask regmap(int i) {
return riscv.callInMaskInt(i); // Normal argument
}
@Override public RegMask outregmap() { return riscv.RET_MASK; }

@Override public String name() { return _name; }

// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}

@Override public void asm(CodeGen code, SB sb) {
sb.p(_name);
}

@Override public String op() { return "call"; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.seaofnodes.simple.node.cpus.riscv;

import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;

import java.io.ByteArrayOutputStream;

public class CallRRISC extends CallNode implements MachNode{
CallRRISC( CallNode call ) { super(call); }

@Override public String label() { return op(); }
@Override public RegMask regmap(int i) {
// Todo: float or int?
return i==_inputs._len
? riscv.RMASK // Function call target
: riscv.callInMaskInt(i); // Normal argument
}
@Override public RegMask outregmap() { return riscv.RET_MASK; }

// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}

@Override public void asm(CodeGen code, SB sb) {
sb.p(code.reg(fptr()));
}

@Override public String op() { return "callr"; }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.seaofnodes.simple.node.cpus.riscv;


import com.seaofnodes.simple.*;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import java.io.ByteArrayOutputStream;

// Compare immediate. Sets flags.(RFLAGS)
public class CmpIRISC extends MachConcreteNode implements MachNode{
final int _imm;
final String _bop;
CmpIRISC( BoolNode bool, TypeInteger ti ) {
super(bool);
_inputs.pop(); // Toss ideal ConstantNode away, embedded into machine op
_bop = bool.op(); // One of <,<=,==
_imm = (int)ti.value();
assert _imm == ti.value();
}
CmpIRISC( Node cmp, double ignore ) {
super(cmp);
_bop = "==";
_imm = 0;
}

@Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; }
@Override public RegMask outregmap() { return riscv.FLAGS_MASK; }

// Encoding is appended into the byte array; size is returned
@Override public int encoding(ByteArrayOutputStream bytes) {
throw Utils.TODO();
}

// General form: "cmp rs1, 1"
@Override public void asm(CodeGen code, SB sb) {
String dst = code.reg(this);
if( dst!="FLAGS" ) sb.p(dst).p(" = ");
sb.p(code.reg(in(1)));
if( _imm != 0 ) sb.p(", #").p(_imm);
}

@Override public String op() { return _imm==0 ? "test" : "cmp"; }
}
Loading

0 comments on commit 47b405d

Please sign in to comment.