Simon Jakobi pushed to branch wip/sjakobi/T26964 at Glasgow Haskell Compiler / GHC
Commits:
-
e35ce402
by Simon Jakobi at 2026-06-05T02:48:49+02:00
10 changed files:
- compiler/GHC/StgToCmm/Prim.hs
- rts/PrimOps.cmm
- rts/RtsMessages.c
- rts/include/rts/Messages.h
- testsuite/tests/codeGen/should_fail/T26964.stderr
- + testsuite/tests/codeGen/should_fail/T26964Module.hs
- + testsuite/tests/codeGen/should_fail/T26964Module.stderr
- + testsuite/tests/codeGen/should_fail/T26964ModuleA.hs
- testsuite/tests/codeGen/should_fail/T26964b.stderr
- testsuite/tests/codeGen/should_fail/all.T
Changes:
| ... | ... | @@ -3625,10 +3625,12 @@ emitCheckedMemcpyCall dst src n align = do |
| 3625 | 3625 | doCheck platform = do
|
| 3626 | 3626 | name <- fromMaybe "<unknown primop>" <$> getCurrentPrimOpName
|
| 3627 | 3627 | nameLbl <- newStringCLit name
|
| 3628 | + modLbl <- getCurrentModuleCLit
|
|
| 3628 | 3629 | overlapCheckFailed <- getCode $
|
| 3629 | 3630 | emitCCallNeverReturns []
|
| 3630 | 3631 | (mkLblExpr mkMemcpyRangeOverlapLabel)
|
| 3631 | - [ (CmmLit nameLbl, AddrHint) ]
|
|
| 3632 | + [ (CmmLit nameLbl, AddrHint)
|
|
| 3633 | + , (CmmLit modLbl, AddrHint) ]
|
|
| 3632 | 3634 | emit =<< mkCmmIfThen' rangesOverlap overlapCheckFailed (Just False)
|
| 3633 | 3635 | where
|
| 3634 | 3636 | rangesOverlap = (checkDiff dst src `or` checkDiff src dst) `ne` zero
|
| ... | ... | @@ -3740,11 +3742,20 @@ whenCheckBounds a = do |
| 3740 | 3742 | False -> pure ()
|
| 3741 | 3743 | True -> a
|
| 3742 | 3744 | |
| 3745 | +-- | Render the module currently being code-generated as a string literal, for
|
|
| 3746 | +-- use in @-fcheck-prim-bounds@ failure diagnostics.
|
|
| 3747 | +getCurrentModuleCLit :: FCode CmmLit
|
|
| 3748 | +getCurrentModuleCLit = do
|
|
| 3749 | + mod <- getModuleName
|
|
| 3750 | + ctx <- getContext
|
|
| 3751 | + newStringCLit (renderWithContext ctx (ppr mod))
|
|
| 3752 | + |
|
| 3743 | 3753 | -- | Emit a call to the RTS @rtsOutOfBoundsAccess@ bounds-check failure
|
| 3744 | 3754 | -- handler, passing the offending index, the number of accessed elements and
|
| 3745 | 3755 | -- the array size, so that the runtime can produce an informative error
|
| 3746 | 3756 | -- message. The name of the offending primop is read from the code generator
|
| 3747 | --- environment (see 'withCurrentPrimOpName').
|
|
| 3757 | +-- environment (see 'withCurrentPrimOpName'), and the module being compiled is
|
|
| 3758 | +-- read from the 'StgToCmmConfig'.
|
|
| 3748 | 3759 | emitBoundsCheckFailed :: CmmExpr -- ^ accessed index
|
| 3749 | 3760 | -> CmmExpr -- ^ number of accessed elements
|
| 3750 | 3761 | -> CmmExpr -- ^ array size (in elements)
|
| ... | ... | @@ -3752,12 +3763,14 @@ emitBoundsCheckFailed :: CmmExpr -- ^ accessed index |
| 3752 | 3763 | emitBoundsCheckFailed idx count sz = do
|
| 3753 | 3764 | name <- fromMaybe "<unknown primop>" <$> getCurrentPrimOpName
|
| 3754 | 3765 | nameLbl <- newStringCLit name
|
| 3766 | + modLbl <- getCurrentModuleCLit
|
|
| 3755 | 3767 | emitCCallNeverReturns []
|
| 3756 | 3768 | (mkLblExpr mkOutOfBoundsAccessLabel)
|
| 3757 | 3769 | [ (idx, NoHint)
|
| 3758 | 3770 | , (count, NoHint)
|
| 3759 | 3771 | , (sz, NoHint)
|
| 3760 | - , (CmmLit nameLbl, AddrHint) ]
|
|
| 3772 | + , (CmmLit nameLbl, AddrHint)
|
|
| 3773 | + , (CmmLit modLbl, AddrHint) ]
|
|
| 3761 | 3774 | |
| 3762 | 3775 | emitBoundsCheck :: CmmExpr -- ^ accessed index
|
| 3763 | 3776 | -> CmmExpr -- ^ array size (in elements)
|
| ... | ... | @@ -93,7 +93,7 @@ import CLOSURE ghc_hs_iface; |
| 93 | 93 | // the index reported here may be in bytes. count is 1 since these checks cover
|
| 94 | 94 | // a single access.
|
| 95 | 95 | #define ASSERT_IN_BOUNDS(op, ind, sz) \
|
| 96 | - if (ind >= sz) { ccall rtsOutOfBoundsAccess(ind, 1, sz, op); }
|
|
| 96 | + if (ind >= sz) { ccall rtsOutOfBoundsAccess(ind, 1, sz, op, "<RTS>"); }
|
|
| 97 | 97 | #else
|
| 98 | 98 | #define ASSERT_IN_BOUNDS(op, ind, sz)
|
| 99 | 99 | #endif
|
| ... | ... | @@ -352,32 +352,31 @@ rtsBadAlignmentBarf(void) |
| 352 | 352 | }
|
| 353 | 353 | |
| 354 | 354 | void
|
| 355 | -rtsOutOfBoundsAccess(StgInt index, StgWord count, StgWord size, const char *op)
|
|
| 355 | +rtsOutOfBoundsAccess(StgInt index, StgWord count, StgWord size, const char *op, const char *module)
|
|
| 356 | 356 | {
|
| 357 | 357 | if (count <= 1) {
|
| 358 | - errorBelch("%s: array access out of bounds:\n"
|
|
| 358 | + errorBelch("%s: array access out of bounds in module %s:\n"
|
|
| 359 | 359 | " index %" FMT_Int " is not within [0, %" FMT_Word ").\n"
|
| 360 | 360 | "This is usually caused by incorrect use of unsafe primops "
|
| 361 | 361 | "in user or library code.",
|
| 362 | - op, index, size);
|
|
| 362 | + op, module, index, size);
|
|
| 363 | 363 | } else {
|
| 364 | - errorBelch("%s: array access out of bounds:\n"
|
|
| 364 | + errorBelch("%s: array access out of bounds in module %s:\n"
|
|
| 365 | 365 | " range of %" FMT_Word " elements starting at index %" FMT_Int
|
| 366 | 366 | " is not within [0, %" FMT_Word ").\n"
|
| 367 | 367 | "This is usually caused by incorrect use of unsafe primops "
|
| 368 | 368 | "in user or library code.",
|
| 369 | - op, count, index, size);
|
|
| 369 | + op, module, count, index, size);
|
|
| 370 | 370 | }
|
| 371 | 371 | stg_exit(EXIT_FAILURE);
|
| 372 | 372 | }
|
| 373 | 373 | |
| 374 | 374 | void
|
| 375 | -rtsMemcpyRangeOverlap(const char *op)
|
|
| 375 | +rtsMemcpyRangeOverlap(const char *op, const char *module)
|
|
| 376 | 376 | {
|
| 377 | - errorBelch("%s: overlapping source and destination ranges in a "
|
|
| 378 | - "memcpy-using operation.\n"
|
|
| 377 | + errorBelch("%s: overlapping source and destination ranges in module %s.\n"
|
|
| 379 | 378 | "This is usually caused by incorrect use of unsafe primops "
|
| 380 | 379 | "in user or library code.",
|
| 381 | - op);
|
|
| 380 | + op, module);
|
|
| 382 | 381 | stg_exit(EXIT_FAILURE);
|
| 383 | 382 | } |
| ... | ... | @@ -108,5 +108,5 @@ extern RtsMsgFunction rtsSysErrorMsgFn; |
| 108 | 108 | |
| 109 | 109 | /* Used by code generator */
|
| 110 | 110 | void rtsBadAlignmentBarf(void) STG_NORETURN;
|
| 111 | -void rtsOutOfBoundsAccess(StgInt index, StgWord count, StgWord size, const char *op) STG_NORETURN;
|
|
| 112 | -void rtsMemcpyRangeOverlap(const char *op) STG_NORETURN; |
|
| 111 | +void rtsOutOfBoundsAccess(StgInt index, StgWord count, StgWord size, const char *op, const char *module) STG_NORETURN;
|
|
| 112 | +void rtsMemcpyRangeOverlap(const char *op, const char *module) STG_NORETURN; |
| 1 | -readSmallArray#: array access out of bounds:
|
|
| 1 | +readSmallArray#: array access out of bounds in module Main:
|
|
| 2 | 2 | index -1 is not within [0, 5).
|
| 3 | 3 | This is usually caused by incorrect use of unsafe primops in user or library code. |
| 1 | +module Main where
|
|
| 2 | + |
|
| 3 | +import T26964ModuleA (bad)
|
|
| 4 | + |
|
| 5 | +main :: IO ()
|
|
| 6 | +main = bad |
| 1 | +readSmallArray#: array access out of bounds in module T26964ModuleA:
|
|
| 2 | + index -1 is not within [0, 5).
|
|
| 3 | +This is usually caused by incorrect use of unsafe primops in user or library code. |
| 1 | +{-# LANGUAGE UnboxedTuples #-}
|
|
| 2 | +{-# LANGUAGE MagicHash #-}
|
|
| 3 | + |
|
| 4 | +-- The failing -fcheck-prim-bounds primop is emitted while code-generating this
|
|
| 5 | +-- helper module, so the runtime diagnostic should report T26964ModuleA, not the
|
|
| 6 | +-- Main module that merely calls 'bad'.
|
|
| 7 | + |
|
| 8 | +module T26964ModuleA (bad) where
|
|
| 9 | + |
|
| 10 | +import GHC.Exts
|
|
| 11 | +import GHC.IO
|
|
| 12 | + |
|
| 13 | +{-# NOINLINE bad #-}
|
|
| 14 | +bad :: IO ()
|
|
| 15 | +bad = IO $ \s0 ->
|
|
| 16 | + case newSmallArray# 5# () s0 of
|
|
| 17 | + (# s1, marr #) -> readSmallArray# marr (-1#) s1 |
| 1 | -copyMutableByteArray#: array access out of bounds:
|
|
| 1 | +copyMutableByteArray#: array access out of bounds in module Main:
|
|
| 2 | 2 | range of 4 elements starting at index 6 is not within [0, 8).
|
| 3 | 3 | This is usually caused by incorrect use of unsafe primops in user or library code. |
| ... | ... | @@ -42,3 +42,9 @@ test('T26964', |
| 42 | 42 | test('T26964b',
|
| 43 | 43 | [omit_ghci, js_skip, exit_code(1), normalise_errmsg_fun(strip_prog_prefix)],
|
| 44 | 44 | compile_and_run, ['-fcheck-prim-bounds'])
|
| 45 | +# T26964Module checks that the reported module is the one containing the failing
|
|
| 46 | +# primop (the helper T26964ModuleA), not the Main module that calls it.
|
|
| 47 | +test('T26964Module',
|
|
| 48 | + [omit_ghci, js_skip, exit_code(1), normalise_errmsg_fun(strip_prog_prefix),
|
|
| 49 | + extra_files(['T26964ModuleA.hs'])],
|
|
| 50 | + multimod_compile_and_run, ['T26964Module', '-fcheck-prim-bounds']) |