|
8 | 8 | "go/constant"
|
9 | 9 | "go/token"
|
10 | 10 | "go/types"
|
| 11 | + "math/bits" |
11 | 12 | "path/filepath"
|
12 | 13 | "sort"
|
13 | 14 | "strconv"
|
@@ -2552,10 +2553,70 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p
|
2552 | 2553 |
|
2553 | 2554 | if typeFrom.Info()&types.IsFloat != 0 && typeTo.Info()&types.IsInteger != 0 {
|
2554 | 2555 | // Conversion from float to int.
|
| 2556 | + // Passing an out-of-bounds float to LLVM would cause UB, so that UB is trapped by select instructions. |
| 2557 | + // The Go specification says that this should be implementation-defined behavior. |
| 2558 | + // This implements saturating behavior, except that NaN is mapped to the minimum value. |
| 2559 | + var significandBits int |
| 2560 | + switch typeFrom.Kind() { |
| 2561 | + case types.Float32: |
| 2562 | + significandBits = 23 |
| 2563 | + case types.Float64: |
| 2564 | + significandBits = 52 |
| 2565 | + } |
2555 | 2566 | if typeTo.Info()&types.IsUnsigned != 0 { // if unsigned
|
2556 |
| - return b.CreateFPToUI(value, llvmTypeTo, ""), nil |
| 2567 | + // Select the maximum value for this unsigned integer type. |
| 2568 | + max := ^(^uint64(0) << uint(llvmTypeTo.IntTypeWidth())) |
| 2569 | + maxFloat := float64(max) |
| 2570 | + if bits.Len64(max) > significandBits { |
| 2571 | + // Round the max down to fit within the significand. |
| 2572 | + maxFloat = float64(max & ^uint64(0) << uint(bits.Len64(max)-significandBits)) |
| 2573 | + } |
| 2574 | + |
| 2575 | + // Check if the value is in-bounds (0 <= value <= max). |
| 2576 | + positive := b.CreateFCmp(llvm.FloatOLE, llvm.ConstNull(llvmTypeFrom), value, "positive") |
| 2577 | + withinMax := b.CreateFCmp(llvm.FloatOLE, value, llvm.ConstFloat(llvmTypeFrom, maxFloat), "withinmax") |
| 2578 | + inBounds := b.CreateAnd(positive, withinMax, "inbounds") |
| 2579 | + |
| 2580 | + // Assuming that the value is out-of-bounds, select a saturated value. |
| 2581 | + saturated := b.CreateSelect(positive, |
| 2582 | + llvm.ConstInt(llvmTypeTo, max, false), // value > max |
| 2583 | + llvm.ConstNull(llvmTypeTo), // value < 0 (or NaN) |
| 2584 | + "saturated", |
| 2585 | + ) |
| 2586 | + |
| 2587 | + // Do a normal conversion. |
| 2588 | + normal := b.CreateFPToUI(value, llvmTypeTo, "normal") |
| 2589 | + |
| 2590 | + return b.CreateSelect(inBounds, normal, saturated, ""), nil |
2557 | 2591 | } else { // if signed
|
2558 |
| - return b.CreateFPToSI(value, llvmTypeTo, ""), nil |
| 2592 | + // Select the minimum value for this signed integer type. |
| 2593 | + min := uint64(1) << uint(llvmTypeTo.IntTypeWidth()-1) |
| 2594 | + minFloat := -float64(min) |
| 2595 | + |
| 2596 | + // Select the maximum value for this signed integer type. |
| 2597 | + max := ^(^uint64(0) << uint(llvmTypeTo.IntTypeWidth()-1)) |
| 2598 | + maxFloat := float64(max) |
| 2599 | + if bits.Len64(max) > significandBits { |
| 2600 | + // Round the max down to fit within the significand. |
| 2601 | + maxFloat = float64(max & ^uint64(0) << uint(bits.Len64(max)-significandBits)) |
| 2602 | + } |
| 2603 | + |
| 2604 | + // Check if the value is in-bounds (min <= value <= max). |
| 2605 | + aboveMin := b.CreateFCmp(llvm.FloatOLE, llvm.ConstFloat(llvmTypeFrom, minFloat), value, "abovemin") |
| 2606 | + belowMax := b.CreateFCmp(llvm.FloatOLE, value, llvm.ConstFloat(llvmTypeFrom, maxFloat), "belowmax") |
| 2607 | + inBounds := b.CreateAnd(aboveMin, belowMax, "inbounds") |
| 2608 | + |
| 2609 | + // Assuming that the value is out-of-bounds, select a saturated value. |
| 2610 | + saturated := b.CreateSelect(aboveMin, |
| 2611 | + llvm.ConstInt(llvmTypeTo, max, false), // value > max |
| 2612 | + llvm.ConstInt(llvmTypeTo, min, false), // value < min (or NaN) |
| 2613 | + "saturated", |
| 2614 | + ) |
| 2615 | + |
| 2616 | + // Do a normal conversion. |
| 2617 | + normal := b.CreateFPToSI(value, llvmTypeTo, "normal") |
| 2618 | + |
| 2619 | + return b.CreateSelect(inBounds, normal, saturated, ""), nil |
2559 | 2620 | }
|
2560 | 2621 | }
|
2561 | 2622 |
|
|
0 commit comments