summaryrefslogtreecommitdiff
path: root/chapter25
diff options
context:
space:
mode:
Diffstat (limited to 'chapter25')
-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
16 files changed, 168 insertions, 5 deletions
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;
+}