Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
-
706d33e3
by Recursion Ninja at 2025-08-15T04:12:12-04:00
-
1cdc6f46
by Cheng Shao at 2025-08-15T04:12:56-04:00
-
5df0d908
by Cheng Shao at 2025-08-18T12:00:36-04:00
-
c5ff5591
by Cheng Shao at 2025-08-18T12:00:37-04:00
17 changed files:
- compiler/GHC/Builtin/primops.txt.pp
- compiler/GHC/CmmToLlvm/CodeGen.hs
- compiler/GHC/StgToByteCode.hs
- hadrian/src/Settings/Builders/RunTest.hs
- libffi-tarballs
- libraries/ghc-internal/cbits/pdep.c
- libraries/ghc-internal/cbits/pext.c
- testsuite/config/ghc
- + testsuite/tests/llvm/should_run/T20645.hs
- + testsuite/tests/llvm/should_run/T20645.stdout
- testsuite/tests/llvm/should_run/all.T
- testsuite/tests/numeric/should_run/foundation.hs
- utils/genprimopcode/Lexer.x
- utils/genprimopcode/Main.hs
- utils/genprimopcode/Parser.y
- utils/genprimopcode/ParserM.hs
- utils/genprimopcode/Syntax.hs
Changes:
... | ... | @@ -148,6 +148,7 @@ defaults |
148 | 148 | vector = []
|
149 | 149 | deprecated_msg = {} -- A non-empty message indicates deprecation
|
150 | 150 | div_like = False -- Second argument expected to be non zero - used for tests
|
151 | + defined_bits = Nothing -- The number of bits the operation is defined for (if not all bits)
|
|
151 | 152 | |
152 | 153 | -- Note [When do out-of-line primops go in primops.txt.pp]
|
153 | 154 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
... | ... | @@ -1065,8 +1066,10 @@ primop CtzOp "ctz#" GenPrimOp Word# -> Word# |
1065 | 1066 | |
1066 | 1067 | primop BSwap16Op "byteSwap16#" GenPrimOp Word# -> Word#
|
1067 | 1068 | {Swap bytes in the lower 16 bits of a word. The higher bytes are undefined. }
|
1069 | + with defined_bits = 16
|
|
1068 | 1070 | primop BSwap32Op "byteSwap32#" GenPrimOp Word# -> Word#
|
1069 | 1071 | {Swap bytes in the lower 32 bits of a word. The higher bytes are undefined. }
|
1072 | + with defined_bits = 32
|
|
1070 | 1073 | primop BSwap64Op "byteSwap64#" GenPrimOp Word64# -> Word64#
|
1071 | 1074 | {Swap bytes in a 64 bits of a word.}
|
1072 | 1075 | primop BSwapOp "byteSwap#" GenPrimOp Word# -> Word#
|
... | ... | @@ -1074,10 +1077,13 @@ primop BSwapOp "byteSwap#" GenPrimOp Word# -> Word# |
1074 | 1077 | |
1075 | 1078 | primop BRev8Op "bitReverse8#" GenPrimOp Word# -> Word#
|
1076 | 1079 | {Reverse the order of the bits in a 8-bit word.}
|
1080 | + with defined_bits = 8
|
|
1077 | 1081 | primop BRev16Op "bitReverse16#" GenPrimOp Word# -> Word#
|
1078 | 1082 | {Reverse the order of the bits in a 16-bit word.}
|
1083 | + with defined_bits = 16
|
|
1079 | 1084 | primop BRev32Op "bitReverse32#" GenPrimOp Word# -> Word#
|
1080 | 1085 | {Reverse the order of the bits in a 32-bit word.}
|
1086 | + with defined_bits = 32
|
|
1081 | 1087 | primop BRev64Op "bitReverse64#" GenPrimOp Word64# -> Word64#
|
1082 | 1088 | {Reverse the order of the bits in a 64-bit word.}
|
1083 | 1089 | primop BRevOp "bitReverse#" GenPrimOp Word# -> Word#
|
... | ... | @@ -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
|
... | ... | @@ -84,11 +84,11 @@ import Data.Coerce (coerce) |
84 | 84 | #if MIN_VERSION_rts(1,0,3)
|
85 | 85 | import qualified Data.ByteString.Char8 as BS
|
86 | 86 | #endif
|
87 | -import Data.Map (Map)
|
|
88 | 87 | import Data.IntMap (IntMap)
|
89 | 88 | import qualified Data.Map as Map
|
90 | 89 | import qualified Data.IntMap as IntMap
|
91 | -import qualified GHC.Data.FiniteMap as Map
|
|
90 | +import GHC.Types.Unique.Map (UniqMap)
|
|
91 | +import qualified GHC.Types.Unique.Map as UniqMap
|
|
92 | 92 | import Data.Ord
|
93 | 93 | import Data.Either ( partitionEithers )
|
94 | 94 | |
... | ... | @@ -209,7 +209,7 @@ type StackDepth = ByteOff |
209 | 209 | |
210 | 210 | -- | Maps Ids to their stack depth. This allows us to avoid having to mess with
|
211 | 211 | -- it after each push/pop.
|
212 | -type BCEnv = Map Id StackDepth -- To find vars on the stack
|
|
212 | +type BCEnv = UniqMap Id StackDepth -- To find vars on the stack
|
|
213 | 213 | |
214 | 214 | {-
|
215 | 215 | ppBCEnv :: BCEnv -> SDoc
|
... | ... | @@ -379,7 +379,7 @@ schemeR_wrk fvs nm original_body (args, body) |
379 | 379 | sum_szsb_args = sum szsb_args
|
380 | 380 | -- Make a stack offset for each argument or free var -- they should
|
381 | 381 | -- appear contiguous in the stack, in order.
|
382 | - p_init = Map.fromList (zip all_args (mkStackOffsets 0 szsb_args))
|
|
382 | + p_init = UniqMap.listToUniqMap (zip all_args (mkStackOffsets 0 szsb_args))
|
|
383 | 383 | |
384 | 384 | -- make the arg bitmap
|
385 | 385 | bits = argBits platform (reverse (map (idArgRep platform) all_args))
|
... | ... | @@ -442,7 +442,7 @@ fvsToEnv :: BCEnv -> CgStgRhs -> [Id] |
442 | 442 | -- it, have to agree about this layout
|
443 | 443 | |
444 | 444 | fvsToEnv p rhs = [v | v <- dVarSetElems $ freeVarsOfRhs rhs,
|
445 | - v `Map.member` p]
|
|
445 | + v `UniqMap.elemUniqMap` p]
|
|
446 | 446 | |
447 | 447 | -- -----------------------------------------------------------------------------
|
448 | 448 | -- schemeE
|
... | ... | @@ -533,7 +533,7 @@ schemeE d s p (StgLet _xlet |
533 | 533 | alloc_code <- mkConAppCode d s p data_con args
|
534 | 534 | platform <- targetPlatform <$> getDynFlags
|
535 | 535 | let !d2 = d + wordSize platform
|
536 | - body_code <- schemeE d2 s (Map.insert x d2 p) body
|
|
536 | + body_code <- schemeE d2 s (UniqMap.addToUniqMap p x d2) body
|
|
537 | 537 | return (alloc_code `appOL` body_code)
|
538 | 538 | -- General case for let. Generates correct, if inefficient, code in
|
539 | 539 | -- all situations.
|
... | ... | @@ -557,7 +557,7 @@ schemeE d s p (StgLet _ext binds body) = do |
557 | 557 | -- after the closures have been allocated in the heap (but not
|
558 | 558 | -- filled in), and pointers to them parked on the stack.
|
559 | 559 | offsets = mkStackOffsets d (genericReplicate n_binds (wordSize platform))
|
560 | - p' = Map.insertList (zipEqual xs offsets) p
|
|
560 | + p' = UniqMap.addListToUniqMap p $ zipEqual xs offsets
|
|
561 | 561 | d' = d + wordsToBytes platform n_binds
|
562 | 562 | |
563 | 563 | -- ToDo: don't build thunks for things with no free variables
|
... | ... | @@ -1180,7 +1180,7 @@ doCase d s p scrut bndr alts |
1180 | 1180 | |
1181 | 1181 | -- Env in which to compile the alts, not including
|
1182 | 1182 | -- any vars bound by the alts themselves
|
1183 | - p_alts = Map.insert bndr d_bndr p
|
|
1183 | + p_alts = UniqMap.addToUniqMap p bndr d_bndr
|
|
1184 | 1184 | |
1185 | 1185 | bndr_ty = idType bndr
|
1186 | 1186 | isAlgCase = isAlgType bndr_ty
|
... | ... | @@ -1208,12 +1208,11 @@ doCase d s p scrut bndr alts |
1208 | 1208 | |
1209 | 1209 | stack_bot = d_alts
|
1210 | 1210 | |
1211 | - p' = Map.insertList
|
|
1211 | + p' = UniqMap.addListToUniqMap p_alts
|
|
1212 | 1212 | [ (arg, tuple_start -
|
1213 | 1213 | wordsToBytes platform (nativeCallSize call_info) +
|
1214 | 1214 | offset)
|
1215 | 1215 | | (NonVoid arg, offset) <- args_offsets]
|
1216 | - p_alts
|
|
1217 | 1216 | in do
|
1218 | 1217 | rhs_code <- schemeE stack_bot s p' rhs
|
1219 | 1218 | return (NoDiscr, rhs_code)
|
... | ... | @@ -1227,10 +1226,9 @@ doCase d s p scrut bndr alts |
1227 | 1226 | stack_bot = d_alts + wordsToBytes platform size
|
1228 | 1227 | |
1229 | 1228 | -- convert offsets from Sp into offsets into the virtual stack
|
1230 | - p' = Map.insertList
|
|
1229 | + p' = UniqMap.addListToUniqMap p_alts
|
|
1231 | 1230 | [ (arg, stack_bot - ByteOff offset)
|
1232 | 1231 | | (NonVoid arg, offset) <- args_offsets ]
|
1233 | - p_alts
|
|
1234 | 1232 | |
1235 | 1233 | in do
|
1236 | 1234 | massert isAlgCase
|
... | ... | @@ -1312,12 +1310,13 @@ doCase d s p scrut bndr alts |
1312 | 1310 | -- NB: unboxed tuple cases bind the scrut binder to the same offset
|
1313 | 1311 | -- as one of the alt binders, so we have to remove any duplicates here:
|
1314 | 1312 | -- 'toAscList' takes care of sorting the result, which was previously done after the application of 'filter'.
|
1315 | - rel_slots = IntSet.toAscList $ IntSet.fromList $ Map.elems $ Map.mapMaybeWithKey spread p
|
|
1316 | - spread id offset | isUnboxedTupleType (idType id) ||
|
|
1317 | - isUnboxedSumType (idType id) = Nothing
|
|
1318 | - | isFollowableArg (idArgRep platform id) = Just (fromIntegral rel_offset)
|
|
1319 | - | otherwise = Nothing
|
|
1320 | - where rel_offset = bytesToWords platform (d - offset)
|
|
1313 | + rel_slots = IntSet.toAscList $ UniqMap.nonDetFoldUniqMap go IntSet.empty p
|
|
1314 | + go (var, offset) !acc
|
|
1315 | + | isUnboxedTupleType (idType var) || isUnboxedSumType (idType var)
|
|
1316 | + = acc
|
|
1317 | + | isFollowableArg (idArgRep platform var)
|
|
1318 | + = fromIntegral (bytesToWords platform (d - offset)) `IntSet.insert` acc
|
|
1319 | + | otherwise = acc
|
|
1321 | 1320 | |
1322 | 1321 | bitmap = intsToReverseBitmap platform bitmap_size' pointers
|
1323 | 1322 | |
... | ... | @@ -2546,7 +2545,7 @@ instance Outputable Discr where |
2546 | 2545 | |
2547 | 2546 | |
2548 | 2547 | lookupBCEnv_maybe :: Id -> BCEnv -> Maybe ByteOff
|
2549 | -lookupBCEnv_maybe = Map.lookup
|
|
2548 | +lookupBCEnv_maybe v env = UniqMap.lookupUniqMap env v
|
|
2550 | 2549 | |
2551 | 2550 | idSizeW :: Platform -> Id -> WordOff
|
2552 | 2551 | idSizeW platform = WordOff . argRepSizeW platform . idArgRep platform
|
... | ... | @@ -86,6 +86,16 @@ data TestCompilerArgs = TestCompilerArgs{ |
86 | 86 | , pkgConfCacheFile :: FilePath }
|
87 | 87 | deriving (Eq, Show)
|
88 | 88 | |
89 | +-- | Some archs like wasm32/js used to report have_llvm=True because
|
|
90 | +-- they are based on LLVM related toolchains like wasi-sdk/emscripten,
|
|
91 | +-- but these targets don't really support the LLVM backend, and the
|
|
92 | +-- optllvm test way doesn't work. We used to special-case wasm32/js to
|
|
93 | +-- avoid auto-adding optllvm way in testsuite/config/ghc, but this is
|
|
94 | +-- still problematic if someone writes a new LLVM-related test and
|
|
95 | +-- uses something like when(have_llvm(), extra_ways(["optllvm"])). So
|
|
96 | +-- better just enforce have_llvm=False for these targets here.
|
|
97 | +allowHaveLLVM :: String -> Bool
|
|
98 | +allowHaveLLVM = not . (`elem` ["wasm32", "javascript"])
|
|
89 | 99 | |
90 | 100 | -- | If the tree is in-compiler then we already know how we will build it so
|
91 | 101 | -- don't build anything in order to work out what we will build.
|
... | ... | @@ -129,7 +139,7 @@ inTreeCompilerArgs stg = do |
129 | 139 | |
130 | 140 | llc_cmd <- queryTargetTarget tgtLlc
|
131 | 141 | llvm_as_cmd <- queryTargetTarget tgtLlvmAs
|
132 | - let have_llvm = all isJust [llc_cmd, llvm_as_cmd]
|
|
142 | + let have_llvm = allowHaveLLVM arch && all isJust [llc_cmd, llvm_as_cmd]
|
|
133 | 143 | |
134 | 144 | top <- topDirectory
|
135 | 145 | |
... | ... | @@ -176,7 +186,7 @@ outOfTreeCompilerArgs = do |
176 | 186 | let debugged = "debug" `isInfixOf` rtsWay
|
177 | 187 | |
178 | 188 | llc_cmd <- getTestSetting TestLLC
|
179 | - have_llvm <- liftIO (isJust <$> findExecutable llc_cmd)
|
|
189 | + have_llvm <- (allowHaveLLVM arch &&) <$> liftIO (isJust <$> findExecutable llc_cmd)
|
|
180 | 190 | profiled <- getBooleanSetting TestGhcProfiled
|
181 | 191 | |
182 | 192 | pkgConfCacheFile <- getTestSetting TestGhcPackageDb <&> (</> "package.cache")
|
1 | -Subproject commit a5480d7e7f86a9bb5b44dd1156a92f69f7c185ec |
|
1 | +Subproject commit 7c51059557b68d29820a0a87cebfa6fe73c8adf5 |
... | ... | @@ -24,20 +24,23 @@ hs_pdep64(StgWord64 src, StgWord64 mask) |
24 | 24 | return result;
|
25 | 25 | }
|
26 | 26 | |
27 | +// When dealing with values of bit-width shorter than uint64_t, ensure to
|
|
28 | +// cast the return value to correctly truncate the undefined upper bits.
|
|
29 | +// This is *VERY* important when GHC is using the LLVM backend!
|
|
27 | 30 | StgWord
|
28 | 31 | hs_pdep32(StgWord src, StgWord mask)
|
29 | 32 | {
|
30 | - return hs_pdep64(src, mask);
|
|
33 | + return (StgWord) ((StgWord32) hs_pdep64(src, mask));
|
|
31 | 34 | }
|
32 | 35 | |
33 | 36 | StgWord
|
34 | 37 | hs_pdep16(StgWord src, StgWord mask)
|
35 | 38 | {
|
36 | - return hs_pdep64(src, mask);
|
|
39 | + return (StgWord) ((StgWord16) hs_pdep64(src, mask));
|
|
37 | 40 | }
|
38 | 41 | |
39 | 42 | StgWord
|
40 | 43 | hs_pdep8(StgWord src, StgWord mask)
|
41 | 44 | {
|
42 | - return hs_pdep64(src, mask);
|
|
45 | + return (StgWord) ((StgWord8) hs_pdep64(src, mask));
|
|
43 | 46 | } |
1 | 1 | #include "Rts.h"
|
2 | 2 | #include "MachDeps.h"
|
3 | 3 | |
4 | -StgWord64
|
|
5 | -hs_pext64(StgWord64 src, StgWord64 mask)
|
|
4 | +static StgWord64
|
|
5 | +hs_pext(const unsigned char bit_width, const StgWord64 src, const StgWord64 mask)
|
|
6 | 6 | {
|
7 | 7 | uint64_t result = 0;
|
8 | 8 | int offset = 0;
|
9 | 9 | |
10 | - for (int bit = 0; bit != sizeof(uint64_t) * 8; ++bit) {
|
|
10 | + for (int bit = 0; bit != bit_width; ++bit) {
|
|
11 | 11 | const uint64_t src_bit = (src >> bit) & 1;
|
12 | 12 | const uint64_t mask_bit = (mask >> bit) & 1;
|
13 | 13 | |
... | ... | @@ -20,20 +20,29 @@ hs_pext64(StgWord64 src, StgWord64 mask) |
20 | 20 | return result;
|
21 | 21 | }
|
22 | 22 | |
23 | +StgWord64
|
|
24 | +hs_pext64(const StgWord64 src, const StgWord64 mask)
|
|
25 | +{
|
|
26 | + return hs_pext(64, src, mask);
|
|
27 | +}
|
|
28 | + |
|
29 | +// When dealing with values of bit-width shorter than uint64_t, ensure to
|
|
30 | +// cast the return value to correctly truncate the undefined upper bits.
|
|
31 | +// This is *VERY* important when GHC is using the LLVM backend!
|
|
23 | 32 | StgWord
|
24 | -hs_pext32(StgWord src, StgWord mask)
|
|
33 | +hs_pext32(const StgWord src, const StgWord mask)
|
|
25 | 34 | {
|
26 | - return hs_pext64(src, mask);
|
|
35 | + return (StgWord) ((StgWord32) hs_pext(32, src, mask));
|
|
27 | 36 | }
|
28 | 37 | |
29 | 38 | StgWord
|
30 | -hs_pext16(StgWord src, StgWord mask)
|
|
39 | +hs_pext16(const StgWord src, const StgWord mask)
|
|
31 | 40 | {
|
32 | - return hs_pext64(src, mask);
|
|
41 | + return (StgWord) ((StgWord16) hs_pext(16, src, mask));
|
|
33 | 42 | }
|
34 | 43 | |
35 | 44 | StgWord
|
36 | -hs_pext8(StgWord src, StgWord mask)
|
|
45 | +hs_pext8(const StgWord src, const StgWord mask)
|
|
37 | 46 | {
|
38 | - return hs_pext64(src, mask);
|
|
47 | + return (StgWord) ((StgWord8) hs_pext(8, src, mask));
|
|
39 | 48 | } |
... | ... | @@ -70,7 +70,7 @@ if windows: |
70 | 70 | config.other_ways += winio_ways
|
71 | 71 | |
72 | 72 | # LLVM
|
73 | -if not config.unregisterised and not config.arch in {"wasm32", "javascript"} and config.have_llvm:
|
|
73 | +if not config.unregisterised and config.have_llvm:
|
|
74 | 74 | config.compile_ways.append('optllvm')
|
75 | 75 | config.run_ways.append('optllvm')
|
76 | 76 |
1 | +-- Minimal reproducer for https://gitlab.haskell.org/ghc/ghc/-/issues/20645
|
|
2 | +{-# LANGUAGE MagicHash #-}
|
|
3 | +{-# LANGUAGE ExtendedLiterals #-}
|
|
4 | +import GHC.Exts
|
|
5 | +import GHC.Word
|
|
6 | +import Numeric (showHex)
|
|
7 | + |
|
8 | +opaqueInt8# :: Int8# -> Int8#
|
|
9 | +opaqueInt8# x = x
|
|
10 | +{-# OPAQUE opaqueInt8# #-}
|
|
11 | + |
|
12 | +main :: IO ()
|
|
13 | +main = let !x = opaqueInt8# 109#Int8
|
|
14 | + !y = opaqueInt8# 1#Int8
|
|
15 | + in putStrLn $ flip showHex "" (W# ( pext8#
|
|
16 | + (word8ToWord# (int8ToWord8# (0#Int8 `subInt8#` x )))
|
|
17 | + (word8ToWord# (int8ToWord8# (y `subInt8#` 4#Int8)))
|
|
18 | + )) |
1 | +49 |
... | ... | @@ -17,3 +17,4 @@ test('T22487', [normal, normalise_errmsg_fun(ignore_llvm_and_vortex)], compile_a |
17 | 17 | test('T22033', [normal, normalise_errmsg_fun(ignore_llvm_and_vortex)], compile_and_run, [''])
|
18 | 18 | test('T25730', [req_c, unless(arch('x86_64'), skip), normalise_errmsg_fun(ignore_llvm_and_vortex)], compile_and_run, ['T25730C.c'])
|
19 | 19 | # T25730C.c contains Intel instrinsics, so only run this test on x86
|
20 | +test('T20645', [normal, normalise_errmsg_fun(ignore_llvm_and_vortex), when(have_llvm(), extra_ways(["optllvm"]))], compile_and_run, ['']) |
... | ... | @@ -24,6 +24,7 @@ module Main |
24 | 24 | ( main
|
25 | 25 | ) where
|
26 | 26 | |
27 | +import Data.Bits (Bits((.&.), bit))
|
|
27 | 28 | import Data.Word
|
28 | 29 | import Data.Int
|
29 | 30 | import GHC.Natural
|
... | ... | @@ -408,6 +409,33 @@ instance TestPrimop (Word# -> Int# -> Word#) where |
408 | 409 | testPrimop s l r = Property s $ \(uWord -> a1) (uInt -> a2) -> (wWord (l a1 a2)) === wWord (r a1 a2)
|
409 | 410 | -}
|
410 | 411 | |
412 | +-- | A special data-type for representing functions where,
|
|
413 | +-- since only some number of the lower bits are defined,
|
|
414 | +-- testing for strict equality in the undefined upper bits is not appropriate!
|
|
415 | +-- Without using this data-type, false-positive failures will be reported
|
|
416 | +-- when the undefined bit regions do not match, even though the equality of bits
|
|
417 | +-- in this undefined region has no bearing on correctness.
|
|
418 | +data LowerBitsAreDefined =
|
|
419 | + LowerBitsAreDefined
|
|
420 | + { definedLowerWidth :: Word
|
|
421 | + -- ^ The (strictly-non-negative) number of least-significant bits
|
|
422 | + -- for which the attached function is defined.
|
|
423 | + , undefinedBehavior :: (Word# -> Word#)
|
|
424 | + -- ^ Function with undefined behavior for some of its most significant bits.
|
|
425 | + }
|
|
426 | + |
|
427 | +instance TestPrimop LowerBitsAreDefined where
|
|
428 | + testPrimop s l r = Property s $ \ (uWord#-> x0) ->
|
|
429 | + let -- Create a mask to unset all bits in the undefined area,
|
|
430 | + -- leaving set bits only in the area of defined behavior.
|
|
431 | + -- Since the upper bits are undefined,
|
|
432 | + -- if the function defines behavior for the lower N bits,
|
|
433 | + -- then /only/ the lower N bits are preserved,
|
|
434 | + -- and the upper WORDSIZE - N bits are discarded.
|
|
435 | + mask = bit (fromEnum (definedLowerWidth r)) - 1
|
|
436 | + valL = wWord# (undefinedBehavior l x0) .&. mask
|
|
437 | + valR = wWord# (undefinedBehavior r x0) .&. mask
|
|
438 | + in valL === valR
|
|
411 | 439 | |
412 | 440 | twoNonZero :: (a -> a -> b) -> a -> NonZero a -> b
|
413 | 441 | twoNonZero f x (NonZero y) = f x y
|
... | ... | @@ -655,13 +683,13 @@ testPrimops = Group "primop" |
655 | 683 | , testPrimop "ctz32#" Primop.ctz32# Wrapper.ctz32#
|
656 | 684 | , testPrimop "ctz64#" Primop.ctz64# Wrapper.ctz64#
|
657 | 685 | , testPrimop "ctz#" Primop.ctz# Wrapper.ctz#
|
658 | - , testPrimop "byteSwap16#" Primop.byteSwap16# Wrapper.byteSwap16#
|
|
659 | - , testPrimop "byteSwap32#" Primop.byteSwap32# Wrapper.byteSwap32#
|
|
686 | + , testPrimop "byteSwap16#" (16 `LowerBitsAreDefined` Primop.byteSwap16#) (16 `LowerBitsAreDefined` Wrapper.byteSwap16#)
|
|
687 | + , testPrimop "byteSwap32#" (32 `LowerBitsAreDefined` Primop.byteSwap32#) (32 `LowerBitsAreDefined` Wrapper.byteSwap32#)
|
|
660 | 688 | , testPrimop "byteSwap64#" Primop.byteSwap64# Wrapper.byteSwap64#
|
661 | 689 | , testPrimop "byteSwap#" Primop.byteSwap# Wrapper.byteSwap#
|
662 | - , testPrimop "bitReverse8#" Primop.bitReverse8# Wrapper.bitReverse8#
|
|
663 | - , testPrimop "bitReverse16#" Primop.bitReverse16# Wrapper.bitReverse16#
|
|
664 | - , testPrimop "bitReverse32#" Primop.bitReverse32# Wrapper.bitReverse32#
|
|
690 | + , testPrimop "bitReverse8#" (8 `LowerBitsAreDefined` Primop.bitReverse8#) (8 `LowerBitsAreDefined` Wrapper.bitReverse8#)
|
|
691 | + , testPrimop "bitReverse16#" (16 `LowerBitsAreDefined` Primop.bitReverse16#) (16 `LowerBitsAreDefined` Wrapper.bitReverse16#)
|
|
692 | + , testPrimop "bitReverse32#" (32 `LowerBitsAreDefined` Primop.bitReverse32#) (32 `LowerBitsAreDefined` Wrapper.bitReverse32#)
|
|
665 | 693 | , testPrimop "bitReverse64#" Primop.bitReverse64# Wrapper.bitReverse64#
|
666 | 694 | , testPrimop "bitReverse#" Primop.bitReverse# Wrapper.bitReverse#
|
667 | 695 | , testPrimop "narrow8Int#" Primop.narrow8Int# Wrapper.narrow8Int#
|
... | ... | @@ -56,6 +56,7 @@ words :- |
56 | 56 | <0> "CanFail" { mkT TCanFail }
|
57 | 57 | <0> "ThrowsException" { mkT TThrowsException }
|
58 | 58 | <0> "ReadWriteEffect" { mkT TReadWriteEffect }
|
59 | + <0> "defined_bits" { mkT TDefinedBits }
|
|
59 | 60 | <0> "can_fail_warning" { mkT TCanFailWarnFlag }
|
60 | 61 | <0> "DoNotWarnCanFail" { mkT TDoNotWarnCanFail }
|
61 | 62 | <0> "WarnIfEffectIsCanFail" { mkT TWarnIfEffectIsCanFail }
|
... | ... | @@ -10,6 +10,7 @@ module Main where |
10 | 10 | import Parser
|
11 | 11 | import Syntax
|
12 | 12 | |
13 | +import Control.Applicative (asum)
|
|
13 | 14 | import Data.Char
|
14 | 15 | import Data.List (union, intersperse, intercalate, nub, sort)
|
15 | 16 | import Data.Maybe ( catMaybes, mapMaybe )
|
... | ... | @@ -116,9 +117,15 @@ desugarVectorSpec i = case vecOptions i of |
116 | 117 | main :: IO ()
|
117 | 118 | main = getArgs >>= \args ->
|
118 | 119 | if length args /= 1 || head args `notElem` known_args
|
119 | - then error ("usage: genprimopcode command < primops.txt > ...\n"
|
|
120 | + then error ("Usage: genprimopcode command < primops.txt > ...\n"
|
|
120 | 121 | ++ " where command is one of\n"
|
121 | 122 | ++ unlines (map (" "++) known_args)
|
123 | + ++ unlines
|
|
124 | + [ ""
|
|
125 | + , "Nota Bene: Be sure to manually run primops.txt through the C Pre-Processor"
|
|
126 | + , " before sending the input stream to STDIN, i.e:"
|
|
127 | + , ""
|
|
128 | + , " cpp -P -w primops.txt | genprimopcode command" ]
|
|
122 | 129 | )
|
123 | 130 | else
|
124 | 131 | do hSetEncoding stdin utf8 -- The input file is in UTF-8. Set the encoding explicitly.
|
... | ... | @@ -312,6 +319,7 @@ gen_hs_source (Info defaults entries) = |
312 | 319 | opt (OptionVector _) = ""
|
313 | 320 | opt (OptionFixity mf) = "fixity = " ++ show mf
|
314 | 321 | opt (OptionEffect eff) = "effect = " ++ show eff
|
322 | + opt (OptionDefinedBits bc) = "defined_bits = " ++ show bc
|
|
315 | 323 | opt (OptionCanFailWarnFlag wf) = "can_fail_warning = " ++ show wf
|
316 | 324 | |
317 | 325 | hdr s@(Section {}) = sec s
|
... | ... | @@ -638,6 +646,7 @@ gen_switch_from_attribs attrib_name fn_name (Info defaults entries) |
638 | 646 | getAltRhs (OptionVector _) = "True"
|
639 | 647 | getAltRhs (OptionFixity mf) = show mf
|
640 | 648 | getAltRhs (OptionEffect eff) = show eff
|
649 | + getAltRhs (OptionDefinedBits bc) = show bc
|
|
641 | 650 | getAltRhs (OptionCanFailWarnFlag wf) = show wf
|
642 | 651 | |
643 | 652 | mkAlt po
|
... | ... | @@ -753,7 +762,12 @@ gen_foundation_tests (Info _ entries) |
753 | 762 | = let testPrimOpHow = if is_divLikeOp po
|
754 | 763 | then "testPrimopDivLike"
|
755 | 764 | else "testPrimop"
|
756 | - in Just $ intercalate " " [testPrimOpHow, "\"" ++ poName ++ "\"", wrap "Primop" poName, wrap "Wrapper" poName]
|
|
765 | + qualOp qualification =
|
|
766 | + let qName = wrap qualification poName
|
|
767 | + in case mb_defined_bits po of
|
|
768 | + Nothing -> qName
|
|
769 | + Just bs -> concat ["(", show bs, " `LowerBitsAreDefined` ", qName, ")"]
|
|
770 | + in Just $ intercalate " " [testPrimOpHow, "\"" ++ poName ++ "\"", qualOp "Primop", qualOp "Wrapper"]
|
|
757 | 771 | | otherwise = Nothing
|
758 | 772 | |
759 | 773 | |
... | ... | @@ -771,6 +785,16 @@ gen_foundation_tests (Info _ entries) |
771 | 785 | divableTyCons = ["Int#", "Word#", "Word8#", "Word16#", "Word32#", "Word64#"
|
772 | 786 | ,"Int8#", "Int16#", "Int32#", "Int64#"]
|
773 | 787 | |
788 | + mb_defined_bits :: Entry -> Maybe Word
|
|
789 | + mb_defined_bits op@(PrimOpSpec{}) =
|
|
790 | + let opOpts = opts op
|
|
791 | + getDefBits :: Option -> Maybe Word
|
|
792 | + getDefBits (OptionDefinedBits x) = x
|
|
793 | + getDefBits _ = Nothing
|
|
794 | + in asum $ getDefBits <$> opOpts
|
|
795 | + mb_defined_bits _ = Nothing
|
|
796 | + |
|
797 | + |
|
774 | 798 | ------------------------------------------------------------------
|
775 | 799 | -- Create PrimOpInfo text from PrimOpSpecs -----------------------
|
776 | 800 | ------------------------------------------------------------------
|
... | ... | @@ -50,6 +50,7 @@ import AccessOps |
50 | 50 | CanFail { TCanFail }
|
51 | 51 | ThrowsException { TThrowsException }
|
52 | 52 | ReadWriteEffect { TReadWriteEffect }
|
53 | + defined_bits { TDefinedBits }
|
|
53 | 54 | can_fail_warning { TCanFailWarnFlag }
|
54 | 55 | DoNotWarnCanFail { TDoNotWarnCanFail }
|
55 | 56 | WarnIfEffectIsCanFail { TWarnIfEffectIsCanFail }
|
... | ... | @@ -81,13 +82,14 @@ pOptions : pOption pOptions { $1 : $2 } |
81 | 82 | | {- empty -} { [] }
|
82 | 83 | |
83 | 84 | pOption :: { Option }
|
84 | -pOption : lowerName '=' false { OptionFalse $1 }
|
|
85 | - | lowerName '=' true { OptionTrue $1 }
|
|
86 | - | lowerName '=' pStuffBetweenBraces { OptionString $1 $3 }
|
|
87 | - | lowerName '=' integer { OptionInteger $1 $3 }
|
|
88 | - | vector '=' pVectorTemplate { OptionVector $3 }
|
|
89 | - | fixity '=' pInfix { OptionFixity $3 }
|
|
90 | - | effect '=' pEffect { OptionEffect $3 }
|
|
85 | +pOption : lowerName '=' false { OptionFalse $1 }
|
|
86 | + | lowerName '=' true { OptionTrue $1 }
|
|
87 | + | lowerName '=' pStuffBetweenBraces { OptionString $1 $3 }
|
|
88 | + | lowerName '=' integer { OptionInteger $1 $3 }
|
|
89 | + | vector '=' pVectorTemplate { OptionVector $3 }
|
|
90 | + | fixity '=' pInfix { OptionFixity $3 }
|
|
91 | + | effect '=' pEffect { OptionEffect $3 }
|
|
92 | + | defined_bits '=' pGoodBits { OptionDefinedBits $3 }
|
|
91 | 93 | | can_fail_warning '=' pPrimOpCanFailWarnFlag { OptionCanFailWarnFlag $3 }
|
92 | 94 | |
93 | 95 | pInfix :: { Maybe Fixity }
|
... | ... | @@ -102,6 +104,10 @@ pEffect : NoEffect { NoEffect } |
102 | 104 | | ThrowsException { ThrowsException }
|
103 | 105 | | ReadWriteEffect { ReadWriteEffect }
|
104 | 106 | |
107 | +pGoodBits :: { Maybe Word }
|
|
108 | +pGoodBits : integer { Just $ toEnum $1 }
|
|
109 | + | nothing { Nothing }
|
|
110 | + |
|
105 | 111 | pPrimOpCanFailWarnFlag :: { PrimOpCanFailWarnFlag }
|
106 | 112 | pPrimOpCanFailWarnFlag : DoNotWarnCanFail { DoNotWarnCanFail }
|
107 | 113 | | WarnIfEffectIsCanFail { WarnIfEffectIsCanFail }
|
... | ... | @@ -116,6 +116,7 @@ data Token = TEOF |
116 | 116 | | TCanFail
|
117 | 117 | | TThrowsException
|
118 | 118 | | TReadWriteEffect
|
119 | + | TDefinedBits
|
|
119 | 120 | | TCanFailWarnFlag
|
120 | 121 | | TDoNotWarnCanFail
|
121 | 122 | | TWarnIfEffectIsCanFail
|
... | ... | @@ -76,6 +76,7 @@ data Option |
76 | 76 | | OptionFixity (Maybe Fixity) -- fixity = infix{,l,r} <int> | Nothing
|
77 | 77 | | OptionEffect PrimOpEffect -- effect = NoEffect | DoNotSpeculate | CanFail | ThrowsException | ReadWriteEffect | FallibleReadWriteEffect
|
78 | 78 | | OptionCanFailWarnFlag PrimOpCanFailWarnFlag -- can_fail_warning = DoNotWarnCanFail | WarnIfEffectIsCanFail | YesWarnCanFail
|
79 | + | OptionDefinedBits (Maybe Word) -- defined_bits = Just 16 | Nothing
|
|
79 | 80 | deriving Show
|
80 | 81 | |
81 | 82 | -- categorises primops
|
... | ... | @@ -196,6 +197,7 @@ get_attrib_name (OptionVector _) = "vector" |
196 | 197 | get_attrib_name (OptionFixity _) = "fixity"
|
197 | 198 | get_attrib_name (OptionEffect _) = "effect"
|
198 | 199 | get_attrib_name (OptionCanFailWarnFlag _) = "can_fail_warning"
|
200 | +get_attrib_name (OptionDefinedBits _) = "defined_bits"
|
|
199 | 201 | |
200 | 202 | lookup_attrib :: String -> [Option] -> Maybe Option
|
201 | 203 | lookup_attrib _ [] = Nothing
|