@@ -230,23 +230,22 @@ genCall t@(PrimTarget (MO_Prefetch_Data localityInt)) [] args
230
230
statement $ Expr $ Call StdCall fptr (argVars' ++ argSuffix) []
231
231
| otherwise = panic $ " prefetch locality level integer must be between 0 and 3, given: " ++ (show localityInt)
232
232
233
- -- Handle PopCnt, Clz, Ctz, and BSwap that need to only convert arg
234
- -- and return types
235
- genCall t@ (PrimTarget (MO_PopCnt w)) dsts args =
236
- genCallSimpleCast w t dsts args
237
-
238
- genCall t@ (PrimTarget (MO_Pdep w)) dsts args =
239
- genCallSimpleCast2 w t dsts args
240
- genCall t@ (PrimTarget (MO_Pext w)) dsts args =
241
- genCallSimpleCast2 w t dsts args
242
- genCall t@ (PrimTarget (MO_Clz w)) dsts args =
243
- genCallSimpleCast w t dsts args
244
- genCall t@ (PrimTarget (MO_Ctz w)) dsts args =
245
- genCallSimpleCast w t dsts args
246
- genCall t@ (PrimTarget (MO_BSwap w)) dsts args =
247
- genCallSimpleCast w t dsts args
248
- genCall t@ (PrimTarget (MO_BRev w)) dsts args =
249
- genCallSimpleCast w t dsts args
233
+ -- Handle Clz, Ctz, BRev, BSwap, Pdep, Pext, and PopCnt that need to only
234
+ -- convert arg and return types
235
+ genCall (PrimTarget op@ (MO_Clz w)) [dst] args =
236
+ genCallSimpleCast w op dst args
237
+ genCall (PrimTarget op@ (MO_Ctz w)) [dst] args =
238
+ genCallSimpleCast w op dst args
239
+ genCall (PrimTarget op@ (MO_BRev w)) [dst] args =
240
+ genCallSimpleCast w op dst args
241
+ genCall (PrimTarget op@ (MO_BSwap w)) [dst] args =
242
+ genCallSimpleCast w op dst args
243
+ genCall (PrimTarget op@ (MO_Pdep w)) [dst] args =
244
+ genCallSimpleCast w op dst args
245
+ genCall (PrimTarget op@ (MO_Pext w)) [dst] args =
246
+ genCallSimpleCast w op dst args
247
+ genCall (PrimTarget op@ (MO_PopCnt w)) [dst] args =
248
+ genCallSimpleCast w op dst args
250
249
251
250
genCall (PrimTarget (MO_AtomicRMW width amop)) [dst] [addr, n] = runStmtsDecls $ do
252
251
addrVar <- exprToVarW addr
@@ -640,63 +639,28 @@ genCallExtract _ _ _ _ =
640
639
-- since GHC only really has i32 and i64 types and things like Word8 are backed
641
640
-- by an i32 and just present a logical i8 range. So we must handle conversions
642
641
-- from i32 to i8 explicitly as LLVM is strict about types.
643
- genCallSimpleCast :: Width -> ForeignTarget -> [CmmFormal ] -> [CmmActual ]
644
- -> LlvmM StmtData
645
- genCallSimpleCast w t@ (PrimTarget op) [dst] args = do
646
- let width = widthToLlvmInt w
647
- dstTy = cmmToLlvmType $ localRegType dst
648
-
649
- fname <- cmmPrimOpFunctions op
650
- (fptr, _, top3) <- getInstrinct fname width [width]
651
-
652
- (dstV, _dst_ty) <- getCmmReg (CmmLocal dst)
653
-
654
- let (_, arg_hints) = foreignTargetHints t
655
- let args_hints = zip args arg_hints
656
- (argsV, stmts2, top2) <- arg_vars args_hints ([] , nilOL, [] )
657
- (argsV', stmts4) <- castVars Signed $ zip argsV [width]
658
- (retV, s1) <- doExpr width $ Call StdCall fptr argsV' []
659
- (retVs', stmts5) <- castVars (cmmPrimOpRetValSignage op) [(retV,dstTy)]
660
- let retV' = singletonPanic " genCallSimpleCast" retVs'
661
- let s2 = Store retV' dstV Nothing []
662
-
663
- let stmts = stmts2 `appOL` stmts4 `snocOL`
664
- s1 `appOL` stmts5 `snocOL` s2
665
- return (stmts, top2 ++ top3)
666
- genCallSimpleCast _ _ dsts _ =
667
- panic (" genCallSimpleCast: " ++ show (length dsts) ++ " dsts" )
668
-
669
- -- Handle simple function call that only need simple type casting, of the form:
670
- -- truncate arg >>= \a -> call(a) >>= zext
671
- --
672
- -- since GHC only really has i32 and i64 types and things like Word8 are backed
673
- -- by an i32 and just present a logical i8 range. So we must handle conversions
674
- -- from i32 to i8 explicitly as LLVM is strict about types.
675
- genCallSimpleCast2 :: Width -> ForeignTarget -> [CmmFormal ] -> [CmmActual ]
676
- -> LlvmM StmtData
677
- genCallSimpleCast2 w t@ (PrimTarget op) [dst] args = do
678
- let width = widthToLlvmInt w
679
- dstTy = cmmToLlvmType $ localRegType dst
680
-
681
- fname <- cmmPrimOpFunctions op
682
- (fptr, _, top3) <- getInstrinct fname width (const width <$> args)
683
-
684
- (dstV, _dst_ty) <- getCmmReg (CmmLocal dst)
685
-
686
- let (_, arg_hints) = foreignTargetHints t
687
- let args_hints = zip args arg_hints
688
- (argsV, stmts2, top2) <- arg_vars args_hints ([] , nilOL, [] )
689
- (argsV', stmts4) <- castVars Signed $ zip argsV (const width <$> argsV)
690
- (retV, s1) <- doExpr width $ Call StdCall fptr argsV' []
691
- (retVs', stmts5) <- castVars (cmmPrimOpRetValSignage op) [(retV,dstTy)]
692
- let retV' = singletonPanic " genCallSimpleCast2" retVs'
693
- let s2 = Store retV' dstV Nothing []
642
+ genCallSimpleCast :: Width -> CallishMachOp -> CmmFormal -> [CmmActual ]
643
+ -> LlvmM StmtData
644
+ genCallSimpleCast specW op dst args = do
645
+ let width = widthToLlvmInt specW
646
+ argsW = const width <$> args
647
+ dstType = cmmToLlvmType $ localRegType dst
648
+ signage = cmmPrimOpRetValSignage op
649
+
650
+ fname <- cmmPrimOpFunctions op
651
+ (fptr, _, top3) <- getInstrinct fname width argsW
652
+ (dstV, _dst_ty) <- getCmmReg (CmmLocal dst)
653
+ let (_, arg_hints) = foreignTargetHints $ PrimTarget op
654
+ let args_hints = zip args arg_hints
655
+ (argsV, stmts2, top2) <- arg_vars args_hints ([] , nilOL, [] )
656
+ (argsV', stmts4) <- castVars signage $ zip argsV argsW
657
+ (retV, s1) <- doExpr width $ Call StdCall fptr argsV' []
658
+ (retV', stmts5) <- castVar signage retV dstType
659
+ let s2 = Store retV' dstV Nothing []
694
660
695
661
let stmts = stmts2 `appOL` stmts4 `snocOL`
696
- s1 `appOL ` stmts5 `snocOL` s2
662
+ s1 `snocOL ` stmts5 `snocOL` s2
697
663
return (stmts, top2 ++ top3)
698
- genCallSimpleCast2 _ _ dsts _ =
699
- panic (" genCallSimpleCast2: " ++ show (length dsts) ++ " dsts" )
700
664
701
665
-- | Create a function pointer from a target.
702
666
getFunPtrW :: (LMString -> LlvmType ) -> ForeignTarget
@@ -811,11 +775,47 @@ castVar signage v t | getVarType v == t
811
775
Signed -> LM_Sext
812
776
Unsigned -> LM_Zext
813
777
814
-
815
778
cmmPrimOpRetValSignage :: CallishMachOp -> Signage
816
779
cmmPrimOpRetValSignage mop = case mop of
817
- MO_Pdep _ -> Unsigned
818
- MO_Pext _ -> Unsigned
780
+ -- Some bit-wise operations /must/ always treat the input and output values
781
+ -- as 'Unsigned' in order to return the expected result values when pre/post-
782
+ -- operation bit-width truncation and/or extension occur. For example,
783
+ -- consider the Bit-Reverse operation:
784
+ --
785
+ -- If the result of a Bit-Reverse is treated as signed,
786
+ -- an positive input can result in an negative output, i.e.:
787
+ --
788
+ -- identity(0x03) = 0x03 = 00000011
789
+ -- breverse(0x03) = 0xC0 = 11000000
790
+ --
791
+ -- Now if an extension is performed after the operation to
792
+ -- promote a smaller bit-width value into a larger bit-width
793
+ -- type, it is expected that the /bit-wise/ operations will
794
+ -- not be treated /numerically/ as signed.
795
+ --
796
+ -- To illustrate the difference, consider how a signed extension
797
+ -- for the type i16 to i32 differs for out values above:
798
+ -- ext_zeroed(i32, breverse(0x03)) = 0x00C0 = 0000000011000000
799
+ -- ext_signed(i32, breverse(0x03)) = 0xFFC0 = 1111111111000000
800
+ --
801
+ -- Here we can see that the former output is the expected result
802
+ -- of a bit-wise operation which needs to be promoted to a larger
803
+ -- bit-width type. The latter output is not desirable when we must
804
+ -- constraining a value into a range of i16 within an i32 type.
805
+ --
806
+ -- Hence we always treat the "signage" as unsigned for Bit-Reverse!
807
+ --
808
+ -- The same reasoning applied to Bit-Reverse above applies to the other
809
+ -- bit-wise operations; do not sign extend a possibly negated number!
810
+ MO_BRev _ -> Unsigned
811
+ MO_BSwap _ -> Unsigned
812
+ MO_Clz _ -> Unsigned
813
+ MO_Ctz _ -> Unsigned
814
+ MO_Pdep _ -> Unsigned
815
+ MO_Pext _ -> Unsigned
816
+ MO_PopCnt _ -> Unsigned
817
+
818
+ -- All other cases, default to preserving the numeric sign when extending.
819
819
_ -> Signed
820
820
821
821
-- | Decide what C function to use to implement a CallishMachOp
0 commit comments