Andreas Klebinger pushed to branch wip/apk/rts-flag-fixes at Glasgow Haskell Compiler / GHC

Commits:

9 changed files:

Changes:

  • changelog.d/T27830-rts-flag-bounds
    1
    +section: rts
    
    2
    +synopsis: Fix bugs in RTS flags handling: RTS Flags now respects -n even with large -A args and errors give more accurate bounds on valid value.
    
    3
    +issues: #27830 #27829
    
    4
    +mrs: !16713
    
    5
    +

  • docs/users_guide/runtime_control.rst
    ... ... @@ -635,10 +635,11 @@ performance.
    635 635
     
    
    636 636
         Set the number of generations used by the garbage
    
    637 637
         collector. The default of 2 seems to be good, but the garbage
    
    638
    -    collector can support any number of generations. Anything larger
    
    638
    +    collector can in theory support any number of generations. Anything larger
    
    639 639
         than about 4 is probably not a good idea unless your program runs
    
    640 640
         for a *long* time, because the oldest generation will hardly ever
    
    641
    -    get collected.
    
    641
    +    get collected. Currently the RTS hardcodes a limit of 64 generations which
    
    642
    +    seems plenty.
    
    642 643
     
    
    643 644
         Specifying 1 generation with ``+RTS -G1`` gives you a simple 2-space
    
    644 645
         collector, as you would expect. In a 2-space collector, the :rts-flag:`-A
    

  • rts/RtsFlags.c
    ... ... @@ -141,8 +141,8 @@ void initRtsFlagsDefaults(void)
    141 141
         if (maxStkSize == 0)
    
    142 142
             maxStkSize = 8 * 1024 * 1024;
    
    143 143
         // GcFlags.maxStkSiz is 32-bit, so we need to cap to prevent overflow (#17019)
    
    144
    -    else if (maxStkSize > UINT32_MAX * sizeof(W_))
    
    145
    -        maxStkSize = UINT32_MAX * sizeof(W_);
    
    144
    +    else if (maxStkSize > (StgWord64) UINT32_MAX * sizeof(W_))
    
    145
    +        maxStkSize = (StgWord64) UINT32_MAX * sizeof(W_);
    
    146 146
     
    
    147 147
         RtsFlags.GcFlags.statsFile          = NULL;
    
    148 148
         RtsFlags.GcFlags.giveStats          = NO_GC_STATS;
    
    ... ... @@ -155,7 +155,7 @@ void initRtsFlagsDefaults(void)
    155 155
         /* -A default. See #16499 for a discussion about the tradeoffs */
    
    156 156
         RtsFlags.GcFlags.minAllocAreaSize   = (4 * 1024 * 1024)       / BLOCK_SIZE;
    
    157 157
         RtsFlags.GcFlags.largeAllocLim      = 0; /* defaults to minAllocAreasize */
    
    158
    -    RtsFlags.GcFlags.nurseryChunkSize   = 0;
    
    158
    +    RtsFlags.GcFlags.nurseryChunkSize   = -1; /* -1: Nothing set by user, normalized to off by default, 0: off explicitly, <n>: explicit size*/
    
    159 159
         RtsFlags.GcFlags.minOldGenSize      = (1024 * 1024)       / BLOCK_SIZE; /* -O default */
    
    160 160
         RtsFlags.GcFlags.maxHeapSize        = 0;    /* off by default */
    
    161 161
         RtsFlags.GcFlags.heapLimitGrace     = (1024 * 1024);
    
    ... ... @@ -1239,19 +1239,19 @@ error = true;
    1239 1239
                       if (rts_argv[arg][2] == 'L') {
    
    1240 1240
                           RtsFlags.GcFlags.largeAllocLim
    
    1241 1241
                               = decodeSize(rts_argv[arg], 3, 2*BLOCK_SIZE,
    
    1242
    -                                       HS_INT_MAX) / BLOCK_SIZE;
    
    1242
    +                                       (StgWord64) HS_WORD32_MAX * BLOCK_SIZE) / BLOCK_SIZE;
    
    1243 1243
                       } else {
    
    1244 1244
                           // minimum two blocks in the nursery, so that we have one
    
    1245 1245
                           // to grab for allocate().
    
    1246 1246
                           RtsFlags.GcFlags.minAllocAreaSize
    
    1247 1247
                               = decodeSize(rts_argv[arg], 2, 2*BLOCK_SIZE,
    
    1248
    -                                       HS_INT_MAX) / BLOCK_SIZE;
    
    1248
    +                                       (StgWord64) HS_WORD32_MAX * BLOCK_SIZE) / BLOCK_SIZE;
    
    1249 1249
                       }
    
    1250 1250
                       break;
    
    1251 1251
                   case 'n':
    
    1252 1252
                       OPTION_UNSAFE;
    
    1253 1253
                       RtsFlags.GcFlags.nurseryChunkSize
    
    1254
    -                      = decodeSize(rts_argv[arg], 2, 2*BLOCK_SIZE, HS_INT_MAX)
    
    1254
    +                      = decodeSize(rts_argv[arg], 2, 0, ((StgWord64) HS_INT32_MAX) * BLOCK_SIZE)
    
    1255 1255
                                / BLOCK_SIZE;
    
    1256 1256
                       break;
    
    1257 1257
     
    
    ... ... @@ -1302,7 +1302,7 @@ error = true;
    1302 1302
                   case 'K':
    
    1303 1303
                       OPTION_UNSAFE;
    
    1304 1304
                       RtsFlags.GcFlags.maxStkSize =
    
    1305
    -                      decodeSize(rts_argv[arg], 2, 0, UINT32_MAX)
    
    1305
    +                      decodeSize(rts_argv[arg], 2, 0, (StgWord64) UINT32_MAX * sizeof(W_))
    
    1306 1306
                           / sizeof(W_);
    
    1307 1307
                       break;
    
    1308 1308
     
    
    ... ... @@ -1311,22 +1311,22 @@ error = true;
    1311 1311
                     switch(rts_argv[arg][2]) {
    
    1312 1312
                     case 'c':
    
    1313 1313
                       RtsFlags.GcFlags.stkChunkSize =
    
    1314
    -                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
    
    1314
    +                      decodeSize(rts_argv[arg], 3, sizeof(W_), (StgWord64)HS_WORD32_MAX * sizeof(W_))
    
    1315 1315
                           / sizeof(W_);
    
    1316 1316
                       break;
    
    1317 1317
                     case 'b':
    
    1318 1318
                       RtsFlags.GcFlags.stkChunkBufferSize =
    
    1319
    -                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
    
    1319
    +                      decodeSize(rts_argv[arg], 3, sizeof(W_), (StgWord64)HS_WORD32_MAX * sizeof(W_))
    
    1320 1320
                           / sizeof(W_);
    
    1321 1321
                       break;
    
    1322 1322
                     case 'i':
    
    1323 1323
                       RtsFlags.GcFlags.initialStkSize =
    
    1324
    -                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
    
    1324
    +                      decodeSize(rts_argv[arg], 3, sizeof(W_), (StgWord64)HS_WORD32_MAX * sizeof(W_))
    
    1325 1325
                           / sizeof(W_);
    
    1326 1326
                       break;
    
    1327 1327
                     default:
    
    1328 1328
                       RtsFlags.GcFlags.initialStkSize =
    
    1329
    -                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX)
    
    1329
    +                      decodeSize(rts_argv[arg], 2, sizeof(W_), (StgWord64)HS_WORD32_MAX * sizeof(W_))
    
    1330 1330
                           / sizeof(W_);
    
    1331 1331
                       break;
    
    1332 1332
                     }
    
    ... ... @@ -1339,7 +1339,7 @@ error = true;
    1339 1339
                               decodeSize(rts_argv[arg], 8, BLOCK_SIZE, HS_WORD_MAX);
    
    1340 1340
                       } else {
    
    1341 1341
                           RtsFlags.GcFlags.maxHeapSize =
    
    1342
    -                          decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX)
    
    1342
    +                          decodeSize(rts_argv[arg], 2, BLOCK_SIZE, (StgWord64) HS_WORD32_MAX * BLOCK_SIZE)
    
    1343 1343
                               / BLOCK_SIZE;
    
    1344 1344
                           // user give size in *bytes* but "maxHeapSize" is in
    
    1345 1345
                           // *blocks*
    
    ... ... @@ -1388,8 +1388,9 @@ error = true;
    1388 1388
     #endif
    
    1389 1389
                   case 'G':
    
    1390 1390
                       OPTION_UNSAFE;
    
    1391
    -                  RtsFlags.GcFlags.generations =
    
    1392
    -                      decodeSize(rts_argv[arg], 2, 1, HS_INT_MAX);
    
    1391
    +                  // Capped at 64 by hardcoded array size in non-threaded RTS.
    
    1392
    +                  RtsFlags.GcFlags.generations = (uint32_t)
    
    1393
    +                      decodeSize(rts_argv[arg], 2, 1, GC_MAX_GENERATIONS);
    
    1393 1394
                       break;
    
    1394 1395
     
    
    1395 1396
                   case 'H':
    
    ... ... @@ -1398,7 +1399,7 @@ error = true;
    1398 1399
                           RtsFlags.GcFlags.heapSizeSuggestionAuto = true;
    
    1399 1400
                       } else {
    
    1400 1401
                           RtsFlags.GcFlags.heapSizeSuggestion = (uint32_t)
    
    1401
    -                          (decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX)
    
    1402
    +                          (decodeSize(rts_argv[arg], 2, BLOCK_SIZE, (StgWord64) HS_WORD32_MAX * BLOCK_SIZE)
    
    1402 1403
                               / BLOCK_SIZE);
    
    1403 1404
                       }
    
    1404 1405
                       break;
    
    ... ... @@ -1407,7 +1408,7 @@ error = true;
    1407 1408
                       OPTION_UNSAFE;
    
    1408 1409
                       RtsFlags.GcFlags.minOldGenSize =
    
    1409 1410
                           (uint32_t)(decodeSize(rts_argv[arg], 2, BLOCK_SIZE,
    
    1410
    -                                       HS_WORD_MAX)
    
    1411
    +                                       (StgWord64) HS_WORD32_MAX * BLOCK_SIZE)
    
    1411 1412
                                 / BLOCK_SIZE);
    
    1412 1413
                       break;
    
    1413 1414
     
    
    ... ... @@ -1994,9 +1995,25 @@ static void normaliseRtsOpts (void)
    1994 1995
             RtsFlags.GcFlags.minAllocAreaSize = RtsFlags.GcFlags.maxHeapSize;
    
    1995 1996
         }
    
    1996 1997
     
    
    1997
    -    // If we have -A16m or larger, use -n4m.
    
    1998
    -    if (RtsFlags.GcFlags.minAllocAreaSize >= (16*1024*1024) / BLOCK_SIZE) {
    
    1999
    -        RtsFlags.GcFlags.nurseryChunkSize = (4*1024*1024) / BLOCK_SIZE;
    
    1998
    +    // If no explicit size was given, and we have -A16m or larger, use -n4m.
    
    1999
    +    if (RtsFlags.GcFlags.nurseryChunkSize == -1) {
    
    2000
    +        if (RtsFlags.GcFlags.minAllocAreaSize >= (16*1024*1024) / BLOCK_SIZE) {
    
    2001
    +            RtsFlags.GcFlags.nurseryChunkSize = (4*1024*1024) / BLOCK_SIZE;
    
    2002
    +        } else {
    
    2003
    +            RtsFlags.GcFlags.nurseryChunkSize = 0;
    
    2004
    +        }
    
    2005
    +    }
    
    2006
    +    else if ( RtsFlags.GcFlags.nurseryChunkSize > 0 && RtsFlags.GcFlags.nurseryChunkSize < 2) {
    
    2007
    +        errorBelch("nursery chunk size (-n) must be at least %" FMT_Word " large.", (W_)(2 * BLOCK_SIZE));
    
    2008
    +        errorBelch("Disabling nursery chunking");
    
    2009
    +        RtsFlags.GcFlags.nurseryChunkSize = 0;
    
    2010
    +    }
    
    2011
    +    // If the user gave a chunk size respect it, unless it's larger than
    
    2012
    +    // minimum allocation area.
    
    2013
    +    else if ( (StgWord64) RtsFlags.GcFlags.nurseryChunkSize > (StgWord64) RtsFlags.GcFlags.minAllocAreaSize) {
    
    2014
    +        errorBelch("nursery chunk size (-n) is bigger than minimum alloc area size (-A)");
    
    2015
    +        errorBelch("Disabling nursery chunking");
    
    2016
    +        RtsFlags.GcFlags.nurseryChunkSize = 0;
    
    2000 2017
         }
    
    2001 2018
     
    
    2002 2019
         if (RtsFlags.ParFlags.parGcLoadBalancingGen == ~0u) {
    
    ... ... @@ -2207,9 +2224,7 @@ decodeSize(const char *flag, uint32_t offset, StgWord64 min, StgWord64 max)
    2207 2224
         val = (StgWord64)m;
    
    2208 2225
     
    
    2209 2226
         if (m < 0 || val < min || val > max) {
    
    2210
    -        // printf doesn't like 64-bit format specs on Windows
    
    2211
    -        // apparently, so fall back to unsigned long.
    
    2212
    -        errorBelch("error in RTS option %s: size outside allowed range (%" FMT_Word " - %" FMT_Word ")", flag, (W_)min, (W_)max);
    
    2227
    +        errorBelch("error in RTS option %s: size outside allowed range (%" FMT_Word64 " - %" FMT_Word64 ")", flag, (StgWord64)min, (StgWord64)max);
    
    2213 2228
             stg_exit(EXIT_FAILURE);
    
    2214 2229
         }
    
    2215 2230
     
    

  • rts/include/rts/Flags.h
    ... ... @@ -45,7 +45,7 @@ typedef struct _GC_FLAGS {
    45 45
         uint32_t     maxHeapSize;        /* in *blocks* */
    
    46 46
         uint32_t     minAllocAreaSize;   /* in *blocks* */
    
    47 47
         uint32_t     largeAllocLim;      /* in *blocks* */
    
    48
    -    uint32_t     nurseryChunkSize;   /* in *blocks* */
    
    48
    +    int32_t      nurseryChunkSize;   /* in *blocks*, int to distinguish default off, vs explicit off */
    
    49 49
         uint32_t     minOldGenSize;      /* in *blocks* */
    
    50 50
         uint32_t     heapSizeSuggestion; /* in *blocks* */
    
    51 51
         bool heapSizeSuggestionAuto;
    
    ... ... @@ -80,7 +80,7 @@ typedef struct _GC_FLAGS {
    80 80
                                      * to handle the exception before we
    
    81 81
                                      * raise it again.
    
    82 82
                                      */
    
    83
    -    StgWord heapLimitGrace;     /* units: *blocks*
    
    83
    +    StgWord heapLimitGrace;     /* units: *bytes*
    
    84 84
                                      * After a HeapOverflow exception has
    
    85 85
                                      * been raised, how much extra space is
    
    86 86
                                      * given to the thread to handle the
    

  • rts/include/rts/storage/GC.h
    ... ... @@ -52,6 +52,8 @@
    52 52
     // ie. StgWord.  Note that we have had some tricky int overflows in a
    
    53 53
     // couple of cases caused by using ints rather than longs (e.g. #5086)
    
    54 54
     
    
    55
    +#define GC_MAX_GENERATIONS 64
    
    56
    +
    
    55 57
     typedef StgWord memcount;
    
    56 58
     
    
    57 59
     typedef struct nursery_ {
    

  • rts/sm/GC.c
    ... ... @@ -154,7 +154,7 @@ static Condition gc_exit_leave_now_cv;
    154 154
     
    
    155 155
     #else // THREADED_RTS
    
    156 156
     // Must match the alignment of gen_workspace.
    
    157
    -StgWord8 the_gc_thread[sizeof(gc_thread) + 64 * sizeof(gen_workspace)]
    
    157
    +StgWord8 the_gc_thread[sizeof(gc_thread) + GC_MAX_GENERATIONS * sizeof(gen_workspace)]
    
    158 158
         ATTRIBUTE_ALIGNED(GEN_WORKSPACE_ALIGNMENT);
    
    159 159
     #endif // THREADED_RTS
    
    160 160
     
    

  • rts/sm/Storage.c
    ... ... @@ -273,7 +273,7 @@ void storageAddCapabilities (uint32_t from, uint32_t to)
    273 273
         if (RtsFlags.GcFlags.nurseryChunkSize == 0) {
    
    274 274
             new_n_nurseries = to;
    
    275 275
         } else {
    
    276
    -        memcount total_alloc = to * RtsFlags.GcFlags.minAllocAreaSize;
    
    276
    +        memcount total_alloc = to * (size_t) RtsFlags.GcFlags.minAllocAreaSize;
    
    277 277
             new_n_nurseries =
    
    278 278
                 stg_max(to, total_alloc / RtsFlags.GcFlags.nurseryChunkSize);
    
    279 279
         }
    

  • testsuite/tests/rts/rtsflags001.stderr-ws-32
    1
    -rtsflags001: error in RTS option -H0m: size outside allowed range (4096 - 4294967295)
    1
    +rtsflags001: error in RTS option -H0m: size outside allowed range (4096 - 17592186040320)

  • testsuite/tests/rts/rtsflags001.stderr-ws-64
    1
    -rtsflags001: error in RTS option -H0m: size outside allowed range (4096 - 18446744073709551615)
    1
    +rtsflags001: error in RTS option -H0m: size outside allowed range (4096 - 17592186040320)