| ... |
... |
@@ -6714,10 +6714,172 @@ genClz bid width dst src = do |
|
6714
|
6714
|
-- W8/W16 cases because the 'MOV' insn already
|
|
6715
|
6715
|
-- took care of implicitly clearing the upper bits
|
|
6716
|
6716
|
|
|
|
6717
|
+{-
|
|
|
6718
|
+Note [Word-to-float conversion on x86-64]
|
|
|
6719
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
6720
|
+CVTSI2SD/CVTSI2SS treat their source as a *signed* integer, so an
|
|
|
6721
|
+unsigned Word with the MSB set would yield a negative float.
|
|
|
6722
|
+
|
|
|
6723
|
+We use a halve-and-double trick:
|
|
|
6724
|
+ 1. If src < 2^63 (MSB clear): convert directly; the signed and
|
|
|
6725
|
+ unsigned interpretations agree.
|
|
|
6726
|
+ 2. If src >= 2^63 (MSB set):
|
|
|
6727
|
+ (a) Compute tmp = (src `shiftR` 1) .|. (src .&. 1)
|
|
|
6728
|
+ which halves src while preserving the LSB as a "round bit".
|
|
|
6729
|
+ (b) Convert tmp as a signed integer (its MSB is now clear).
|
|
|
6730
|
+ (c) Double the float result.
|
|
|
6731
|
+
|
|
|
6732
|
+The round bit in step (a) is crucial for correct rounding. Without
|
|
|
6733
|
+it, adjacent even and odd large values would produce the same float.
|
|
|
6734
|
+With it, the conversion in step (b) sees the correct rounding
|
|
|
6735
|
+information, and doubling in step (c) scales back to the right range.
|
|
|
6736
|
+
|
|
|
6737
|
+Example (Float64, src = 2^64 - 1 = 0xFFFF_FFFF_FFFF_FFFF):
|
|
|
6738
|
+ tmp = 0x7FFF_FFFF_FFFF_FFFF | 1 = 0x7FFF_FFFF_FFFF_FFFF
|
|
|
6739
|
+ float64(tmp) rounds to 2^63 ≈ 9.2234e18
|
|
|
6740
|
+ 2 × 9.2234e18 = 1.8447e19 (= float64(2^64 - 1)) ✓
|
|
|
6741
|
+
|
|
|
6742
|
+Note [Word-to-float64 conversion on i386]
|
|
|
6743
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
6744
|
+On 32-bit x86, StgWord is 32 bits. CVTSI2SD converts a *signed*
|
|
|
6745
|
+32-bit integer, so inputs with the MSB set look negative.
|
|
|
6746
|
+
|
|
|
6747
|
+Trick: add 2^31 to flip the MSB, convert as signed, then add back 2^31 as a
|
|
|
6748
|
+Float64 constant.
|
|
|
6749
|
+
|
|
|
6750
|
+Let src' = src + 2^31 (mod 2^32):
|
|
|
6751
|
+ src in [0, 2^31): src' in [2^31, 2^32), signed value = src - 2^31.
|
|
|
6752
|
+ CVTSI2SD gives src - 2^31. + 2^31.0 → src ✓
|
|
|
6753
|
+ src = 2^31: src' = 0 (wraps). CVTSI2SD gives 0.0. + 2^31.0 → 2^31 ✓
|
|
|
6754
|
+ src in (2^31, 2^32): src' = src - 2^31 ∈ (0, 2^31), positive.
|
|
|
6755
|
+ CVTSI2SD gives src - 2^31. + 2^31.0 → src ✓
|
|
|
6756
|
+
|
|
|
6757
|
+The constant 2^31 is materialised without a memory load: 0x4F000000
|
|
|
6758
|
+is the IEEE 754 float32 bit-pattern for 2^31; a MOVD + CVTSS2SD
|
|
|
6759
|
+gives the exact float64 value.
|
|
|
6760
|
+
|
|
|
6761
|
+Note [Word-to-float32 conversion on i386]
|
|
|
6762
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
6763
|
+On 32-bit x86, StgWord is 32 bits. CVTSI2SS converts a *signed*
|
|
|
6764
|
+32-bit integer, so values >= 2^31 would convert incorrectly.
|
|
|
6765
|
+
|
|
|
6766
|
+We split the 32-bit unsigned value into its high and low 16-bit halves
|
|
|
6767
|
+and convert each separately:
|
|
|
6768
|
+ result = float32(high16) × 65536.0 + float32(low16)
|
|
|
6769
|
+
|
|
|
6770
|
+Both halves are in [0, 65535], within float32's exact integer range
|
|
|
6771
|
+(24-bit mantissa covers integers up to 2^24 = 16777216 > 65535).
|
|
|
6772
|
+Multiplying by 65536.0 = 2^16 is exact (no mantissa bits consumed).
|
|
|
6773
|
+The final addition rounds to the nearest float32, matching a direct
|
|
|
6774
|
+uint32→float32 conversion.
|
|
|
6775
|
+
|
|
|
6776
|
+The constant 65536.0 (= 0x47800000 in float32 bit-pattern) is loaded
|
|
|
6777
|
+via a MOV + MOVD, avoiding a memory load.
|
|
|
6778
|
+-}
|
|
|
6779
|
+
|
|
6717
|
6780
|
genWordToFloat :: BlockId -> Width -> CmmFormal -> CmmActual -> NatM InstrBlock
|
|
6718
|
|
-genWordToFloat bid width dst src =
|
|
6719
|
|
- -- TODO: generate assembly instead
|
|
6720
|
|
- genPrimCCall bid (word2FloatLabel width) [dst] [src]
|
|
|
6781
|
+genWordToFloat bid width dst src = do
|
|
|
6782
|
+ is32Bit <- is32BitPlatform
|
|
|
6783
|
+ platform <- getPlatform
|
|
|
6784
|
+
|
|
|
6785
|
+ let srcFormat = intFormat $ cmmExprWidth platform src
|
|
|
6786
|
+ let dst_r = getLocalRegReg dst
|
|
|
6787
|
+ let conv = case width of
|
|
|
6788
|
+ W64 -> CVTSI2SD
|
|
|
6789
|
+ W32 -> CVTSI2SS
|
|
|
6790
|
+ _ -> pprPanic "genWordToFloat: unsupported width" (ppr width)
|
|
|
6791
|
+ let dstFormat = floatFormat width
|
|
|
6792
|
+
|
|
|
6793
|
+ (src_r, code_src) <- getSomeReg src
|
|
|
6794
|
+
|
|
|
6795
|
+ if is32Bit
|
|
|
6796
|
+ then case (srcFormat, width) of
|
|
|
6797
|
+ (II32, W64) -> do
|
|
|
6798
|
+ -- See Note [Word-to-float64 conversion on i386]
|
|
|
6799
|
+ cst_r <- getNewRegNat srcFormat
|
|
|
6800
|
+ cst_v <- getNewRegNat dstFormat
|
|
|
6801
|
+ flip_r <- getNewRegNat srcFormat
|
|
|
6802
|
+ return $ code_src `appOL` toOL
|
|
|
6803
|
+ [ MOV srcFormat (OpImm (ImmInt 0x4F000000)) (OpReg cst_r) -- load the constant
|
|
|
6804
|
+ , MOVD srcFormat (floatFormat W32) (OpReg cst_r) (OpReg cst_v)
|
|
|
6805
|
+ , CVTSS2SD cst_v cst_v
|
|
|
6806
|
+ , MOV srcFormat (OpReg src_r) (OpReg flip_r) -- copy src (modified below)
|
|
|
6807
|
+ , ADD srcFormat (OpImm $ ImmInteger 0x80000000) (OpReg flip_r) -- flip_r = flip MSB(src)
|
|
|
6808
|
+ -- XOR dst_r with itself to avoid a false dependency: CVTSI2SD
|
|
|
6809
|
+ -- (SSE2) only writes the lower 64 bits of the destination XMM
|
|
|
6810
|
+ -- register, leaving the upper bits unchanged. That creates a
|
|
|
6811
|
+ -- dependency on the old value of dst_r. Zeroing it first breaks
|
|
|
6812
|
+ -- the dependency chain.
|
|
|
6813
|
+ , XOR dstFormat (OpReg dst_r) (OpReg dst_r)
|
|
|
6814
|
+ , conv srcFormat (OpReg flip_r) dst_r
|
|
|
6815
|
+ , ADD dstFormat (OpReg cst_v) (OpReg dst_r) -- +2147483648.0
|
|
|
6816
|
+ ]
|
|
|
6817
|
+ (II32, W32) -> do
|
|
|
6818
|
+ -- See Note [Word-to-float32 conversion on i386]
|
|
|
6819
|
+ tmp_v <- getNewRegNat dstFormat
|
|
|
6820
|
+ cst_v <- getNewRegNat dstFormat
|
|
|
6821
|
+ cst_r <- getNewRegNat srcFormat
|
|
|
6822
|
+ high_r <- getNewRegNat srcFormat
|
|
|
6823
|
+ low_r <- getNewRegNat srcFormat
|
|
|
6824
|
+ return $ code_src `appOL` toOL
|
|
|
6825
|
+ [ MOV srcFormat (OpImm (ImmInt 0x47800000)) (OpReg cst_r) -- load the constant
|
|
|
6826
|
+ , MOVD srcFormat dstFormat (OpReg cst_r) (OpReg cst_v)
|
|
|
6827
|
+ , MOVZxL II16 (OpReg src_r) (OpReg low_r) -- low_r = low 16 bits
|
|
|
6828
|
+ , MOV srcFormat (OpReg src_r) (OpReg high_r) -- copy src (modified below)
|
|
|
6829
|
+ , SHR srcFormat (OpImm $ ImmInt 16) (OpReg high_r) -- high_r = high 16 bits
|
|
|
6830
|
+ , conv srcFormat (OpReg high_r) dst_r -- dst_r = float(high)
|
|
|
6831
|
+ , MUL dstFormat (OpReg cst_v) (OpReg dst_r) -- dst_r = float(high) * 65536.0
|
|
|
6832
|
+ -- XOR tmp_v to avoid a false dependency on its previous value
|
|
|
6833
|
+ -- before the CVTSI2SS below (same reasoning as in the W64 case).
|
|
|
6834
|
+ , XOR dstFormat (OpReg tmp_v) (OpReg tmp_v)
|
|
|
6835
|
+ , conv srcFormat (OpReg low_r) tmp_v -- tmp_v = float(low)
|
|
|
6836
|
+ , ADD dstFormat (OpReg tmp_v) (OpReg dst_r) -- dst_r = float(high)*65536.0 + float(low)
|
|
|
6837
|
+ ]
|
|
|
6838
|
+ _ -> panic ("genWordToFloat: unsupported source operand format: " ++ show srcFormat)
|
|
|
6839
|
+ else do
|
|
|
6840
|
+ -- See Note [Word-to-float conversion on x86-64]
|
|
|
6841
|
+ half_r <- getNewRegNat srcFormat
|
|
|
6842
|
+ round_r <- getNewRegNat srcFormat
|
|
|
6843
|
+
|
|
|
6844
|
+ lblLarge <- getBlockIdNat
|
|
|
6845
|
+ lblSmall <- getBlockIdNat
|
|
|
6846
|
+ lblAfter <- getBlockIdNat
|
|
|
6847
|
+
|
|
|
6848
|
+ -- We're building a diamond CFG:
|
|
|
6849
|
+ -- bid -> lblSmall -> lblAfter -> origSucc
|
|
|
6850
|
+ -- \-> lblLarge ->/
|
|
|
6851
|
+ -- addImmediateSuccessorNat moves bid's original successor to lblAfter,
|
|
|
6852
|
+ -- then we fix up the other edges.
|
|
|
6853
|
+ addImmediateSuccessorNat bid lblAfter
|
|
|
6854
|
+ -- Small values (MSB clear, i.e. < 2^63) are assumed more common in
|
|
|
6855
|
+ -- practice, hence the higher weight on the lblSmall edge.
|
|
|
6856
|
+ updateCfgNat ( addWeightEdge bid lblSmall 100
|
|
|
6857
|
+ . addWeightEdge bid lblLarge 50
|
|
|
6858
|
+ . addWeightEdge lblSmall lblAfter 1
|
|
|
6859
|
+ . addWeightEdge lblLarge lblAfter 1
|
|
|
6860
|
+ . delEdge bid lblAfter )
|
|
|
6861
|
+
|
|
|
6862
|
+ return $ appOL (code_src)
|
|
|
6863
|
+ $ toOL
|
|
|
6864
|
+ [ TEST srcFormat (OpReg src_r) (OpReg src_r)
|
|
|
6865
|
+ , JXX NEG lblLarge
|
|
|
6866
|
+ -- Adding this label to allow optimizations to either invert condition or just eliminate
|
|
|
6867
|
+ , JXX ALWAYS lblSmall
|
|
|
6868
|
+ , NEWBLOCK lblSmall
|
|
|
6869
|
+ , conv srcFormat (OpReg src_r) dst_r -- direct conversion for src < 2^63
|
|
|
6870
|
+ , JXX ALWAYS lblAfter
|
|
|
6871
|
+ , NEWBLOCK lblLarge
|
|
|
6872
|
+ -- Halve src, preserving the LSB as a round bit, then convert and double.
|
|
|
6873
|
+ , MOV srcFormat (OpReg src_r) (OpReg half_r)
|
|
|
6874
|
+ , SHR srcFormat (OpImm $ ImmInt 1) (OpReg half_r) -- half_r = src >> 1
|
|
|
6875
|
+ , MOV srcFormat (OpReg src_r) (OpReg round_r) -- copy src (modified below)
|
|
|
6876
|
+ , AND srcFormat (OpImm $ ImmInt 1) (OpReg round_r) -- round_r = src & 1 (round bit)
|
|
|
6877
|
+ , OR srcFormat (OpReg round_r) (OpReg half_r) -- half_r = (src >> 1) | (src & 1)
|
|
|
6878
|
+ , conv srcFormat (OpReg half_r) dst_r
|
|
|
6879
|
+ , ADD dstFormat (OpReg dst_r) (OpReg dst_r) -- double the result
|
|
|
6880
|
+ , JXX ALWAYS lblAfter
|
|
|
6881
|
+ , NEWBLOCK lblAfter
|
|
|
6882
|
+ ]
|
|
6721
|
6883
|
|
|
6722
|
6884
|
genAtomicRead :: Width -> MemoryOrdering -> LocalReg -> CmmExpr -> NatM InstrBlock
|
|
6723
|
6885
|
genAtomicRead width _mord dst addr = do
|