summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter14/README.md17
-rw-r--r--chapter14/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter14/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter14/src/test/java/com/seaofnodes/simple/Chapter12Test.java3
-rw-r--r--chapter14/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java24
-rw-r--r--chapter14/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/Chapter12Test.java3
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter16/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter16/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter16/src/test/java/com/seaofnodes/simple/Chapter12Test.java3
-rw-r--r--chapter16/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter16/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter17a/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter17a/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter17a/src/test/java/com/seaofnodes/simple/Chapter12Test.java3
-rw-r--r--chapter17a/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter17a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter17b/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter17b/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter17b/src/test/java/com/seaofnodes/simple/Chapter12Test.java3
-rw-r--r--chapter17b/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter17b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java4
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java4
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java5
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Eval2.java2
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java27
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java1
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java27
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java1
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java4
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java4
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Eval2.java2
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java28
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java1
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java28
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java1
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java30
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java1
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java4
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java4
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Eval2.java2
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java21
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java1
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java19
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java1
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java22
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java1
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java4
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java4
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Eval2.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/Parser.java4
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java21
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java1
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java19
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java1
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java22
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java1
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java4
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java4
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Eval2.java2
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter23/README.md8
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/Parser.java4
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java21
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java1
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java19
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java1
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java22
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java1
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java4
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java4
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Eval2.java2
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/Parser.java4
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java23
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java21
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java1
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java19
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java1
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java22
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java1
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java4
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java4
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Eval2.java2
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/Node.java3
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java24
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java21
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java1
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java19
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java1
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java22
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java19
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java21
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Eval2.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/floatToInteger.c25
-rw-r--r--docs/chapter-backports.md33
-rw-r--r--isa/src/main/java/com/seaofnodes/isa/Arm64.java6
-rw-r--r--isa/src/main/java/com/seaofnodes/isa/RiscV.java10
-rw-r--r--isa/src/main/java/com/seaofnodes/isa/X86.java16
-rw-r--r--isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java9
-rw-r--r--isa/src/test-support/java/com/seaofnodes/isa/eval/EvalRisc5.java20
-rw-r--r--isa/src/test/java/com/seaofnodes/isa/RiscTest.java22
-rw-r--r--isa/src/test/java/com/seaofnodes/isa/X86Test.java4
135 files changed, 1299 insertions, 77 deletions
diff --git a/chapter14/README.md b/chapter14/README.md
index 81862508..60e82403 100644
--- a/chapter14/README.md
+++ b/chapter14/README.md
@@ -34,10 +34,16 @@ f = f+x; // Auto-widen x to 2.0, result is 2.5
f = f+true; // Since bools are just synonyms for integers 0,1 they auto-widen also
```
-There is no corresponding way right now to round a `flt` back to an `int`:
+Assigning a float to an integer truncates toward zero:
```java
-int x = 3.14; // error
+int rounded = 17.3/2.5; // 6
+rounded = -17.3/2.5; // -6
```
+This applies to variable initialization, reassignment, and stores to integer
+fields or array elements. Conversion first produces a signed 64-bit integer:
+NaN becomes zero, and values outside the range (including infinities) clamp to
+the nearest limit. Narrow integer destinations then apply the same bit
+truncation and sign/zero extension as integer-to-integer assignments.
Here is Newton's method for computing a square root:
```java
@@ -60,14 +66,17 @@ Floating point operations have their own Nodes:
| AddF, SubF, MulF, DivF | Basic binary FP ops |
| MinusF | Unary negate |
| EQF, LTF, LEF | FP compare operators |
+| ToFloat, ToInteger | Numeric conversions |
+| RoundF32 | Round to f32 precision |
The parser uses [`Node.widen()`](src/main/java/com/seaofnodes/simple/node/Node.java)
to choose the floating-point operation and insert `ToFloatNode` conversions.
The float lattice has constants between `TOP` and `BOT`, like the integer
lattice before ranges. [`TypeFloat`](src/main/java/com/seaofnodes/simple/type/TypeFloat.java)
also records whether a value fits `f32`; assigning to `f32` rounds it to that
-precision. Conversion from float back to integer remains a separate, pending
-language feature.
+precision. `ToIntegerNode` performs the inverse numeric conversion on assignment
+to an integer destination, folding constants with the same rules as runtime
+conversion.
## Narrow or sub-word types
diff --git a/chapter14/src/main/java/com/seaofnodes/simple/Parser.java b/chapter14/src/main/java/com/seaofnodes/simple/Parser.java
index 4ab4d9ac..0602d290 100644
--- a/chapter14/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter14/src/main/java/com/seaofnodes/simple/Parser.java
@@ -733,6 +733,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter14/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter14/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter14/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter14/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter14/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index e84da36a..9e7240d8 100644
--- a/chapter14/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter14/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -130,8 +130,7 @@ N n = new N;
n.i = 3.14;
return n.i;
""");
- try { parser.parse().iterate(); fail(); }
- catch( Exception e ) { assertEquals("Cannot store 3.14 into field int i",e.getMessage()); }
+ assertEquals("3",com.seaofnodes.simple.evaluator.Evaluator.evaluate(parser.parse().iterate(),0).toString());
}
@Test
diff --git a/chapter14/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter14/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index f2b51da2..5438fb2b 100644
--- a/chapter14/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter14/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -8,6 +8,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var stop = new Parser(test[0]).parse().iterate();
+ assertEquals(test[0],test[1],Evaluator.evaluate(stop,17).toString());
+ assertEquals(test[0],test[2],Evaluator.evaluate(stop,-17).toString());
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var stop = new Parser("flt x = arg; return 0-x;").parse().iterate();
// Compare strings so +0.0 and -0.0 remain distinct.
@@ -81,7 +100,7 @@ return x+1==x;
assertEquals(0L, Evaluator.evaluate(stop, 1));
}
@Test
- public void testReferenceFieldRejectsFloat() {
+ public void testReferenceFieldConvertsFloat() {
Parser parser = new Parser(
"""
struct N { N next; int i; }
@@ -89,7 +108,6 @@ N n = new N;
n.i = 3.14;
return n.i;
""");
- try { parser.parse().iterate(); fail(); }
- catch( Exception e ) { assertEquals("Cannot store 3.14 into field int i",e.getMessage()); }
+ assertEquals("3",com.seaofnodes.simple.evaluator.Evaluator.evaluate(parser.parse().iterate(),0).toString());
}
}
diff --git a/chapter14/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter14/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index afe4cfe3..afa35372 100644
--- a/chapter14/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter14/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -157,6 +157,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
index a0c15ae3..81291326 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
@@ -834,6 +834,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter15/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter15/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter15/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index e885deb3..813dd757 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -126,8 +126,7 @@ N n = new N;
n.i = 3.14;
return n.i;
""");
- try { parser.parse().iterate(); fail(); }
- catch( Exception e ) { assertEquals("Cannot store 3.14 into field int i",e.getMessage()); }
+ assertEquals("3",com.seaofnodes.simple.evaluator.Evaluator.evaluate(parser.parse().iterate(),0).toString());
}
@Test
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter15/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index f4323fe0..fd71ad5f 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -8,6 +8,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var stop = new Parser(test[0]).parse().iterate();
+ assertEquals(test[0],test[1],Evaluator.evaluate(stop,17).toString());
+ assertEquals(test[0],test[2],Evaluator.evaluate(stop,-17).toString());
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var stop = new Parser("flt x = arg; return 0-x;").parse().iterate();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 9eed23de..485f73c5 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -305,6 +305,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter16/src/main/java/com/seaofnodes/simple/Parser.java b/chapter16/src/main/java/com/seaofnodes/simple/Parser.java
index deb359ce..41a3fcaa 100644
--- a/chapter16/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter16/src/main/java/com/seaofnodes/simple/Parser.java
@@ -918,6 +918,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter16/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter16/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter16/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter16/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter16/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 22976058..8d1d93c6 100644
--- a/chapter16/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter16/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -125,8 +125,7 @@ N n = new N;
n.i = 3.14;
return n.i;
""");
- try { parser.parse().iterate(); fail(); }
- catch( Exception e ) { assertEquals("Cannot store 3.14 into field int i",e.getMessage()); }
+ assertEquals("3",com.seaofnodes.simple.evaluator.Evaluator.evaluate(parser.parse().iterate(),0).toString());
}
@Test
diff --git a/chapter16/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter16/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index f4323fe0..fd71ad5f 100644
--- a/chapter16/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter16/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -8,6 +8,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var stop = new Parser(test[0]).parse().iterate();
+ assertEquals(test[0],test[1],Evaluator.evaluate(stop,17).toString());
+ assertEquals(test[0],test[2],Evaluator.evaluate(stop,-17).toString());
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var stop = new Parser("flt x = arg; return 0-x;").parse().iterate();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter16/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter16/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 3eb406eb..8b530307 100644
--- a/chapter16/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter16/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -297,6 +297,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter17a/src/main/java/com/seaofnodes/simple/Parser.java b/chapter17a/src/main/java/com/seaofnodes/simple/Parser.java
index f5c371cc..e2df494e 100644
--- a/chapter17a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter17a/src/main/java/com/seaofnodes/simple/Parser.java
@@ -1009,6 +1009,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter17a/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter17a/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter17a/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter17a/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter17a/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 7e3b6843..a12e4218 100644
--- a/chapter17a/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter17a/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -125,8 +125,7 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- try { parser.parse().iterate(); fail(); }
- catch( Exception e ) { assertEquals("Type 3.14 is not of declared type int",e.getMessage()); }
+ assertEquals("3",com.seaofnodes.simple.evaluator.Evaluator.evaluate(parser.parse().iterate(),0).toString());
}
@Test
diff --git a/chapter17a/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter17a/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index bff36f3a..ce674b44 100644
--- a/chapter17a/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter17a/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -8,6 +8,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var stop = new Parser(test[0]).parse().iterate();
+ assertEquals(test[0],test[1],Evaluator.evaluate(stop,17).toString());
+ assertEquals(test[0],test[2],Evaluator.evaluate(stop,-17).toString());
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var stop = new Parser("flt x = arg; return 0-x;").parse().iterate();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter17a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter17a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 30af9895..0721402f 100644
--- a/chapter17a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter17a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -297,6 +297,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter17b/src/main/java/com/seaofnodes/simple/Parser.java b/chapter17b/src/main/java/com/seaofnodes/simple/Parser.java
index 2d794aa5..c02f0305 100644
--- a/chapter17b/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter17b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -1156,6 +1156,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter17b/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter17b/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter17b/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter17b/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter17b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 7e3b6843..a12e4218 100644
--- a/chapter17b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter17b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -125,8 +125,7 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- try { parser.parse().iterate(); fail(); }
- catch( Exception e ) { assertEquals("Type 3.14 is not of declared type int",e.getMessage()); }
+ assertEquals("3",com.seaofnodes.simple.evaluator.Evaluator.evaluate(parser.parse().iterate(),0).toString());
}
@Test
diff --git a/chapter17b/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter17b/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index bff36f3a..ce674b44 100644
--- a/chapter17b/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter17b/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -8,6 +8,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var stop = new Parser(test[0]).parse().iterate();
+ assertEquals(test[0],test[1],Evaluator.evaluate(stop,17).toString());
+ assertEquals(test[0],test[2],Evaluator.evaluate(stop,-17).toString());
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var stop = new Parser("flt x = arg; return 0-x;").parse().iterate();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter17b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter17b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 30af9895..0721402f 100644
--- a/chapter17b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter17b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -297,6 +297,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
index 3e80aa60..82ad8d04 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
@@ -1350,6 +1350,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index fc5b37d9..130ac8ea 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -123,8 +123,8 @@ struct N { !N? !next; int i; };
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()); }
+ code.parse().opto();
+ assertEquals("3",Eval2.eval(code,0));
}
@Test
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 7b065411..7eed49fb 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -8,6 +8,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var code = new CodeGen(test[0]).parse().opto().typeCheck();
+ assertEquals(test[0],test[1],Eval2.eval(code,17));
+ assertEquals(test[0],test[2],Eval2.eval(code,-17));
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index e64d5b27..93b64747 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -124,8 +124,8 @@ return s.x;
@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()); }
+ code.parse().opto();
+ assertEquals("2",Eval2.eval(code,0));
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 0758fbf5..22eb91b9 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -14,11 +14,8 @@ public class Chapter18Test {
CodeGen zero = new CodeGen("var a = new "+type+"[arg]; return a[arg-1];")
.parse().opto().typeCheck();
assertEquals("0.0",Eval2.eval(zero,3));
- // Eval2 does not execute RoundF32 yet; check nonconstant narrowing here.
new CodeGen(type+" x = arg; var a = new "+type+"[arg]; a[0] = x; return a[0];")
.parse().opto().typeCheck();
- }
- for( String type : new String[]{"flt","f64"} ) {
CodeGen loop = new CodeGen("var a = new "+type+"[arg]; "+
"for(int i=0; i<arg; i++) a[i] += i+0.25; return a[0]+a[arg-1];")
.parse().opto().typeCheck();
@@ -28,7 +25,7 @@ public class Chapter18Test {
CodeGen rounded = new CodeGen("var a = new "+type+"[arg]; "+
"for(int i=0; i<arg; i++) a[i] = 16777216.0+i; return a[arg-1];")
.parse().opto().typeCheck();
- assertEquals("1.6777217E7",Eval2.eval(rounded,2));
+ assertEquals(type.equals("f32") ? "1.6777216E7" : "1.6777217E7",Eval2.eval(rounded,2));
}
CodeGen rounded = new CodeGen("var a = new f32[arg]; "+
"for(int i=0; i<arg; i++) a[i] = 16777217.0; return a[arg-1];")
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java
index c517ce6e..3f337e86 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -279,6 +279,8 @@ public abstract class Eval2 {
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 RoundF32Node rnd -> (double)(float)d(rnd.in(1));
+ case ToIntegerNode cvt -> (long)d(cvt.in(1));
case ToFloatNode toflt-> (double)x(toflt.in(1));
case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2));
default -> throw Utils.TODO();
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter18/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 1e2a08a7..7dbc551e 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -296,6 +296,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/Parser.java b/chapter19/src/main/java/com/seaofnodes/simple/Parser.java
index dde2d09b..00aa117e 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/Parser.java
@@ -1340,6 +1340,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter19/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter19/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
new file mode 100644
index 00000000..261a1dcd
--- /dev/null
+++ b/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -0,0 +1,27 @@
+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.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class F8ToIRISC extends MachConcreteNode implements MachNode {
+ F8ToIRISC(Node f8toi) {super(f8toi);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.RMASK; }
+
+ // 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(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "f8toi"; }
+}
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index 81b31c6a..ad6e1c0f 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -202,6 +202,7 @@ public class riscv extends Machine{
case SubFNode subf -> new SubFRISC(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIRISC(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
new file mode 100644
index 00000000..33a77d4c
--- /dev/null
+++ b/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -0,0 +1,27 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class F8ToIX86 extends MachConcreteNode implements MachNode {
+ F8ToIX86(Node f8toi ) { super(f8toi); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // 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(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "f8toi"; }
+}
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index 523d0e34..e7e0b874 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -205,6 +205,7 @@ public class x86_64_v2 extends Machine {
case SubFNode subf -> new SubFX86(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIX86(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 199d185d..c2e07b22 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -123,8 +123,8 @@ struct N { !N? !next; int i; };
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()); }
+ code.parse().opto();
+ assertEquals("3",Eval2.eval(code,0));
}
@Test
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 7b065411..7eed49fb 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -8,6 +8,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var code = new CodeGen(test[0]).parse().opto().typeCheck();
+ assertEquals(test[0],test[1],Eval2.eval(code,17));
+ assertEquals(test[0],test[2],Eval2.eval(code,-17));
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index 3de0a901..bd957d6a 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -124,8 +124,8 @@ return s.x;
@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()); }
+ code.parse().opto();
+ assertEquals("2",Eval2.eval(code,0));
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter19/src/test/java/com/seaofnodes/simple/Eval2.java
index 4837fcda..46251cef 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -279,6 +279,8 @@ public abstract class Eval2 {
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 RoundF32Node rnd -> (double)(float)d(rnd.in(1));
+ case ToIntegerNode cvt -> (long)d(cvt.in(1));
case ToFloatNode toflt-> (double)x(toflt.in(1));
case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2));
default -> throw Utils.TODO();
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter19/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 3f023bb2..cb98971b 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -299,6 +299,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/Parser.java b/chapter20/src/main/java/com/seaofnodes/simple/Parser.java
index 2a228bc8..697e84ce 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/Parser.java
@@ -1341,6 +1341,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter20/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
new file mode 100644
index 00000000..937c437b
--- /dev/null
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
@@ -0,0 +1,28 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+// VCVT (between floating-point and integer)
+public class F8ToIARM extends MachConcreteNode implements MachNode {
+ F8ToIARM(Node f8toi) {super(f8toi);}
+ @Override public String op() { return "f8toi"; }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return arm.DMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return arm.WMASK; }
+
+ // 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(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index 009af342..72254310 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -219,6 +219,7 @@ public class arm extends Machine {
case SubFNode subf -> new SubFARM(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIARM(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
new file mode 100644
index 00000000..f4de5d48
--- /dev/null
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -0,0 +1,28 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class F8ToIRISC extends MachConcreteNode implements MachNode {
+ F8ToIRISC(Node f8toi) {super(f8toi);}
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+
+ // 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(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "f8toi"; }
+}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index daf33f29..0542085c 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -221,6 +221,7 @@ public class riscv extends Machine {
case SubFNode subf -> new SubFRISC(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIRISC(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
new file mode 100644
index 00000000..49d666b7
--- /dev/null
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegMask;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.ByteArrayOutputStream;
+
+public class F8ToIX86 extends MachConcreteNode implements MachNode {
+ F8ToIX86(Node f8toi ) { super(f8toi); }
+
+ // Register mask allowed on input i.
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
+ // Register mask allowed as a result. 0 for no register.
+ @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ // 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(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+ @Override public String op() { return "f8toi"; }
+}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index 57771d6a..c66aed9f 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -250,6 +250,7 @@ public class x86_64_v2 extends Machine {
case SubFNode subf -> new SubFX86(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIX86(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 36a1a0aa..6b7b5d85 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -124,8 +124,8 @@ struct N { !N? !next; int i; };
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()); }
+ code.parse().opto();
+ assertEquals("3",Eval2.eval(code,0));
}
@Test
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 009beb0b..23d0ef37 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -7,6 +7,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var code = new CodeGen(test[0]).parse().opto().typeCheck();
+ assertEquals(test[0],test[1],Eval2.eval(code,17));
+ assertEquals(test[0],test[2],Eval2.eval(code,-17));
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index b799ee82..ff35f904 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -123,8 +123,8 @@ return s.x;
@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()); }
+ code.parse().opto();
+ assertEquals("2",Eval2.eval(code,0));
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter20/src/test/java/com/seaofnodes/simple/Eval2.java
index b55607df..3312ec6b 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -281,6 +281,8 @@ public abstract class Eval2 {
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 RoundF32Node rnd -> (double)(float)d(rnd.in(1));
+ case ToIntegerNode cvt -> (long)d(cvt.in(1));
case ToFloatNode toflt-> (double)x(toflt.in(1));
case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2));
default -> throw Utils.TODO();
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter20/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 3f023bb2..cb98971b 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -299,6 +299,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/Parser.java b/chapter21/src/main/java/com/seaofnodes/simple/Parser.java
index 10e2c5c1..6f307785 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/Parser.java
@@ -1359,6 +1359,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
new file mode 100644
index 00000000..098c1353
--- /dev/null
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+
+public class F8ToIARM extends MachConcreteNode implements MachNode {
+ F8ToIARM(Node f8toi) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ enc.add4(Arm64.floatToInteger(enc.reg(in(1))-arm.D_OFFSET,enc.reg(this)));
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+}
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 af0461a6..4006565e 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
@@ -314,6 +314,7 @@ public class arm extends Machine {
case SubFNode subf -> new SubFARM(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn-> new I2F8ARM(tfn);
+ case ToIntegerNode cvt -> new F8ToIARM(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
new file mode 100644
index 00000000..b2d4efea
--- /dev/null
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -0,0 +1,19 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+
+public class F8ToIRISC extends MachConcreteNode implements MachNode {
+ F8ToIRISC(Node f8toi) {super(f8toi);}
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ RiscV.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-riscv.F_OFFSET);
+ }
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index a9a6d84b..7e840509 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -239,6 +239,7 @@ public class riscv extends Machine {
case SubFNode subf -> new SubFRISC(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIRISC(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
new file mode 100644
index 00000000..44bcbe7b
--- /dev/null
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -0,0 +1,22 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+
+public class F8ToIX86 extends MachConcreteNode implements MachNode {
+ F8ToIX86(Node f8toi ) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
+ @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ @Override public void encoding( Encoding enc ) {
+ X86.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-x86_64_v2.XMM_OFFSET);
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index 959ea440..1b7cf775 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -284,6 +284,7 @@ public class x86_64_v2 extends Machine {
case SubFNode subf -> new SubFX86(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIX86(cvt);
case XCtrlNode x -> new ConstantNode(Type.XCONTROL);
case XorNode xor -> xor(xor);
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index f2078d03..ec7b3cb3 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -124,8 +124,8 @@ struct N { !N? !next; int i; };
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()); }
+ code.parse().opto();
+ assertEquals("3",Eval2.eval(code,0));
}
@Test
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 009beb0b..23d0ef37 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -7,6 +7,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var code = new CodeGen(test[0]).parse().opto().typeCheck();
+ assertEquals(test[0],test[1],Eval2.eval(code,17));
+ assertEquals(test[0],test[2],Eval2.eval(code,-17));
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index eeb94d50..e7f5849d 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -123,8 +123,8 @@ return s.x;
@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()); }
+ code.parse().opto();
+ assertEquals("2",Eval2.eval(code,0));
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java
index dabd63cc..a855350e 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -283,6 +283,8 @@ public abstract class Eval2 {
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 RoundF32Node rnd -> (double)(float)d(rnd.in(1));
+ case ToIntegerNode cvt -> (long)d(cvt.in(1));
case ToFloatNode toflt-> (double)x(toflt.in(1));
case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2));
default -> throw Utils.TODO();
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 3f023bb2..cb98971b 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -299,6 +299,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java
index 6ceccf0a..ea618757 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java
@@ -665,6 +665,8 @@ public class Parser {
// Auto-narrow wide ints to narrow ints. For loads, emit code to force
// the loaded value to match the declared sign/zero bits. For stores,
// just force the type, acting "as if" the store silently truncates.
+ if( expr._type instanceof TypeFloat && t instanceof TypeInteger )
+ expr = peep(new ToIntegerNode(expr));
Type et = expr._type;
if( isLoad ) { expr = zsMask(expr,t); et = expr._type; }
else if( et instanceof TypeInteger && t instanceof TypeInteger ) et=t;
@@ -1438,6 +1440,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
new file mode 100644
index 00000000..098c1353
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+
+public class F8ToIARM extends MachConcreteNode implements MachNode {
+ F8ToIARM(Node f8toi) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ enc.add4(Arm64.floatToInteger(enc.reg(in(1))-arm.D_OFFSET,enc.reg(this)));
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index 15e2f0fe..da5e6d4b 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -316,6 +316,7 @@ public class arm extends Machine {
case SubFNode subf -> new SubFARM(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> new I2F8ARM(tfn);
+ case ToIntegerNode cvt -> new F8ToIARM(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
new file mode 100644
index 00000000..b2d4efea
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -0,0 +1,19 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+
+public class F8ToIRISC extends MachConcreteNode implements MachNode {
+ F8ToIRISC(Node f8toi) {super(f8toi);}
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ RiscV.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-riscv.F_OFFSET);
+ }
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index a9327c50..a1e3b767 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -240,6 +240,7 @@ public class riscv extends Machine {
case SubFNode subf -> new SubFRISC(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIRISC(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
new file mode 100644
index 00000000..44bcbe7b
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -0,0 +1,22 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.*;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+
+public class F8ToIX86 extends MachConcreteNode implements MachNode {
+ F8ToIX86(Node f8toi ) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
+ @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ @Override public void encoding( Encoding enc ) {
+ X86.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-x86_64_v2.XMM_OFFSET);
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index 5c0d4599..7ddd17d4 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -287,6 +287,7 @@ public class x86_64_v2 extends Machine {
case SubFNode subf -> new SubFX86(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIX86(cvt);
case XCtrlNode x -> new ConstantNode(Type.XCONTROL);
case XorNode xor -> xor(xor);
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index ad0b6088..9b41d77f 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -124,8 +124,8 @@ struct N { !N? !next; int i; };
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()); }
+ code.parse().opto();
+ assertEquals("3",Eval2.eval(code,0));
}
@Test
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 009beb0b..23d0ef37 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -7,6 +7,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var code = new CodeGen(test[0]).parse().opto().typeCheck();
+ assertEquals(test[0],test[1],Eval2.eval(code,17));
+ assertEquals(test[0],test[2],Eval2.eval(code,-17));
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index eeb94d50..e7f5849d 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -123,8 +123,8 @@ return s.x;
@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()); }
+ code.parse().opto();
+ assertEquals("2",Eval2.eval(code,0));
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter22/src/test/java/com/seaofnodes/simple/Eval2.java
index cbe7bde0..bbf88f1a 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -281,6 +281,8 @@ public abstract class Eval2 {
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 RoundF32Node rnd -> (double)(float)d(rnd.in(1));
+ case ToIntegerNode cvt -> (long)d(cvt.in(1));
case ToFloatNode toflt-> (double)x(toflt.in(1));
case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2));
default -> throw Utils.TODO();
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter22/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 3f023bb2..cb98971b 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -299,6 +299,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter23/README.md b/chapter23/README.md
index fd330cdd..59ce0dc5 100644
--- a/chapter23/README.md
+++ b/chapter23/README.md
@@ -48,12 +48,6 @@ Also in this chapter we revisit our Types and make some major changes:
etc; only the implementation details change.
-The lazy memory model from Chapter 11 continues through the new type system.
-BulkMemPhi/MemPhi discover aliases during optimization. Each New covers only
-instance fields; class-wide (`_one`) fields are excluded from its partial memory.
-Field offsets stay symbolic until layout, and Loads retain cyclic field types
-and deep-final information while querying the contents of a precise alias.
-
You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter23) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter22...linear-chapter23) it to the previous chapter.
@@ -185,7 +179,7 @@ alternatives) but it is a great tutorial data structure.
```java
struct List {
!List? !next; // Next pointer or null
- str !name; // Payload
+ str !name; // Payload
};
```
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/Parser.java b/chapter23/src/main/java/com/seaofnodes/simple/Parser.java
index 4b7cca70..6084eaa5 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/Parser.java
@@ -700,6 +700,8 @@ public class Parser {
// Auto-narrow wide ints to narrow ints. For loads, emit code to force
// the loaded value to match the declared sign/zero bits. For stores,
// just force the type, acting "as if" the store silently truncates.
+ if( expr._type instanceof TypeFloat && t instanceof TypeInteger )
+ expr = peep(new ToIntegerNode(expr));
Type et = expr._type;
if( isLoad ) { expr = zsMask(expr,t); et = expr._type; }
else if( et instanceof TypeInteger && t instanceof TypeInteger ) et=t;
@@ -1583,6 +1585,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter23/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
new file mode 100644
index 00000000..31dcfc36
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+
+public class F8ToIARM extends MachConcreteNode implements MachNode {
+ F8ToIARM(Node f8toi) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ enc.add4(Arm64.floatToInteger(enc.reg(in(1))-arm.D_OFFSET,enc.reg(this)));
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index e7119608..8bdcfe10 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -316,6 +316,7 @@ public class arm extends Machine {
case SubFNode subf -> new SubFARM(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> new I2F8ARM(tfn);
+ case ToIntegerNode cvt -> new F8ToIARM(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
new file mode 100644
index 00000000..a56ad216
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -0,0 +1,19 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+
+public class F8ToIRISC extends MachConcreteNode implements MachNode {
+ F8ToIRISC(Node f8toi) {super(f8toi);}
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ RiscV.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-riscv.F_OFFSET);
+ }
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index 6be0376a..78d21f3b 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -240,6 +240,7 @@ public class riscv extends Machine {
case SubFNode subf -> new SubFRISC(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIRISC(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
new file mode 100644
index 00000000..a79a1017
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -0,0 +1,22 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+
+public class F8ToIX86 extends MachConcreteNode implements MachNode {
+ F8ToIX86(Node f8toi ) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
+ @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ @Override public void encoding( Encoding enc ) {
+ X86.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-x86_64_v2.XMM_OFFSET);
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index 4beef793..e283bc8f 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -287,6 +287,7 @@ public class x86_64_v2 extends Machine {
case SubFNode subf -> new SubFX86(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIX86(cvt);
case XCtrlNode x -> new ConstantNode(Type.XCONTROL);
case XorNode xor -> xor(xor);
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 8e9aaed6..041e0d06 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -124,8 +124,8 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Type 3.14 is not of declared type i64",e.getMessage()); }
+ code.parse().opto().typeCheck();
+ assertEquals("3",Eval2.eval(code,0));
}
@Test
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 009beb0b..23d0ef37 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -7,6 +7,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var code = new CodeGen(test[0]).parse().opto().typeCheck();
+ assertEquals(test[0],test[1],Eval2.eval(code,17));
+ assertEquals(test[0],test[2],Eval2.eval(code,-17));
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index 813fc4c2..59bd3b0c 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -123,8 +123,8 @@ return s.x;
@Test public void testInt10() {
CodeGen code = new CodeGen("flt x = 1.5; x++; int i = x; return i;");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Type 2.5f is not of declared type i64",e.getMessage()); }
+ code.parse().opto().typeCheck();
+ assertEquals("2",Eval2.eval(code,0));
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter23/src/test/java/com/seaofnodes/simple/Eval2.java
index feb67ba9..908cc2e0 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -283,6 +283,8 @@ public abstract class Eval2 {
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 RoundF32Node rnd -> (double)(float)d(rnd.in(1));
+ case ToIntegerNode cvt -> (long)d(cvt.in(1));
case ToFloatNode toflt-> (double)x(toflt.in(1));
case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2));
default -> throw Utils.TODO();
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter23/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 1c116162..20aac367 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -298,6 +298,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/Parser.java b/chapter24/src/main/java/com/seaofnodes/simple/Parser.java
index f28d0871..9cf565ac 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/Parser.java
@@ -707,6 +707,8 @@ public class Parser {
// Auto-narrow wide ints to narrow ints. For loads, emit code to force
// the loaded value to match the declared sign/zero bits. For stores,
// just force the type, acting "as if" the store silently truncates.
+ if( expr._type instanceof TypeFloat && t instanceof TypeInteger )
+ expr = peep(new ToIntegerNode(expr));
Type et = expr._type;
if( isLoad ) { expr = zsMask(expr,t); et = expr._type; }
else if( et instanceof TypeInteger && t instanceof TypeInteger ) et=t;
@@ -1643,6 +1645,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter24/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..df740202
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,23 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
new file mode 100644
index 00000000..31dcfc36
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+
+public class F8ToIARM extends MachConcreteNode implements MachNode {
+ F8ToIARM(Node f8toi) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ enc.add4(Arm64.floatToInteger(enc.reg(in(1))-arm.D_OFFSET,enc.reg(this)));
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index f512f467..defec5ef 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -318,6 +318,7 @@ public class arm extends Machine {
case SubFNode subf -> new SubFARM(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> new I2F8ARM(tfn);
+ case ToIntegerNode cvt -> new F8ToIARM(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
new file mode 100644
index 00000000..a56ad216
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -0,0 +1,19 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+
+public class F8ToIRISC extends MachConcreteNode implements MachNode {
+ F8ToIRISC(Node f8toi) {super(f8toi);}
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ RiscV.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-riscv.F_OFFSET);
+ }
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index 6be0376a..78d21f3b 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -240,6 +240,7 @@ public class riscv extends Machine {
case SubFNode subf -> new SubFRISC(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIRISC(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
new file mode 100644
index 00000000..a79a1017
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -0,0 +1,22 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+
+public class F8ToIX86 extends MachConcreteNode implements MachNode {
+ F8ToIX86(Node f8toi ) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
+ @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ @Override public void encoding( Encoding enc ) {
+ X86.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-x86_64_v2.XMM_OFFSET);
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index 4beef793..e283bc8f 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -287,6 +287,7 @@ public class x86_64_v2 extends Machine {
case SubFNode subf -> new SubFX86(subf);
case SubNode sub -> sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIX86(cvt);
case XCtrlNode x -> new ConstantNode(Type.XCONTROL);
case XorNode xor -> xor(xor);
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 8e9aaed6..041e0d06 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -124,8 +124,8 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Type 3.14 is not of declared type i64",e.getMessage()); }
+ code.parse().opto().typeCheck();
+ assertEquals("3",Eval2.eval(code,0));
}
@Test
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 009beb0b..23d0ef37 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -7,6 +7,25 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var code = new CodeGen(test[0]).parse().opto().typeCheck();
+ assertEquals(test[0],test[1],Eval2.eval(code,17));
+ assertEquals(test[0],test[2],Eval2.eval(code,-17));
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index 813fc4c2..59bd3b0c 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -123,8 +123,8 @@ return s.x;
@Test public void testInt10() {
CodeGen code = new CodeGen("flt x = 1.5; x++; int i = x; return i;");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Type 2.5f is not of declared type i64",e.getMessage()); }
+ code.parse().opto().typeCheck();
+ assertEquals("2",Eval2.eval(code,0));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter24/src/test/java/com/seaofnodes/simple/Eval2.java
index e0d56399..98e0f18a 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -283,6 +283,8 @@ public abstract class Eval2 {
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 RoundF32Node rnd -> (double)(float)d(rnd.in(1));
+ case ToIntegerNode cvt -> (long)d(cvt.in(1));
case ToFloatNode toflt-> (double)x(toflt.in(1));
case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2));
default -> throw Utils.TODO();
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter24/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 1c116162..20aac367 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -298,6 +298,8 @@ public class Evaluator {
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 RoundF32Node rnd -> (double)(float)vald(rnd.in(1));
+ case ToIntegerNode cvt -> (long)vald(cvt.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
case StoreNode store -> store(store);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
index 42c83f93..cd80b3ff 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
@@ -2365,6 +2365,8 @@ public class Parser {
// zero/sign extend. "i" is limited to either classic unsigned (min==0) or
// classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
private Node zsMask(Node val, Type t ) {
+ if( val._type instanceof TypeFloat && t instanceof TypeInteger )
+ val = new ToIntegerNode(val).peephole();
if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
return val;
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java
index b038366a..b6c3b0c9 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java
@@ -55,6 +55,10 @@ public class ConvertNode extends Node {
if( (src instanceof TypeInteger || src==Type.NIL) && _dst instanceof TypeFloat )
return new ToFloatNode(val());
+ // Convert to i64 first, then apply the destination's integer width.
+ if( src instanceof TypeFloat && _dst instanceof TypeInteger )
+ return new ConvertNode(_dst,new ToIntegerNode(val()).peephole());
+
// Narrow integers produce the declared sign/zero extension.
if( src instanceof TypeInteger && _dst instanceof TypeInteger dst ) {
if( dst._min==0 )
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java
index 9f5e107a..23a7e2c4 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -77,7 +77,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
New,Never,Not,Or,Parm,Phi,Proj,
ReadOnly,Return,Region,RoundF32,
Sar,Shl,Shr,Start,Stop,Store,Sub,ToFloat,
- XCtrl,Xor,StartCU,StopCU,Guard,MemPhi,BulkMemPhi,PtrToInt,ExternOffset;
+ XCtrl,Xor,StartCU,StopCU,Guard,MemPhi,BulkMemPhi,PtrToInt,ExternOffset,ToInteger;
public static final Tag[] VALS = values();
public Node make( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases ) {
return switch(this) {
@@ -101,6 +101,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
case Start -> new StartNode(null,null,TypeInteger.BOT);
case Sub -> new SubNode(null,null,(byte)bais.packed1());
case ToFloat-> new ToFloatNode(null);
+ case ToInteger-> new ToIntegerNode(null);
case XCtrl -> new XCtrlNode();
case Xor -> { int mode=bais.packed1(); assert mode==1; yield new XorNode(null,null,null); }
case Guard -> GuardNode.make(bais);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
new file mode 100644
index 00000000..33806300
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ToIntegerNode.java
@@ -0,0 +1,24 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import com.seaofnodes.simple.type.TypeInteger;
+
+// Truncate toward zero, saturating overflow; NaN converts to zero.
+public class ToIntegerNode extends Node {
+ public ToIntegerNode(Node lhs) { super(null,lhs); }
+ @Override public Tag serialTag() { return Tag.ToInteger; }
+ @Override public String label() { return "ToInteger"; }
+ @Override protected String format() { return "(int)%1"; }
+
+ @Override public Type compute() {
+ Type t = in(1)._type;
+ if( t.isHigh() ) return TypeInteger.TOP;
+ if( t instanceof TypeFloat tf && tf.isConstant() )
+ return TypeInteger.constant((long)tf.value());
+ return TypeInteger.BOT;
+ }
+
+ @Override public Node idealize() { return null; }
+ @Override Node copy(Node lhs, Node rhs) { return new ToIntegerNode(lhs); }
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
new file mode 100644
index 00000000..31dcfc36
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/F8ToIARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+
+public class F8ToIARM extends MachConcreteNode implements MachNode {
+ F8ToIARM(Node f8toi) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { return arm.DMASK; }
+ @Override public RegMask outregmap() { return arm.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ enc.add4(Arm64.floatToInteger(enc.reg(in(1))-arm.D_OFFSET,enc.reg(this)));
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index 2ba2d62e..660f1559 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -339,6 +339,7 @@ public class arm extends Machine {
case StoreNode st -> st(st);
case SubNode sub -> sub.mode()==2 ? new SubFARM(sub) : sub(sub);
case ToFloatNode tfn -> new I2F8ARM(tfn);
+ case ToIntegerNode cvt -> new F8ToIARM(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
new file mode 100644
index 00000000..a56ad216
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/F8ToIRISC.java
@@ -0,0 +1,19 @@
+package com.seaofnodes.simple.node.cpus.riscv;
+
+import com.seaofnodes.isa.RiscV;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+
+public class F8ToIRISC extends MachConcreteNode implements MachNode {
+ F8ToIRISC(Node f8toi) {super(f8toi);}
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return riscv.FMASK; }
+ @Override public RegMask outregmap() { return riscv.WMASK; }
+ @Override public void encoding( Encoding enc ) {
+ RiscV.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-riscv.F_OFFSET);
+ }
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
index 808ad82e..ae69b439 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java
@@ -252,6 +252,7 @@ public class riscv extends Machine {
case StoreNode st -> st(st);
case SubNode sub -> sub.mode()==2 ? new SubFRISC(sub) : sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIRISC(cvt);
case XorNode xor -> xor(xor);
case LoopNode loop -> new LoopNode(loop);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
new file mode 100644
index 00000000..a79a1017
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/F8ToIX86.java
@@ -0,0 +1,22 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.util.SB;
+
+public class F8ToIX86 extends MachConcreteNode implements MachNode {
+ F8ToIX86(Node f8toi ) { super(f8toi); }
+ @Override public String op() { return "cvti"; }
+ @Override public RegMask regmap(int i) { assert i==1; return x86_64_v2.XMASK; }
+ @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; }
+ @Override public RegMask outregmap() { return x86_64_v2.WMASK; }
+
+ @Override public void encoding( Encoding enc ) {
+ X86.floatToInteger(enc,enc.reg(this),enc.reg(in(1))-x86_64_v2.XMM_OFFSET);
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p(code.reg(this)).p(" = ").p("(int)").p(code.reg(in(1)));
+ }
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index f006411e..ff15fdad 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -320,6 +320,7 @@ public class x86_64_v2 extends Machine {
case StoreNode st -> st(st);
case SubNode sub -> sub.mode()==2 ? new SubFX86(sub) : sub(sub);
case ToFloatNode tfn -> i2f8(tfn);
+ case ToIntegerNode cvt -> new F8ToIX86(cvt);
case XCtrlNode x -> ConstantNode.raw(Type.XCONTROL);
case XorNode xor -> xor(xor);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index a71cc167..f1ff2e14 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -124,8 +124,8 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Type 3.14 is not of declared type i64",e.getMessage()); }
+ code.parse().opto().typeCheck();
+ assertEquals("3",Eval2.eval(code,0));
}
@Test
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 20274581..80f70ff8 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -5,6 +5,25 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class Chapter14FloatTest {
+ @Test public void testNumericAssignments() {
+ String[][] tests = {
+ {"int rounded=17.3/2.5; return rounded;", "6", "6"},
+ {"int rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
+ {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
+ {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
+ {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
+ {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
+ {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
+ };
+ for( String[] test : tests ) {
+ var code = new CodeGen(test[0]).parse().opto().typeCheck();
+ assertEquals(test[0],test[1],Eval2.eval(code,17));
+ assertEquals(test[0],test[2],Eval2.eval(code,-17));
+ }
+ }
+
+
@Test public void testSubZeroFloat() {
var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
// Compare strings so +0.0 and -0.0 remain distinct.
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index ab1a59e1..62d0b261 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -123,8 +123,8 @@ return s.x;
@Test public void testInt10() {
CodeGen code = new CodeGen("flt x = 1.5; x++; int i = x; return i;");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Type 2.5f is not of declared type i64",e.getMessage()); }
+ code.parse().opto().typeCheck();
+ assertEquals("2",Eval2.eval(code,0));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 4d610a42..ba71d1dd 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -15,6 +15,27 @@ import static org.junit.Assert.*;
public class Chapter21Test {
+ @Test public void testFloatToInteger() throws IOException {
+ String src = "val float_to_int={ flt x -> int i=x; return i; };"+
+ "val float_to_u8={ flt x -> u8 i=x; return i; };";
+ TestC.runC(src,"floatToInteger","",-1);
+ for( double x : new double[]{0.0,-0.0,6.92,-6.92,255.75,-257.75,
+ 0x1p63,Math.nextDown(0x1p63),-0x1p63,Math.nextDown(-0x1p63),
+ Double.POSITIVE_INFINITY,Double.NEGATIVE_INFINITY,Double.NaN} ) {
+ for( String name : new String[]{"float_to_int","float_to_u8"} ) {
+ long expected = name.equals("float_to_u8") ? ((long)x)&255 : (long)x;
+ EvalRisc5 r = TestRisc5.build(src,name,0,-1,false);
+ r.fregs[riscv.FA0-riscv.F_OFFSET]=x;
+ assertEquals(0,r.step(1000));
+ assertEquals(name+"("+x+")",expected,r.regs[riscv.A0]);
+ EvalArm64 a = TestArm64.build(name,src,0,-1,false);
+ a.fregs[arm.D0-arm.D_OFFSET]=x;
+ assertEquals(0,a.step(1000));
+ assertEquals(name+"("+x+")",expected,a.regs[arm.X0]);
+ }
+ }
+ }
+
@Test public void testReadBeforeConditionalStore() {
var code = new CodeGen("""
struct S { int x; }; !S !a=new S; !S !b=new S;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java
index 5716c1b8..21e677ce 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -307,6 +307,8 @@ public abstract class Eval2 {
case SubNode sub -> sub.mode()==1
? (Object)(x(sub.in(1)) - x(sub.in(2)))
: (Object)(d(sub.in(1)) - d(sub.in(2)));
+ case RoundF32Node rnd -> (double)(float)d(rnd.in(1));
+ case ToIntegerNode cvt -> (long)d(cvt.in(1));
case ToFloatNode toflt-> (double)x(toflt.in(1));
case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2));
default -> throw Utils.TODO();
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/floatToInteger.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/floatToInteger.c
new file mode 100644
index 00000000..cf9390d8
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/progs/floatToInteger.c
@@ -0,0 +1,25 @@
+#include <stdint.h>
+#include <math.h>
+#include <stdio.h>
+
+extern int64_t float_to_int(double);
+extern int64_t float_to_u8(double);
+
+int main(void) {
+ double values[] = {0.0,-0.0,6.92,-6.92,0.99,-0.99,255.75,-257.75,
+ 0x1p63,0x1.fffffffffffffp62,-0x1p63,-0x1.0000000000001p63,
+ INFINITY,-INFINITY,NAN,-NAN};
+ for( unsigned i=0; i<sizeof(values)/sizeof(values[0]); i++ ) {
+ double x=values[i];
+ int64_t expected=isnan(x) ? 0 : x>=0x1p63 ? INT64_MAX
+ : x<=-0x1p63 ? INT64_MIN : (int64_t)x;
+ int64_t actual=float_to_int(x), narrow=float_to_u8(x);
+ if( actual!=expected || narrow!=(expected&255) ) {
+ fprintf(stderr,"conversion of %g: got %lld / %lld, expected %lld / %lld\n",
+ x,(long long)actual,(long long)narrow,
+ (long long)expected,(long long)(expected&255));
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/docs/chapter-backports.md b/docs/chapter-backports.md
index 765cad82..4b516036 100644
--- a/docs/chapter-backports.md
+++ b/docs/chapter-backports.md
@@ -41,8 +41,8 @@ shallow-reference boundary and resolving field definitions by name.
Historical validation entries below use the chapter numbers and test names
in effect when the work was done. In particular, old 10a/10b records refer to
current 10/11, and old `Chapter12Test` float regressions now live in
-`Chapter14FloatTest`. The pending float-to-int conversion belongs with the
-numeric material in Chapter 14; it is not implemented by this reorganization.
+`Chapter14FloatTest`. Float-to-int assignment conversion was subsequently
+added with the numeric material in Chapter 14 and propagated through 25.
Validation of the reorganization (2026-10-03): Make suites pass in every
chapter 10-25, including the shared printer/ISA tests and Chapter 25's native
@@ -465,14 +465,6 @@ lines beyond the original extraction.
## Pending corrections
-- **Float-to-integer conversion (new feature).** Missing from Chapters 14-25;
- Chapter 14's `RoundF32Node` only narrows floats to `f32`. Add an explicit
- conversion, with Chapter 14 as the proposed introduction, and carry it
- through the later chapters. Decide syntax, rounding versus truncation,
- and behavior for NaN, infinities, and out-of-range values before implementing.
- Cover constant folding, evaluation, and machine lowering on all three targets;
- update the Chapter 14 explanation and add boundary-case regressions.
-
- **Chapter 24 Load BOTTOM-on-error backport, unwound/deferred (2026-09-28).**
Cliff requested starting with 24 and analyzing failures before proceeding.
Literal global BOTTOM memory/pointer inputs already propagate BOTTOM. The
@@ -664,6 +656,23 @@ The top-level runner accepts explicit chapter lists, e.g.
## Validation record
+- **Numeric assignment conversions and evaluators (2026-10-07).** Chapters
+ 14-25 accept float-to-integer assignment using ToIntegerNode: truncate toward
+ zero, saturate to i64 on overflow, map NaN to zero, then apply the declared
+ integer width. Locals, fields, and arrays use the conversion; Chapters 22-24
+ also convert before their store-specific width handling. Chapter 25 lowers
+ authoritative Convert nodes and serializes ToInteger in exported graphs.
+ Both evaluators now execute ToInteger and RoundF32, including nonconstant
+ f32 array stores. The Chapter 14 README documents the assignment syntax.
+ Instruction selection starts in 19, ARM support in 20, and shared encoding
+ in 21. x86 and RISC-V sequences normalize hardware overflow/NaN differences;
+ ARM uses FCVTZS. Full Make suites pass in 14-25 and the shared ISA module.
+ Tests cover constant and dynamic conversions, signed values, narrow integer
+ results, limits, infinities, and NaN; Chapter 25 runs native x86 plus compiled
+ ARM/RISC-V emulation. Disposable probes also check 19-20 selection/allocation,
+ 21-24 encoding/emulation, and 25 importing an exported conversion function.
+ Logs: `build/float-convert-<chapter>.log`, `build/float-convert-isa.log`.
+
- **Floating arrays in Chapter 18 (2026-10-07).** Added TypeFloat to the
`TypeStruct.makeAry` assertion, matching Chapter 19 onward; earlier array
chapters already accept floating elements. The original `return new flt[1];`
@@ -672,8 +681,8 @@ The top-level runner accepts explicit chapter lists, e.g.
It fails at the original assertion before the correction. The complete
Chapter 18 suite passes (340 tests plus its fuzzer wrapper); no other compiler
snapshot needed this assertion change. Nonconstant scalar and array narrowing
- now passes type checking with the RoundF32 correction below. Eval2 does not
- execute RoundF32 yet, so runtime coverage uses constant f32 stores. Logs:
+ passes type checking with the RoundF32 correction below. Subsequent evaluator
+ support also exercises nonconstant f32 stores at runtime. Original logs:
`build/float-arrays-18-before.log`, `build/float-arrays-18-after.log`.
- **Nonconstant f32 narrowing (2026-10-07).** RoundF32Node now returns F32
diff --git a/isa/src/main/java/com/seaofnodes/isa/Arm64.java b/isa/src/main/java/com/seaofnodes/isa/Arm64.java
index 2c969da0..ce7f7b11 100644
--- a/isa/src/main/java/com/seaofnodes/isa/Arm64.java
+++ b/isa/src/main/java/com/seaofnodes/isa/Arm64.java
@@ -236,6 +236,12 @@ public final class Arm64 {
(vd << 12) | (0x01100010 << 4) | vm;
}
+ /** FCVTZS Xd,Dn: truncate, saturate overflow, and convert NaN to zero. */
+ public static int floatToInteger(int rn, int rd) {
+ assert 0<=rn && rn<32 && 0<=rd && rd<32;
+ return 0x9E780000 | (rn<<5) | rd;
+ }
+
public static int float_cast(int opcode, int ftype, int rn, int rd) {
assert 0 <= rd && rd < 32;
assert 0 <= rn && rn < 32;
diff --git a/isa/src/main/java/com/seaofnodes/isa/RiscV.java b/isa/src/main/java/com/seaofnodes/isa/RiscV.java
index cb446a6a..0ecd1cec 100644
--- a/isa/src/main/java/com/seaofnodes/isa/RiscV.java
+++ b/isa/src/main/java/com/seaofnodes/isa/RiscV.java
@@ -59,6 +59,16 @@ public final class RiscV {
return bits << 12 | rd << 7 | opcode;
}
+ /** FCVT.L.D with RTZ saturates overflow; explicitly map either NaN to zero. */
+ public static void floatToInteger(CodeSink out, int dst, int src) {
+ out.add4(r_type(OP_FP,dst,1,src,0,0x71)); // FCLASS.D
+ out.add4(i_type(OP_IMM,dst,7,dst,0x300)); // ANDI: NaN classification bits
+ out.add4(b_type(OP_BRANCH,1,(short)dst,(short)0,12)); // BNE nan
+ out.add4(r_type(OP_FP,dst,RM.RTZ.ordinal(),src,2,0x61)); // FCVT.L.D
+ out.add4(j_type(OP_JAL,0,8)); // J done
+ out.add4(i_type(OP_IMM,dst,0,0,0)); // nan: LI dst,0
+ }
+
public static int i_type(int opcode, int rd, int func3, int rs1, int imm12) {
assert 0 <= rd && rd < 32;
assert 0 <= rs1 && rs1 < 32;
diff --git a/isa/src/main/java/com/seaofnodes/isa/X86.java b/isa/src/main/java/com/seaofnodes/isa/X86.java
index 99d61dff..78fa3da5 100644
--- a/isa/src/main/java/com/seaofnodes/isa/X86.java
+++ b/isa/src/main/java/com/seaofnodes/isa/X86.java
@@ -60,6 +60,22 @@ public final class X86 {
out.add1(0x48).add1(0x99); // CQO: sign-extend rax into rdx:rax.
unary(out,0xF7,7,src);
}
+ /** Truncate f64 to i64, saturating overflow and mapping NaN to zero. */
+ public static void floatToInteger(CodeSink out, int dst, int src) {
+ sse(out,0xF2,0x0F2C,true,dst,src); // CVTTSD2SI
+ imm(out,0x81,7,dst,1); // CMP dst,1: OF iff dst is MIN_VALUE
+ out.add1(0x71).add1((src>=8 ? 5 : 4)+24); // JNO done
+ sse(out,0x66,0x0F2E,false,src,src); // UCOMISD: unordered iff NaN
+ out.add1(0x7A).add1(19); // JP nan
+ sse(out,0x66,0x0F7E,true,src,dst); // MOVQ: inspect the float's sign
+ shift(out,7,dst,63); // SAR: 0 or -1
+ unary(out,0xF7,2,dst); // NOT: -1 or 0
+ reg(out,0x0FBA,7,dst); // BTC sign bit: MAX_VALUE or MIN_VALUE
+ out.add1(63);
+ out.add1(0xEB).add1(3); // JMP done
+ reg(out,0x33,dst,dst); // nan: XOR dst,dst
+ }
+
public static void sse(CodeSink out, int prefix, int op, boolean wide, int dst, int src) {
if( prefix!=0 ) out.add1(prefix);
rexF(dst,src,-1,wide,out);
diff --git a/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java b/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java
index 2a4a2708..2fa4aec1 100644
--- a/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java
+++ b/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java
@@ -404,10 +404,13 @@ public class EvalArm64 {
break;
}
case 0x9E: {
- // scvtf
- is_f = true;
int rs1 = (ir >> 5) & 0x1F;
- frval = (double)regs[rs1];
+ if( (ir & 0xFFFFFC00)==0x9E780000 ) { // FCVTZS Xd,Dn
+ rval = (long)fregs[rs1];
+ } else { // SCVTF
+ is_f = true;
+ frval = (double)regs[rs1];
+ }
break;
}
diff --git a/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalRisc5.java b/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalRisc5.java
index f618075d..46baf447 100644
--- a/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalRisc5.java
+++ b/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalRisc5.java
@@ -334,7 +334,25 @@ public class EvalRisc5 {
default -> throw new UnsupportedOperationException();
};
break;
- case 26: frval = (double)regs[rs1]; break; // fcvt.w.d
+ case 24: // FCVT.L.D, RTZ
+ if( rs2!=2 || ((ir>>12)&7)!=1 ) throw new UnsupportedOperationException();
+ is_f = false;
+ rval = Double.isNaN(lhs) ? Long.MAX_VALUE : (long)lhs;
+ break;
+ case 26: frval = (double)regs[rs1]; break; // fcvt.d.l
+ case 28: { // FCLASS.D
+ if( rs2!=0 || ((ir>>12)&7)!=1 ) throw new UnsupportedOperationException();
+ is_f = false;
+ long bits = Double.doubleToRawLongBits(lhs);
+ boolean neg = bits<0;
+ int kind = Double.isNaN(lhs) ? ((bits&(1L<<51))==0 ? 8 : 9)
+ : Double.isInfinite(lhs) ? (neg ? 0 : 7)
+ : lhs==0 ? (neg ? 3 : 4)
+ : Math.abs(lhs)<Double.MIN_NORMAL ? (neg ? 2 : 5)
+ : (neg ? 1 : 6);
+ rval = 1L<<kind;
+ break;
+ }
default:
trap = (2+1); // Fault: Invalid opcode.
throw new UnsupportedOperationException();
diff --git a/isa/src/test/java/com/seaofnodes/isa/RiscTest.java b/isa/src/test/java/com/seaofnodes/isa/RiscTest.java
index e26eb20b..0cef8531 100644
--- a/isa/src/test/java/com/seaofnodes/isa/RiscTest.java
+++ b/isa/src/test/java/com/seaofnodes/isa/RiscTest.java
@@ -9,6 +9,28 @@ public class RiscTest {
assert expected==actual : String.format("Expected %08x, got %08x",expected,actual);
}
public static void main(String[] args) {
+ word(0x9E780020,Arm64.floatToInteger(1,0)); // FCVTZS x0,d1
+ for( double value : new double[]{0.0,-0.0,6.92,-6.92,0.99,-0.99,
+ 0x1p63,Math.nextDown(0x1p63),-0x1p63,Math.nextDown(-0x1p63),
+ Double.POSITIVE_INFINITY,Double.NEGATIVE_INFINITY,Double.NaN,
+ Double.longBitsToDouble(0xFFF0000000000001L)} ) {
+ var a=new EvalArm64(new byte[1024],512);
+ a.st4(4,0x9E780020);
+ a._pc=4;
+ a.fregs[1]=value;
+ a.step(1);
+ assert a.regs[0]==(long)value : "ARM conversion: "+value;
+
+ var bytes=new X86Test.Bytes();
+ RiscV.floatToInteger(bytes,10,1);
+ var r=new EvalRisc5(new byte[1024],512);
+ System.arraycopy(bytes.toByteArray(),0,r._buf,4,bytes.size());
+ r._pc=4;
+ r.fregs[1]=value;
+ // NaN takes four instructions; a finite value takes five.
+ r.step(Double.isNaN(value) ? 4 : 5);
+ assert r.regs[10]==(long)value : "RISC-V conversion: "+value;
+ }
word(0x8B020020,Arm64.r_reg(Arm64.OP_ADD,0,2,0,1,0)); // add x0,x1,x2
word(0x9100A420,Arm64.imm_inst(Arm64.OPI_ADD,41,1,0));
word(0xD2800540,Arm64.mov(Arm64.OP_MOVZ,0,42,0));
diff --git a/isa/src/test/java/com/seaofnodes/isa/X86Test.java b/isa/src/test/java/com/seaofnodes/isa/X86Test.java
index e3aa2699..8af84ae8 100644
--- a/isa/src/test/java/com/seaofnodes/isa/X86Test.java
+++ b/isa/src/test/java/com/seaofnodes/isa/X86Test.java
@@ -16,6 +16,10 @@ public class X86Test {
assert expected.equals(actual) : "Expected "+expected+", got "+actual;
}
public static void main(String[] args) {
+ check("f2480f2cc04883f801711c660f2ec07a1366480f7ec048c1f83f48f7d0480fbaf83feb034833c0",
+ b -> X86.floatToInteger(b,0,0));
+ check("f24d0f2cca4983f901711d66450f2ed27a13664d0f7ed149c1f93f49f7d1490fbaf93feb034d33c9",
+ b -> X86.floatToInteger(b,9,10));
check("4d03ca", b -> X86.reg(b,0x03,9,10));
check("4d0fafca", b -> X86.reg(b,0x0FAF,9,10));
check("4981c17fffffff", b -> X86.imm(b,0x81,0,9,-129));