diff --git a/compiler/compiler.go b/compiler/compiler.go index 35d42eef..d9a5ed4e 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -2569,7 +2569,7 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p maxFloat := float64(max) if bits.Len64(max) > significandBits { // Round the max down to fit within the significand. - maxFloat = float64(max & ^uint64(0) << uint(bits.Len64(max)-significandBits)) + maxFloat = float64(max & (^uint64(0) << uint(bits.Len64(max)-significandBits))) } // Check if the value is in-bounds (0 <= value <= max). @@ -2598,7 +2598,7 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p maxFloat := float64(max) if bits.Len64(max) > significandBits { // Round the max down to fit within the significand. - maxFloat = float64(max & ^uint64(0) << uint(bits.Len64(max)-significandBits)) + maxFloat = float64(max & (^uint64(0) << uint(bits.Len64(max)-significandBits))) } // Check if the value is in-bounds (min <= value <= max). @@ -2609,10 +2609,17 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p // Assuming that the value is out-of-bounds, select a saturated value. saturated := b.CreateSelect(aboveMin, llvm.ConstInt(llvmTypeTo, max, false), // value > max - llvm.ConstInt(llvmTypeTo, min, false), // value < min (or NaN) + llvm.ConstInt(llvmTypeTo, min, false), // value < min "saturated", ) + // Map NaN to 0. + saturated = b.CreateSelect(b.CreateFCmp(llvm.FloatUNO, value, value, "isnan"), + llvm.ConstNull(llvmTypeTo), + saturated, + "remapped", + ) + // Do a normal conversion. normal := b.CreateFPToSI(value, llvmTypeTo, "normal") diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 2ae11229..58199248 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -37,6 +37,7 @@ func TestCompiler(t *testing.T) { "basic.go", "pointer.go", "slice.go", + "float.go", } for _, testCase := range tests { diff --git a/compiler/testdata/float.go b/compiler/testdata/float.go new file mode 100644 index 00000000..a69c3d79 --- /dev/null +++ b/compiler/testdata/float.go @@ -0,0 +1,39 @@ +package main + +// Test converting floats to ints. + +func f32tou32(v float32) uint32 { + return uint32(v) +} + +func maxu32f() float32 { + return float32(^uint32(0)) +} + +func maxu32tof32() uint32 { + f := float32(^uint32(0)) + return uint32(f) +} + +func inftoi32() (uint32, uint32, int32, int32) { + inf := 1.0 + inf /= 0.0 + + return uint32(inf), uint32(-inf), int32(inf), int32(-inf) +} + +func u32tof32tou32(v uint32) uint32 { + return uint32(float32(v)) +} + +func f32tou32tof32(v float32) float32 { + return float32(uint32(v)) +} + +func f32tou8(v float32) uint8 { + return uint8(v) +} + +func f32toi8(v float32) int8 { + return int8(v) +} diff --git a/compiler/testdata/float.ll b/compiler/testdata/float.ll new file mode 100644 index 00000000..817ee091 --- /dev/null +++ b/compiler/testdata/float.ll @@ -0,0 +1,80 @@ +; ModuleID = 'float.go' +source_filename = "float.go" +target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128" +target triple = "i686--linux" + +define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + ret void +} + +define internal i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %positive = fcmp oge float %v, 0.000000e+00 + %withinmax = fcmp ole float %v, 0x41EFFFFFC0000000 + %inbounds = and i1 %positive, %withinmax + %saturated = sext i1 %positive to i32 + %normal = fptoui float %v to i32 + %0 = select i1 %inbounds, i32 %normal, i32 %saturated + ret i32 %0 +} + +define internal float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + ret float 0x41F0000000000000 +} + +define internal i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + ret i32 -1 +} + +define internal { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 } +} + +define internal i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %0 = uitofp i32 %v to float + %withinmax = fcmp ole float %0, 0x41EFFFFFC0000000 + %normal = fptoui float %0 to i32 + %1 = select i1 %withinmax, i32 %normal, i32 -1 + ret i32 %1 +} + +define internal float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %positive = fcmp oge float %v, 0.000000e+00 + %withinmax = fcmp ole float %v, 0x41EFFFFFC0000000 + %inbounds = and i1 %positive, %withinmax + %saturated = sext i1 %positive to i32 + %normal = fptoui float %v to i32 + %0 = select i1 %inbounds, i32 %normal, i32 %saturated + %1 = uitofp i32 %0 to float + ret float %1 +} + +define internal i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %positive = fcmp oge float %v, 0.000000e+00 + %withinmax = fcmp ole float %v, 2.550000e+02 + %inbounds = and i1 %positive, %withinmax + %saturated = sext i1 %positive to i8 + %normal = fptoui float %v to i8 + %0 = select i1 %inbounds, i8 %normal, i8 %saturated + ret i8 %0 +} + +define internal i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + %abovemin = fcmp oge float %v, -1.280000e+02 + %belowmax = fcmp ole float %v, 1.270000e+02 + %inbounds = and i1 %abovemin, %belowmax + %saturated = select i1 %abovemin, i8 127, i8 -128 + %isnan = fcmp uno float %v, 0.000000e+00 + %remapped = select i1 %isnan, i8 0, i8 %saturated + %normal = fptosi float %v to i8 + %0 = select i1 %inbounds, i8 %normal, i8 %remapped + ret i8 %0 +} diff --git a/testdata/float.go b/testdata/float.go index 6d2d1bca..0a82d859 100644 --- a/testdata/float.go +++ b/testdata/float.go @@ -29,12 +29,21 @@ func main() { var f2 float32 = 5.7 var f3 float32 = -2.3 var f4 float32 = -11.8 + println(int32(f1), int32(f2), int32(f3), int32(f4)) + + // float -> int saturating behavior var f5 float32 = -1 var f6 float32 = 256 var f7 float32 = -129 - var f8 float32 = 0 - f8 /= 0 - println(int32(f1), int32(f2), int32(f3), int32(f4), uint8(f5), uint8(f6), int8(f7), int8(f6), uint8(f8), int8(f8)) + f8 := float32(^uint32(0)) + f9 := float32(int32(-2147483648)) + f10 := float32(int32(2147483647)) + var inf float32 = 1 + inf /= 0 + var nan float32 = 0 + nan /= 0 + println(uint8(f5), uint8(f6), int8(f7), int8(f6), uint32(f8), int32(f9), int32(f10), + uint8(inf), uint8(-inf), int8(inf), int8(-inf), uint8(nan), int64(nan)) // int -> float var i1 int32 = 53 diff --git a/testdata/float.txt b/testdata/float.txt index 12c48598..141b84fd 100644 --- a/testdata/float.txt +++ b/testdata/float.txt @@ -11,7 +11,8 @@ +3.333333e-001 +6.666667e-001 +6.666667e-001 -3 5 -2 -11 0 255 -128 127 0 -128 +3 5 -2 -11 +0 255 -128 127 4294967295 -2147483648 2147483647 255 0 127 -128 0 0 +5.300000e+001 -8.000000e+000 +2.000000e+001 (+6.666667e-001+1.200000e+000i) +6.666667e-001