Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ch20 reg #166

Open
wants to merge 60 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
dfaf215
READMEs
cliffclick Feb 7, 2025
6a66786
Update README.md
cliffclick Feb 7, 2025
c2f0ab1
Update README.md
cliffclick Feb 7, 2025
bb293aa
Move printing into print dir
cliffclick Feb 8, 2025
26ed0a0
Move codegen into codegen
cliffclick Feb 8, 2025
478600b
Framework for Reg Alloc
cliffclick Feb 8, 2025
905bdeb
First cut BuildLRG
cliffclick Feb 8, 2025
a94d0f9
Setup for first simple split
cliffclick Feb 8, 2025
7a6388b
First cut split code for mismatch regs
cliffclick Feb 9, 2025
88075ab
Live Ranges U-F
cliffclick Feb 9, 2025
5809344
FPR return
cliffclick Feb 9, 2025
1c0b1ff
Next cut regalloc
cliffclick Feb 11, 2025
47b405d
RISC-V basic isel (#156)
Hels15 Feb 11, 2025
03ed954
Bring pr/154 to ch 20
cliffclick Feb 11, 2025
4cdb14b
First cut regalloc working
cliffclick Feb 12, 2025
b1756ef
Compile some riscv codes
cliffclick Feb 12, 2025
9951333
Progress reg alloc
cliffclick Feb 13, 2025
faa8cbf
UF LRGs can be null during coloring
cliffclick Feb 13, 2025
fefeb5c
Finish UF find
cliffclick Feb 13, 2025
f0e551c
README updates
cliffclick Feb 14, 2025
8491e74
Fix ch19 mis-merge?
cliffclick Feb 14, 2025
8d303df
x86 call args, risc5 load/store regs
cliffclick Feb 14, 2025
8021e91
Push RISC WMASK
cliffclick Feb 14, 2025
371c8f7
Progress calling convention allocations
cliffclick Feb 16, 2025
c46cd15
Beef up Newton test
cliffclick Feb 16, 2025
bb4bc9a
Clean pass normal tests
cliffclick Feb 16, 2025
c175e76
Arm basics, minor changes in riscv (#160)
Hels15 Feb 16, 2025
92094eb
Update Chapter20Test.java
cliffclick Feb 16, 2025
4a9d57d
Weaken range-check test
cliffclick Feb 16, 2025
bb0b25e
Fix issue #161
cliffclick Feb 16, 2025
fb592ff
bug fixs, cloning constants
cliffclick Feb 17, 2025
d454d66
RA fixes
cliffclick Feb 18, 2025
9812d28
Allow a killMap to kill regs.
cliffclick Feb 20, 2025
5bb0016
Added flags-kill for XOR
cliffclick Feb 20, 2025
5ac79b2
Missing add-deps from fuzzer
cliffclick Feb 20, 2025
9d70a14
Update Chapter14Test.java
cliffclick Feb 20, 2025
fab3cf9
remove jukn files
cliffclick Feb 21, 2025
950dcb7
minimal "make release"
cliffclick Feb 21, 2025
b849280
Add regkills in IFG building
cliffclick Feb 22, 2025
2a7d05a
RA improvements
cliffclick Feb 23, 2025
55939ea
Push l,ast change, unbreak branch
cliffclick Feb 23, 2025
fb8ed19
Finally pass the `new [u8]` double-copy test
cliffclick Feb 23, 2025
adcc2dd
Fix several ARM reg setup problems
cliffclick Feb 23, 2025
7ea600d
Bunch of minor arm, risc fixes
cliffclick Feb 24, 2025
05ba577
Much ARM fixage
cliffclick Feb 25, 2025
80ef30a
Fixes to release
cliffclick Feb 26, 2025
2f390fb
Minor release change to allow another foreign port
cliffclick Feb 26, 2025
723f9d2
Allowing non-Simple repo ports
cliffclick Mar 1, 2025
192dcfd
Cleanup call ABI presentation
cliffclick Mar 1, 2025
331f386
Caller save regs
cliffclick Mar 2, 2025
c2c55d2
Expose caller save regs
cliffclick Mar 2, 2025
4cf2db1
More x86,risc,arm call convention fixes
cliffclick Mar 2, 2025
f12f398
Callee-save registers
cliffclick Mar 3, 2025
105f8f4
RegMask supports 128 bits
cliffclick Mar 3, 2025
8c816a6
Fix risc5 compares
cliffclick Mar 3, 2025
800483b
yank MulIRISC
cliffclick Mar 3, 2025
3d76e3f
fix arg flags reg
cliffclick Mar 3, 2025
30ccbab
Update Chapter20Test.java
cliffclick Mar 3, 2025
0fe8764
Turn off transpile tests
cliffclick Mar 3, 2025
edce88c
A lot more readme
cliffclick Mar 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Java stuff
# Compiled class file
*.class
build/

# Log file
*.log
Expand Down
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,38 @@ the backend starts with levering Java: the Evaluator (first appears in Chapter

The following is a rough plan, subject to change.

Each chapter will be self-sufficient and complete; in the sense that each chapter will fully implement
a subset of the Simple language, and include everything that was created in the previous chapter.
Each chapter will also include a detailed commentary on relevant aspects of the
Sea Of Nodes intermediate representation.
Each chapter will be self-sufficient and complete; in the sense that each
chapter will fully implement a subset of the Simple language, and include
everything that was created in the previous chapter. Each chapter will also
include a detailed commentary on relevant aspects of the Sea Of Nodes
intermediate representation.

The Simple language will be styled after a subset of C or Java
The Simple language is styled after a subset of C or Java.

* [Chapter 1](chapter01/README.md): Script that returns an integer literal, i.e., an empty function that takes no arguments and returns a single integer value. The `return` statement.
* [Chapter 2](chapter02/README.md): Simple binary arithmetic such as addition, subtraction, multiplication, division
with constants. Peephole optimization / simple constant folding.
* [Chapter 3](chapter03/README.md): Local variables, and assignment statements. Read on RHS, SSA, more peephole optimization if local is a
constant.
* [Chapter 4](chapter04/README.md): A non-constant external variable input named `arg`. Binary and Comparison operators involving constants and `arg`. Non-zero values will be truthy. Peephole optimizations involving algebraic simplifications.
* [Chapter 4](chapter04/README.md): A non-constant external variable input
named `arg`. Binary and Comparison operators involving constants and `arg`.
Non-zero values will be truthy. Peephole optimizations involving algebraic
simplifications.
* [Chapter 5](chapter05/README.md): `if` statement. CFG construction.
* [Chapter 6](chapter06/README.md): Peephole optimization around dead control flow.
* [Chapter 7](chapter07/README.md): `while` statement. Looping construct - eager phi approach.
* [Chapter 8](chapter08/README.md): Looping construct continued, lazy phi creation, `break` and `continue` statements.
* [Chapter 7](chapter07/README.md): `while` statement; looping constructs - eager phi approach.
* [Chapter 8](chapter08/README.md): Looping constructs continued, lazy phi creation, `break` and `continue` statements.
* [Chapter 9](chapter09/README.md): Global Value Numbering. Iterative peepholes to fixpoint. Worklists.
* [Chapter 10](chapter10/README.md): User defined Struct types. Memory effects: general memory edges in SSA. Equivalence class aliasing. Null pointer analysis. Peephole optimization around load-after-store/store-after-store.
* [Chapter 10](chapter10/README.md): User defined Struct types. Memory effects:
general memory edges in SSA. Equivalence class aliasing. Null pointer
analysis. Peephole optimization around load-after-store/store-after-store.
* [Chapter 11](chapter11/README.md): Global Code Motion - Scheduling.
* [Chapter 12](chapter12/README.md): Float type.
* [Chapter 13](chapter13/README.md): Nested references in Structs.
* [Chapter 14](chapter14/README.md): Narrow primitive types (e.g. bytes)
* [Chapter 15](chapter15/README.md): One dimensional static length array type, with array loads and stores.
* [Chapter 16](chapter16/README.md): Constructors
* [Chapter 17](chapter17/README.md): Mutability & Syntax Sugar: `var`, `val`, `x+=y`, `for(init;test;next)body`
* [Chapter 17](chapter17/README.md): Mutability & Syntax Sugar: `var`, `val`, `x+=y`, `for(init; test; next) body`
* [Chapter 18](chapter18/README.md): Functions and calls.
* [Chapter 19](chapter19/README.md): Instruction selection and portable compilation
* [Chapter 20](chapter20/README.md): Graph Coloring Register Allocation
15 changes: 15 additions & 0 deletions chapter02/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ We extend the set of nodes by adding following additional node types.
| Div | Data | Divide a value by another | Two data nodes, values are divided, order matters | Result of the division |
| Minus | Data | Negate a value | One data node, value is negated | Result of the unary minus |


## *Value* equality vs *Reference* equality

In much of Simple, Nodes are looked up (`find()` calls) via *reference*
equality. This is by far the most common case. In [Chapter
9](../chapter09/README.md) we introduce *value* equality for the first time.
In both cases the choice of value-vs-reference equality is intentional: it is
*never* correct to "just pick one or the other kind of equality". When in
doubt check the context: only *Global Value Numbering* uses value equality;
everywhere we mean reference equality.


## Peephole Optimizations

Nodes in the graph can be peephole-optimized. The graph is viewed through a
Expand Down Expand Up @@ -141,6 +153,7 @@ starting in [Chapter 4](../chapter04/README.md) and [Chapter
There are other important properties of the Lattice that we discuss in [Chapter
4](../chapter04/README.md) and [Chapter 10](../chapter10/README.md), such as the "meet" and "join" operators and their rules.


## Nodes Pre Peephole Optimization

The following visual shows how the graph looks like pre-peephole optimization:
Expand All @@ -152,10 +165,12 @@ The following visual shows how the graph looks like pre-peephole optimization:
* The edges from Constants to Start are shown in dotted lines as these are not true control edges
* We label each edge with its position in the node's list of inputs.


## Post-peephole

![Example Visual](./docs/02-post-peephole-ex1.svg)


## Demonstration
To demonstrate how the optimisation works, let's consider the following code:
```
Expand Down
8 changes: 8 additions & 0 deletions chapter09/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ get the same result - and can be shared.
constant 17 is not the same as 99, but both are the same `ConstantNode` class
and label. The `eq` and `hash` calls check (or hash) these extra bits.

Note the intentional *value* equality here. In many places in Simple we use
*reference* equality - e.g. node/edge maintenance is via reference equality, not
value equality, so e.g. the `Utils.find()` call finds via reference. Through
out the Simple project whenever a Node lookup is happening, beware if its via
*value* or *reference* equality; this is not always explicitly called out but
should be obvious from context.


Since we edit the graph a lot, Nodes can have their inputs change which changes
their hash which means they can't be found in the GVN table. E.g. we swap out
the `Add` for a `Con` in:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,9 @@ static boolean spine_cmp( Node hi, Node lo, Node dep ) {

@Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
@Override Node copyF() { return new AddFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,9 @@ public Node idealize() {
}
@Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); }
@Override Node copyF() { return new MulFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
13 changes: 13 additions & 0 deletions chapter13/src/test/java/com/seaofnodes/simple/Chapter13Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ public void testNullRef3() {
catch( Exception e ) { assertEquals("Cannot store 3.14 into field IntBot i",e.getMessage()); }
}

@Test
public void testNullRef4() {
Parser parser = new Parser("-null-5/null-5");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); }
}

@Test public void testNullRef5() {
Parser parser = new Parser("return null+42;");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}

@Test
public void testEmpty() {
Parser parser = new Parser(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,9 @@ static boolean spine_cmp( Node hi, Node lo, Node dep ) {

@Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
@Override Node copyF() { return new AddFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,9 @@ public Node idealize() {
}
@Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); }
@Override Node copyF() { return new MulFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
13 changes: 13 additions & 0 deletions chapter14/src/test/java/com/seaofnodes/simple/Chapter13Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ public void testNullRef3() {
catch( Exception e ) { assertEquals("Cannot store 3.14 into field int i",e.getMessage()); }
}

@Test
public void testNullRef4() {
Parser parser = new Parser("-null-5/null-5");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); }
}

@Test public void testNullRef5() {
Parser parser = new Parser("return null+42;");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}

@Test
public void testEmpty() {
Parser parser = new Parser(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,9 @@ static boolean spine_cmp( Node hi, Node lo, Node dep ) {

@Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
@Override Node copyF() { return new AddFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,9 @@ public Node idealize() {
}
@Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); }
@Override Node copyF() { return new MulFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
13 changes: 13 additions & 0 deletions chapter15/src/test/java/com/seaofnodes/simple/Chapter13Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ public void testNullRef3() {
catch( Exception e ) { assertEquals("Cannot store 3.14 into field int i",e.getMessage()); }
}

@Test
public void testNullRef4() {
Parser parser = new Parser("-null-5/null-5");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); }
}

@Test public void testNullRef5() {
Parser parser = new Parser("return null+42;");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}

@Test
public void testEmpty() {
Parser parser = new Parser(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,9 @@ static boolean spine_cmp( Node hi, Node lo, Node dep ) {

@Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
@Override Node copyF() { return new AddFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,9 @@ public Node idealize() {
}
@Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); }
@Override Node copyF() { return new MulFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
13 changes: 13 additions & 0 deletions chapter16/src/test/java/com/seaofnodes/simple/Chapter13Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,19 @@ public void testNullRef3() {
catch( Exception e ) { assertEquals("Cannot store 3.14 into field int i",e.getMessage()); }
}

@Test
public void testNullRef4() {
Parser parser = new Parser("-null-5/null-5");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); }
}

@Test public void testNullRef5() {
Parser parser = new Parser("return null+42;");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}

@Test
public void testEmpty() {
Parser parser = new Parser(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ static boolean overflow( long x, long y ) {
public Node idealize () {
Node lhs = in(1);
Node rhs = in(2);
if( rhs.err() != null ) return null;
Type t2 = rhs._type;

// Add of 0. We do not check for (0+x) because this will already
Expand Down Expand Up @@ -184,4 +185,9 @@ static boolean spine_cmp( Node hi, Node lo, Node dep ) {

@Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
@Override Node copyF() { return new AddFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,9 @@ public Node idealize() {
}
@Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); }
@Override Node copyF() { return new MulFNode(null,null); }
@Override public String err() {
if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
return null;
}
}
13 changes: 13 additions & 0 deletions chapter17/src/test/java/com/seaofnodes/simple/Chapter13Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,19 @@ public void testNullRef3() {
catch( Exception e ) { assertEquals("Type 3.14 is not of declared type int",e.getMessage()); }
}

@Test
public void testNullRef4() {
Parser parser = new Parser("-null-5/null-5;");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}

@Test public void testNullRef5() {
Parser parser = new Parser("return null+42;");
try { parser.parse().iterate(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}

@Test
public void testEmpty() {
Parser parser = new Parser(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ public class Chapter17Test {

@Test
public void testJig() {
Parser parser = new Parser("""
int i = 0;
i=i=1;
return i;
//return 3.14;
""");
Parser parser = new Parser("return 1;");
StopNode stop = parser.parse().iterate();
assertEquals("return 1;", stop.toString());
assertEquals(1L, Evaluator.evaluate(stop, 0));
Expand Down
2 changes: 1 addition & 1 deletion chapter18/src/main/java/com/seaofnodes/simple/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,6 @@ public static class ParseException extends RuntimeException {
public final Lexer _loc;
// file:line:charoff err
//String msg = "src:"+_line_number+":"+(_position-_line_start)+" "+errorMessage;
ParseException( String msg, Lexer loc ) { super(msg); _loc = loc; assert loc!=null; }
ParseException( String msg, Lexer loc ) { super(msg); _loc = loc; }
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.seaofnodes.simple.node;

import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.*;

import java.util.BitSet;

public class AddNode extends Node {
Expand Down Expand Up @@ -44,6 +44,7 @@ static boolean overflow( long x, long y ) {
public Node idealize () {
Node lhs = in(1);
Node rhs = in(2);
if( rhs.err()!=null ) return null;
Type t2 = rhs._type;

// Add of 0. We do not check for (0+x) because this will already
Expand Down Expand Up @@ -184,4 +185,10 @@ static boolean spine_cmp( Node hi, Node lo, Node dep ) {

@Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
@Override Node copyF() { return new AddFNode(null,null); }
@Override public Parser.ParseException err() {
if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type,null);
if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type,null);
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.seaofnodes.simple.node;

import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;

import java.util.BitSet;

public class MulNode extends Node {
Expand Down Expand Up @@ -60,4 +60,10 @@ public Node idealize() {
}
@Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); }
@Override Node copyF() { return new MulFNode(null,null); }
@Override public Parser.ParseException err() {
if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null;
if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(1)._type,null);
if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+label()+"' " + in(2)._type,null);
return null;
}
}
Loading