summaryrefslogtreecommitdiff
path: root/isa
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-07 12:57:31 -0700
committerCliff Click <cliffc@acm.org>2026-10-07 12:57:31 -0700
commitaaec6f8136df6f7020bfc05f79ff5273adf73de1 (patch)
tree5df4aae069bd88dd09f45d957c85161e571b7a46 /isa
parent514b3147bf08817d17aacc492be255621209ec8b (diff)
downloadSimple-aaec6f8136df6f7020bfc05f79ff5273adf73de1.tar.gz
Simple-aaec6f8136df6f7020bfc05f79ff5273adf73de1.zip
flt->int conversion
Diffstat (limited to 'isa')
-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
7 files changed, 83 insertions, 4 deletions
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));