Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC

Commits:

14 changed files:

Changes:

  • rts/AllocArray.c
    1
    +#include "rts/PosixSource.h"
    
    2
    +#include "Rts.h"
    
    3
    +
    
    4
    +#include "AllocArray.h"
    
    5
    +
    
    6
    +StgMutArrPtrs *allocateMutArrPtrs (Capability *cap,
    
    7
    +                                   StgWord nelements,
    
    8
    +                                   CostCentreStack *ccs USED_IF_PROFILING)
    
    9
    +{
    
    10
    +    /* All sizes in words */
    
    11
    +
    
    12
    +    /* The card table contains one byte for each 2^MUT_ARR_PTRS_CARD_BITS words
    
    13
    +     * in the array, making sure we round up, and then rounding up to a whole
    
    14
    +     * number of words. */
    
    15
    +    StgWord cardsize = mutArrPtrsCardTableSize(nelements); /* card table */
    
    16
    +    StgWord arrsize  = nelements + cardsize;               /* +array size */
    
    17
    +    StgWord objsize  = sizeofW(StgMutArrPtrs) + arrsize;   /* +header size */
    
    18
    +    StgMutArrPtrs *arr;
    
    19
    +    arr = (StgMutArrPtrs *)allocateMightFail(cap, objsize);
    
    20
    +    if (RTS_UNLIKELY(arr == NULL)) return NULL;
    
    21
    +    TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), arrsize, 0);
    
    22
    +
    
    23
    +    /* No write barrier needed since this is a new allocation. */
    
    24
    +    SET_HDR(arr, &stg_MUT_ARR_PTRS_DIRTY_info, ccs);
    
    25
    +    arr->ptrs = nelements;
    
    26
    +    arr->size = arrsize;
    
    27
    +
    
    28
    +    /* Initialize the card array. Note that memset needs sizes in bytes. */
    
    29
    +    memset(&(arr->payload[nelements]), 0, mutArrPtrsCards(nelements));
    
    30
    +
    
    31
    +    return arr;
    
    32
    +}
    
    33
    +
    
    34
    +StgSmallMutArrPtrs *allocateSmallMutArrPtrs (Capability *cap,
    
    35
    +                                             StgWord nelements,
    
    36
    +                                             CostCentreStack *ccs
    
    37
    +                                               USED_IF_PROFILING)
    
    38
    +{
    
    39
    +    /* All sizes in words */
    
    40
    +    StgWord arrsize = nelements;                              /* array size */
    
    41
    +    StgWord objsize = sizeofW(StgSmallMutArrPtrs) + arrsize;  /* +header size */
    
    42
    +    StgSmallMutArrPtrs *arr;
    
    43
    +    arr = (StgSmallMutArrPtrs *)allocateMightFail(cap, objsize);
    
    44
    +    if (RTS_UNLIKELY(arr == NULL)) return NULL;
    
    45
    +    TICK_ALLOC_PRIM(sizeofW(StgSmallMutArrPtrs), arrsize, 0);
    
    46
    +
    
    47
    +    /* No write barrier needed since this is a new allocation. */
    
    48
    +    SET_HDR(arr, &stg_SMALL_MUT_ARR_PTRS_DIRTY_info, ccs);
    
    49
    +    arr->ptrs = nelements;
    
    50
    +    return arr;
    
    51
    +}
    
    52
    +
    
    53
    +StgArrBytes *allocateArrBytes (Capability *cap,
    
    54
    +                               StgWord arrbytes,
    
    55
    +                               CostCentreStack *ccs USED_IF_PROFILING)
    
    56
    +{
    
    57
    +    /* All sizes in words */
    
    58
    +    StgWord arrwords = ROUNDUP_BYTES_TO_WDS(arrbytes);
    
    59
    +    StgWord objsize  = sizeofW(StgArrBytes) + arrwords;
    
    60
    +    StgArrBytes *arr;
    
    61
    +    arr = (StgArrBytes *)allocateMightFail(cap, objsize);
    
    62
    +    if (RTS_UNLIKELY(arr == NULL)) return NULL;
    
    63
    +    TICK_ALLOC_PRIM(sizeofW(StgArrBytes), arrwords, 0);
    
    64
    +    /* No write barrier needed since this is a new allocation. */
    
    65
    +    SET_HDR(arr, &stg_ARR_WORDS_info, ccs);
    
    66
    +    arr->bytes = arrbytes;
    
    67
    +    return arr;
    
    68
    +}
    
    69
    +
    
    70
    +StgArrBytes *allocateArrBytesPinned (Capability *cap,
    
    71
    +                                     StgWord arrbytes,
    
    72
    +                                     StgWord alignment,
    
    73
    +                                     CostCentreStack *ccs USED_IF_PROFILING)
    
    74
    +{
    
    75
    +    /* we always supply at least word-aligned memory, so there's no
    
    76
    +       need to allow extra space for alignment if the requirement is less
    
    77
    +       than a word.  This also prevents mischief with alignment == 0. */
    
    78
    +    if (alignment <= sizeof(StgWord)) { alignment = sizeof(StgWord); }
    
    79
    +
    
    80
    +    /* All sizes in words */
    
    81
    +    StgWord arrwords = ROUNDUP_BYTES_TO_WDS(arrbytes);
    
    82
    +    StgWord objsize  = sizeofW(StgArrBytes) + arrwords;
    
    83
    +    StgWord alignoff = sizeof(StgArrBytes); // it's the payload to be aligned
    
    84
    +    StgArrBytes *arr;
    
    85
    +    arr = (StgArrBytes *)allocatePinned(cap, objsize, alignment, alignoff);
    
    86
    +    if (RTS_UNLIKELY(arr == NULL)) return NULL;
    
    87
    +    TICK_ALLOC_PRIM(sizeofW(StgArrBytes), arrwords, 0);
    
    88
    +    /* No write barrier needed since this is a new allocation. */
    
    89
    +    SET_HDR(arr, &stg_ARR_WORDS_info, ccs);
    
    90
    +    arr->bytes = arrbytes;
    
    91
    +    return arr;
    
    92
    +}

  • rts/AllocArray.h
    1
    +/* -----------------------------------------------------------------------------
    
    2
    + *
    
    3
    + * (c) The GHC Team 2025
    
    4
    + *
    
    5
    + * Prototypes for functions in AllocArray.c
    
    6
    + *
    
    7
    + * RTS internal utilities for allocating arrays of pointers (StgMutArrPtrs) and
    
    8
    + * arrays of bytes (StgArrBytes).
    
    9
    + * -------------------------------------------------------------------------*/
    
    10
    +
    
    11
    +#pragma once
    
    12
    +
    
    13
    +#include "Capability.h"
    
    14
    +
    
    15
    +#include "BeginPrivate.h"
    
    16
    +
    
    17
    +/* All these allocation functions return NULL on failure. If the context
    
    18
    + * allows, then propagatethe failure upwards, e.g. to a CMM primop where a
    
    19
    + * heap overflow exception can be thrown. Otherwise, use:
    
    20
    + *   if (RTS_UNLIKELY(p == NULL)) exitHeapOverflow();
    
    21
    + */
    
    22
    +
    
    23
    +/* Allocate a StgMutArrPtrs for a given number of elements. It is allocated in
    
    24
    + * the DIRTY state.
    
    25
    + */
    
    26
    +StgMutArrPtrs *allocateMutArrPtrs (Capability *cap,
    
    27
    +                                   StgWord nelements,
    
    28
    +                                   CostCentreStack *ccs);
    
    29
    +
    
    30
    +/* Allocate a StgSmallMutArrPtrs for a given number of elements.
    
    31
    + */
    
    32
    +StgSmallMutArrPtrs *allocateSmallMutArrPtrs (Capability *cap,
    
    33
    +                                             StgWord nelements,
    
    34
    +                                             CostCentreStack *ccs);
    
    35
    +
    
    36
    +/* Allocate a StgArrBytes for a given number of bytes.
    
    37
    + */
    
    38
    +StgArrBytes *allocateArrBytes (Capability *cap,
    
    39
    +                               StgWord nbytes,
    
    40
    +                               CostCentreStack *ccs);
    
    41
    +
    
    42
    +/* Allocate a pinned (and optionally aligned) StgArrBytes for a given number
    
    43
    + * of bytes.
    
    44
    + */
    
    45
    +StgArrBytes *allocateArrBytesPinned (Capability *cap,
    
    46
    +                                     StgWord nbytes,
    
    47
    +                                     StgWord alignment,
    
    48
    +                                     CostCentreStack *ccs);
    
    49
    +
    
    50
    +#include "EndPrivate.h"

  • rts/Heap.c
    ... ... @@ -13,6 +13,7 @@
    13 13
     
    
    14 14
     #include "Capability.h"
    
    15 15
     #include "Printer.h"
    
    16
    +#include "AllocArray.h"
    
    16 17
     
    
    17 18
     StgWord heap_view_closureSize(StgClosure *closure) {
    
    18 19
         ASSERT(LOOKS_LIKE_CLOSURE_PTR(closure));
    
    ... ... @@ -278,18 +279,14 @@ StgMutArrPtrs *heap_view_closurePtrs(Capability *cap, StgClosure *closure) {
    278 279
         StgClosure **ptrs = (StgClosure **) stgMallocBytes(sizeof(StgClosure *) * size, "heap_view_closurePtrs");
    
    279 280
         StgWord nptrs = collect_pointers(closure, ptrs);
    
    280 281
     
    
    281
    -    size = nptrs + mutArrPtrsCardTableSize(nptrs);
    
    282
    -    StgMutArrPtrs *arr =
    
    283
    -        (StgMutArrPtrs *)allocate(cap, sizeofW(StgMutArrPtrs) + size);
    
    284
    -    TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), nptrs, 0);
    
    285
    -    SET_HDR(arr, &stg_MUT_ARR_PTRS_FROZEN_CLEAN_info, cap->r.rCCCS);
    
    286
    -    arr->ptrs = nptrs;
    
    287
    -    arr->size = size;
    
    282
    +    StgMutArrPtrs *arr = allocateMutArrPtrs(cap, nptrs, cap->r.rCCCS);
    
    283
    +    if (RTS_UNLIKELY(arr == NULL)) goto end;
    
    284
    +    SET_INFO((StgClosure *) arr, &stg_MUT_ARR_PTRS_FROZEN_CLEAN_info);
    
    288 285
     
    
    289 286
         for (StgWord i = 0; i<nptrs; i++) {
    
    290 287
             arr->payload[i] = ptrs[i];
    
    291 288
         }
    
    289
    +end:
    
    292 290
         stgFree(ptrs);
    
    293
    -
    
    294 291
         return arr;
    
    295 292
     }

  • rts/PrimOps.cmm
    ... ... @@ -112,20 +112,14 @@ import CLOSURE stg_sel_0_upd_info;
    112 112
     
    
    113 113
     stg_newByteArrayzh ( W_ n )
    
    114 114
     {
    
    115
    -    W_ words, payload_words;
    
    116 115
         gcptr p;
    
    117 116
     
    
    118 117
         MAYBE_GC_N(stg_newByteArrayzh, n);
    
    119 118
     
    
    120
    -    payload_words = ROUNDUP_BYTES_TO_WDS(n);
    
    121
    -    words = BYTES_TO_WDS(SIZEOF_StgArrBytes) + payload_words;
    
    122
    -    ("ptr" p) = ccall allocateMightFail(MyCapability() "ptr", words);
    
    123
    -    if (p == NULL) {
    
    119
    +    ("ptr" p) = ccall allocateArrBytes(MyCapability() "ptr", n, CCCS);
    
    120
    +    if (p == NULL) (likely: False) {
    
    124 121
             jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    125 122
         }
    
    126
    -    TICK_ALLOC_PRIM(SIZEOF_StgArrBytes,WDS(payload_words),0);
    
    127
    -    SET_HDR(p, stg_ARR_WORDS_info, CCCS);
    
    128
    -    StgArrBytes_bytes(p) = n;
    
    129 123
         return (p);
    
    130 124
     }
    
    131 125
     
    
    ... ... @@ -134,64 +128,29 @@ stg_newByteArrayzh ( W_ n )
    134 128
     
    
    135 129
     stg_newPinnedByteArrayzh ( W_ n )
    
    136 130
     {
    
    137
    -    W_ words, bytes, payload_words;
    
    138 131
         gcptr p;
    
    139 132
     
    
    140 133
         MAYBE_GC_N(stg_newPinnedByteArrayzh, n);
    
    141 134
     
    
    142
    -    bytes = n;
    
    143
    -    /* payload_words is what we will tell the profiler we had to allocate */
    
    144
    -    payload_words = ROUNDUP_BYTES_TO_WDS(bytes);
    
    145
    -    /* When we actually allocate memory, we need to allow space for the
    
    146
    -       header: */
    
    147
    -    bytes = bytes + SIZEOF_StgArrBytes;
    
    148
    -    /* Now we convert to a number of words: */
    
    149
    -    words = ROUNDUP_BYTES_TO_WDS(bytes);
    
    150
    -
    
    151
    -    ("ptr" p) = ccall allocatePinned(MyCapability() "ptr", words, BA_ALIGN, SIZEOF_StgArrBytes);
    
    152
    -    if (p == NULL) {
    
    135
    +    ("ptr" p) = ccall allocateArrBytesPinned(MyCapability() "ptr", n,
    
    136
    +                                             BA_ALIGN, CCCS);
    
    137
    +    if (p == NULL) (likely: False) {
    
    153 138
             jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    154 139
         }
    
    155
    -    TICK_ALLOC_PRIM(SIZEOF_StgArrBytes,WDS(payload_words),0);
    
    156
    -
    
    157
    -    /* No write barrier needed since this is a new allocation. */
    
    158
    -    SET_HDR(p, stg_ARR_WORDS_info, CCCS);
    
    159
    -    StgArrBytes_bytes(p) = n;
    
    160 140
         return (p);
    
    161 141
     }
    
    162 142
     
    
    163 143
     stg_newAlignedPinnedByteArrayzh ( W_ n, W_ alignment )
    
    164 144
     {
    
    165
    -    W_ words, bytes, payload_words;
    
    166 145
         gcptr p;
    
    167 146
     
    
    168 147
         again: MAYBE_GC(again);
    
    169 148
     
    
    170
    -    /* we always supply at least word-aligned memory, so there's no
    
    171
    -       need to allow extra space for alignment if the requirement is less
    
    172
    -       than a word.  This also prevents mischief with alignment == 0. */
    
    173
    -    if (alignment <= SIZEOF_W) { alignment = SIZEOF_W; }
    
    174
    -
    
    175
    -    bytes = n;
    
    176
    -
    
    177
    -    /* payload_words is what we will tell the profiler we had to allocate */
    
    178
    -    payload_words = ROUNDUP_BYTES_TO_WDS(bytes);
    
    179
    -
    
    180
    -    /* When we actually allocate memory, we need to allow space for the
    
    181
    -       header: */
    
    182
    -    bytes = bytes + SIZEOF_StgArrBytes;
    
    183
    -    /* Now we convert to a number of words: */
    
    184
    -    words = ROUNDUP_BYTES_TO_WDS(bytes);
    
    185
    -
    
    186
    -    ("ptr" p) = ccall allocatePinned(MyCapability() "ptr", words, alignment, SIZEOF_StgArrBytes);
    
    187
    -    if (p == NULL) {
    
    149
    +    ("ptr" p) = ccall allocateArrBytesPinned(MyCapability() "ptr", n,
    
    150
    +                                             alignment, CCCS);
    
    151
    +    if (p == NULL) (likely: False) {
    
    188 152
             jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    189 153
         }
    
    190
    -    TICK_ALLOC_PRIM(SIZEOF_StgArrBytes,WDS(payload_words),0);
    
    191
    -
    
    192
    -    /* No write barrier needed since this is a new allocation. */
    
    193
    -    SET_HDR(p, stg_ARR_WORDS_info, CCCS);
    
    194
    -    StgArrBytes_bytes(p) = n;
    
    195 154
         return (p);
    
    196 155
     }
    
    197 156
     
    
    ... ... @@ -399,36 +358,23 @@ stg_casInt64Arrayzh( gcptr arr, W_ ind, I64 old, I64 new )
    399 358
     
    
    400 359
     stg_newArrayzh ( W_ n /* words */, gcptr init )
    
    401 360
     {
    
    402
    -    W_ words, size, p;
    
    403 361
         gcptr arr;
    
    404 362
     
    
    405 363
         again: MAYBE_GC(again);
    
    406 364
     
    
    407
    -    // the mark area contains one byte for each 2^MUT_ARR_PTRS_CARD_BITS words
    
    408
    -    // in the array, making sure we round up, and then rounding up to a whole
    
    409
    -    // number of words.
    
    410
    -    size = n + mutArrPtrsCardWords(n);
    
    411
    -    words = BYTES_TO_WDS(SIZEOF_StgMutArrPtrs) + size;
    
    412
    -    ("ptr" arr) = ccall allocateMightFail(MyCapability() "ptr",words);
    
    413
    -    if (arr == NULL) {
    
    365
    +    ("ptr" arr) = ccall allocateMutArrPtrs(MyCapability() "ptr", n, CCCS);
    
    366
    +    if (arr == NULL) (likely: False) {
    
    414 367
             jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    415 368
         }
    
    416
    -    TICK_ALLOC_PRIM(SIZEOF_StgMutArrPtrs, WDS(size), 0);
    
    417
    -
    
    418
    -    /* No write barrier needed since this is a new allocation. */
    
    419
    -    SET_HDR(arr, stg_MUT_ARR_PTRS_DIRTY_info, CCCS);
    
    420
    -    StgMutArrPtrs_ptrs(arr) = n;
    
    421
    -    StgMutArrPtrs_size(arr) = size;
    
    422
    -
    
    423
    -    /* Ensure that the card array is initialized */
    
    424
    -    if (n != 0) {
    
    425
    -        setCardsValue(arr, 0, n, 0);
    
    426
    -    }
    
    427 369
     
    
    428
    -    // Initialise all elements of the array with the value in R2
    
    370
    +    // Initialise all elements of the array with the value init
    
    371
    +    W_ p;
    
    429 372
         p = arr + SIZEOF_StgMutArrPtrs;
    
    373
    +    // Avoid the shift for `WDS(n)` in the inner loop
    
    374
    +    W_ limit;
    
    375
    +    limit = arr + SIZEOF_StgMutArrPtrs + WDS(n);
    
    430 376
       for:
    
    431
    -    if (p < arr + SIZEOF_StgMutArrPtrs + WDS(n)) (likely: True) {
    
    377
    +    if (p < limit) (likely: True) {
    
    432 378
             W_[p] = init;
    
    433 379
             p = p + WDS(1);
    
    434 380
             goto for;
    
    ... ... @@ -522,23 +468,17 @@ stg_casArrayzh ( gcptr arr, W_ ind, gcptr old, gcptr new )
    522 468
     
    
    523 469
     stg_newSmallArrayzh ( W_ n /* words */, gcptr init )
    
    524 470
     {
    
    525
    -    W_ words, size, p;
    
    526 471
         gcptr arr;
    
    527 472
     
    
    528 473
         again: MAYBE_GC(again);
    
    529 474
     
    
    530
    -    words = BYTES_TO_WDS(SIZEOF_StgSmallMutArrPtrs) + n;
    
    531
    -    ("ptr" arr) = ccall allocateMightFail(MyCapability() "ptr",words);
    
    475
    +    ("ptr" arr) = ccall allocateSmallMutArrPtrs(MyCapability() "ptr", n, CCCS);
    
    532 476
         if (arr == NULL) (likely: False) {
    
    533 477
             jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    534 478
         }
    
    535
    -    TICK_ALLOC_PRIM(SIZEOF_StgSmallMutArrPtrs, WDS(n), 0);
    
    536
    -
    
    537
    -    /* No write barrier needed since this is a new allocation. */
    
    538
    -    SET_HDR(arr, stg_SMALL_MUT_ARR_PTRS_DIRTY_info, CCCS);
    
    539
    -    StgSmallMutArrPtrs_ptrs(arr) = n;
    
    540 479
     
    
    541
    -    // Initialise all elements of the array with the value in R2
    
    480
    +    // Initialise all elements of the array with the value init
    
    481
    +    W_ p;
    
    542 482
         p = arr + SIZEOF_StgSmallMutArrPtrs;
    
    543 483
         // Avoid the shift for `WDS(n)` in the inner loop
    
    544 484
         W_ limit;
    
    ... ... @@ -1148,6 +1088,11 @@ stg_listThreadszh ()
    1148 1088
       P_ arr;
    
    1149 1089
     
    
    1150 1090
       ("ptr" arr) = ccall listThreads(MyCapability() "ptr");
    
    1091
    +
    
    1092
    +  if (arr == NULL) (likely: False) {
    
    1093
    +    jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    1094
    +  }
    
    1095
    +
    
    1151 1096
       return (arr);
    
    1152 1097
     }
    
    1153 1098
     
    
    ... ... @@ -1414,7 +1359,7 @@ stg_atomicallyzh (P_ stm)
    1414 1359
         old_trec = StgTSO_trec(CurrentTSO);
    
    1415 1360
     
    
    1416 1361
         /* Nested transactions are not allowed; raise an exception */
    
    1417
    -    if (old_trec != NO_TREC) {
    
    1362
    +    if (old_trec != NO_TREC) (likely: False) {
    
    1418 1363
             jump stg_raisezh(ghczminternal_GHCziInternalziControlziExceptionziBase_nestedAtomically_closure);
    
    1419 1364
         }
    
    1420 1365
     
    
    ... ... @@ -2537,6 +2482,10 @@ for:
    2537 2482
         // Collect pointers.
    
    2538 2483
         ("ptr" ptrArray) = foreign "C" heap_view_closurePtrs(MyCapability() "ptr", clos "ptr");
    
    2539 2484
     
    
    2485
    +    if (ptrArray == NULL) (likely: False) {
    
    2486
    +        jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    2487
    +    }
    
    2488
    +
    
    2540 2489
         return (info, dat_arr, ptrArray);
    
    2541 2490
     }
    
    2542 2491
     
    

  • rts/RtsUtils.c
    ... ... @@ -198,6 +198,13 @@ reportHeapOverflow(void)
    198 198
                                 (W_)RtsFlags.GcFlags.maxHeapSize * BLOCK_SIZE);
    
    199 199
     }
    
    200 200
     
    
    201
    +void
    
    202
    +exitHeapOverflow(void)
    
    203
    +{
    
    204
    +    reportHeapOverflow();  // reportHeapOverflow() doesn't exit (see #2592)
    
    205
    +    stg_exit(EXIT_HEAPOVERFLOW);
    
    206
    +}
    
    207
    +
    
    201 208
     /* -----------------------------------------------------------------------------
    
    202 209
        Sleep for the given period of time.
    
    203 210
        -------------------------------------------------------------------------- */
    

  • rts/ThreadLabels.c
    ... ... @@ -15,6 +15,7 @@
    15 15
     #include "RtsFlags.h"
    
    16 16
     #include "Hash.h"
    
    17 17
     #include "Trace.h"
    
    18
    +#include "AllocArray.h"
    
    18 19
     
    
    19 20
     #include <stdlib.h>
    
    20 21
     #include <string.h>
    
    ... ... @@ -31,25 +32,16 @@
    31 32
      * determined by the ByteArray# length.
    
    32 33
      */
    
    33 34
     
    
    34
    -static StgArrBytes *
    
    35
    -allocateArrBytes(Capability *cap, size_t size_in_bytes)
    
    36
    -{
    
    37
    -    /* round up to a whole number of words */
    
    38
    -    uint32_t data_size_in_words  = ROUNDUP_BYTES_TO_WDS(size_in_bytes);
    
    39
    -    uint32_t total_size_in_words = sizeofW(StgArrBytes) + data_size_in_words;
    
    40
    -
    
    41
    -    StgArrBytes *arr = (StgArrBytes *) allocate(cap, total_size_in_words);
    
    42
    -    SET_ARR_HDR(arr, &stg_ARR_WORDS_info, cap->r.rCCCS, size_in_bytes);
    
    43
    -    return arr;
    
    44
    -}
    
    45
    -
    
    46 35
     void
    
    47 36
     setThreadLabel(Capability  *cap,
    
    48 37
                    StgTSO      *tso,
    
    49 38
                    char *label)
    
    50 39
     {
    
    51 40
         int len = strlen(label);
    
    52
    -    StgArrBytes *arr = allocateArrBytes(cap, len);
    
    41
    +    StgArrBytes *arr = allocateArrBytes(cap, len, cap->r.rCCCS);
    
    42
    +    // On allocation failure don't perform the effect. It's not convenient to
    
    43
    +    // propagate failure from here since there are multiple callers in the RTS.
    
    44
    +    if (RTS_UNLIKELY(arr == NULL)) return;
    
    53 45
         memcpy(&arr->payload, label, len);
    
    54 46
         labelThread(cap, tso, arr);
    
    55 47
     }
    

  • rts/Threads.c
    ... ... @@ -25,6 +25,7 @@
    25 25
     #include "Printer.h"
    
    26 26
     #include "sm/Sanity.h"
    
    27 27
     #include "sm/Storage.h"
    
    28
    +#include "AllocArray.h"
    
    28 29
     
    
    29 30
     #include <string.h>
    
    30 31
     
    
    ... ... @@ -879,6 +880,7 @@ loop:
    879 880
         return true;
    
    880 881
     }
    
    881 882
     
    
    883
    +/* Return NULL on allocation failure */
    
    882 884
     StgMutArrPtrs *listThreads(Capability *cap)
    
    883 885
     {
    
    884 886
         ACQUIRE_LOCK(&sched_mutex);
    
    ... ... @@ -892,13 +894,8 @@ StgMutArrPtrs *listThreads(Capability *cap)
    892 894
         }
    
    893 895
     
    
    894 896
         // Allocate a suitably-sized array...
    
    895
    -    const StgWord size = n_threads + mutArrPtrsCardTableSize(n_threads);
    
    896
    -    StgMutArrPtrs *arr =
    
    897
    -        (StgMutArrPtrs *)allocate(cap, sizeofW(StgMutArrPtrs) + size);
    
    898
    -    SET_HDR(arr, &stg_MUT_ARR_PTRS_DIRTY_info, CCS_SYSTEM);
    
    899
    -    TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), size, 0);
    
    900
    -    arr->ptrs = n_threads;
    
    901
    -    arr->size = size;
    
    897
    +    StgMutArrPtrs *arr = allocateMutArrPtrs(cap, n_threads, cap->r.rCCCS);
    
    898
    +    if (RTS_UNLIKELY(arr == NULL)) goto end;
    
    902 899
     
    
    903 900
         // Populate it...
    
    904 901
         StgWord i = 0;
    
    ... ... @@ -913,6 +910,7 @@ StgMutArrPtrs *listThreads(Capability *cap)
    913 910
             }
    
    914 911
         }
    
    915 912
         CHECKM(i == n_threads, "listThreads: Found too few threads");
    
    913
    +end:
    
    916 914
         RELEASE_LOCK(&sched_mutex);
    
    917 915
         return arr;
    
    918 916
     }
    

  • rts/Weak.c
    ... ... @@ -17,6 +17,7 @@
    17 17
     #include "Prelude.h"
    
    18 18
     #include "ThreadLabels.h"
    
    19 19
     #include "Trace.h"
    
    20
    +#include "AllocArray.h"
    
    20 21
     
    
    21 22
     // List of dead weak pointers collected by the last GC
    
    22 23
     static StgWeak *finalizer_list = NULL;
    
    ... ... @@ -89,8 +90,6 @@ scheduleFinalizers(Capability *cap, StgWeak *list)
    89 90
     {
    
    90 91
         StgWeak *w;
    
    91 92
         StgTSO *t;
    
    92
    -    StgMutArrPtrs *arr;
    
    93
    -    StgWord size;
    
    94 93
         uint32_t n, i;
    
    95 94
     
    
    96 95
         // n_finalizers is not necessarily zero under non-moving collection
    
    ... ... @@ -147,13 +146,10 @@ scheduleFinalizers(Capability *cap, StgWeak *list)
    147 146
     
    
    148 147
         debugTrace(DEBUG_weak, "weak: batching %d finalizers", n);
    
    149 148
     
    
    150
    -    size = n + mutArrPtrsCardTableSize(n);
    
    151
    -    arr = (StgMutArrPtrs *)allocate(cap, sizeofW(StgMutArrPtrs) + size);
    
    152
    -    TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), n, 0);
    
    149
    +    StgMutArrPtrs *arr = allocateMutArrPtrs(cap, n, CCS_SYSTEM_OR_NULL);
    
    150
    +    if (RTS_UNLIKELY(arr == NULL)) exitHeapOverflow();
    
    153 151
         // No write barrier needed here; this array is only going to referred to by this core.
    
    154
    -    SET_HDR(arr, &stg_MUT_ARR_PTRS_FROZEN_CLEAN_info, CCS_SYSTEM);
    
    155
    -    arr->ptrs = n;
    
    156
    -    arr->size = size;
    
    152
    +    SET_INFO((StgClosure *) arr, &stg_MUT_ARR_PTRS_FROZEN_CLEAN_info);
    
    157 153
     
    
    158 154
         n = 0;
    
    159 155
         for (w = list; w; w = w->link) {
    
    ... ... @@ -163,6 +159,10 @@ scheduleFinalizers(Capability *cap, StgWeak *list)
    163 159
             }
    
    164 160
         }
    
    165 161
         // set all the cards to 1
    
    162
    +    StgWord size = n + mutArrPtrsCardTableSize(n);
    
    163
    +    // TODO: does this need to be a StgMutArrPtrs with a card table?
    
    164
    +    // If the cards are all 1 and the array is clean, couldn't it
    
    165
    +    // be a StgSmallMutArrPtrs instead?
    
    166 166
         for (i = n; i < size; i++) {
    
    167 167
             arr->payload[i] = (StgClosure *)(W_)(-1);
    
    168 168
         }
    

  • rts/include/Rts.h
    ... ... @@ -291,6 +291,7 @@ DLL_IMPORT_RTS extern char *prog_name;
    291 291
     
    
    292 292
     void reportStackOverflow(StgTSO* tso);
    
    293 293
     void reportHeapOverflow(void);
    
    294
    +void exitHeapOverflow(void) STG_NORETURN;;
    
    294 295
     
    
    295 296
     void stg_exit(int n) STG_NORETURN;
    
    296 297
     
    

  • rts/include/rts/prof/CCS.h
    ... ... @@ -220,9 +220,14 @@ extern CostCentre * RTS_VAR(CC_LIST); // registered CC list
    220 220
     #define CCS_ALLOC(ccs, size) (ccs)->mem_alloc += ((size)-sizeofW(StgProfHeader))
    
    221 221
     #define ENTER_CCS_THUNK(cap,p) cap->r.rCCCS = p->header.prof.ccs
    
    222 222
     
    
    223
    +/* Allow using CCS_SYSTEM somewhat consistently with/without profiling mode */
    
    224
    +#define CCS_SYSTEM_OR_NULL CCS_SYSTEM
    
    225
    +
    
    223 226
     #else /* !PROFILING */
    
    224 227
     
    
    225 228
     #define CCS_ALLOC(ccs, amount) doNothing()
    
    226 229
     #define ENTER_CCS_THUNK(cap,p) doNothing()
    
    227 230
     
    
    231
    +#define CCS_SYSTEM_OR_NULL NULL
    
    232
    +
    
    228 233
     #endif /* PROFILING */

  • rts/include/rts/storage/GC.h
    ... ... @@ -170,36 +170,106 @@ void listAllBlocks(ListBlocksCb cb, void *user);
    170 170
     /* -----------------------------------------------------------------------------
    
    171 171
        Generic allocation
    
    172 172
     
    
    173
    -   StgPtr allocate(Capability *cap, W_ n)
    
    174
    -                                Allocates memory from the nursery in
    
    175
    -                                the current Capability.
    
    176
    -
    
    177
    -   StgPtr allocatePinned(Capability *cap, W_ n, W_ alignment, W_ align_off)
    
    178
    -                                Allocates a chunk of contiguous store
    
    179
    -                                n words long, which is at a fixed
    
    180
    -                                address (won't be moved by GC). The
    
    181
    -                                word at the byte offset 'align_off'
    
    182
    -                                will be aligned to 'alignment', which
    
    183
    -                                must be a power of two.
    
    184
    -                                Returns a pointer to the first word.
    
    185
    -                                Always succeeds.
    
    186
    -
    
    187
    -                                NOTE: the GC can't in general handle
    
    188
    -                                pinned objects, so allocatePinned()
    
    189
    -                                can only be used for ByteArrays at the
    
    190
    -                                moment.
    
    191
    -
    
    192
    -                                Don't forget to TICK_ALLOC_XXX(...)
    
    193
    -                                after calling allocate or
    
    194
    -                                allocatePinned, for the
    
    195
    -                                benefit of the ticky-ticky profiler.
    
    196
    -
    
    173
    +   See: Note [allocate and allocateMightFail]
    
    174
    +        Note [allocatePinned]
    
    175
    +        Note [allocate failure]
    
    197 176
        -------------------------------------------------------------------------- */
    
    198 177
     
    
    199 178
     StgPtr  allocate          ( Capability *cap, W_ n );
    
    200 179
     StgPtr  allocateMightFail ( Capability *cap, W_ n );
    
    201 180
     StgPtr  allocatePinned    ( Capability *cap, W_ n, W_ alignment, W_ align_off);
    
    202 181
     
    
    182
    +/* Note [allocate and allocateMightFail]
    
    183
    +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    184
    +
    
    185
    +   allocate() and allocateMightFail() allocate an area of memory n
    
    186
    +   *words* large, from the nursery of the supplied Capability, or from
    
    187
    +   the global block pool if the area requested is larger than
    
    188
    +   LARGE_OBJECT_THRESHOLD.  Memory is not allocated from the current
    
    189
    +   nursery block, so as not to interfere with Hp/HpLim.
    
    190
    +
    
    191
    +   The address of the allocated memory is returned.
    
    192
    +
    
    193
    +   After allocating, fill in the heap closure header, e.g.
    
    194
    +   SET_HDR(arr, stg_MUT_ARR_PTRS_DIRTY_info, CCCS);
    
    195
    +   and call TICK_ALLOC_XXX(...) for the benefit of the ticky-ticky
    
    196
    +   profiler.
    
    197
    +
    
    198
    +   On allocation failure, allocateMightFail() returns NULL whereas
    
    199
    +   allocate() terminates the RTS. See Note [allocate failure]. You
    
    200
    +   should prefer allocateMightFail() in cases where you can propagate
    
    201
    +   the failure up to a context in which you can raise exceptions, e.g.
    
    202
    +   in primops.
    
    203
    + */
    
    204
    +
    
    205
    +/* Note [allocatePinned]
    
    206
    +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    207
    +   allocatePinned() allocates a chunk of contiguous store n *words*
    
    208
    +   long, which is at a fixed address (i.e. won't be moved by GC). The
    
    209
    +   word at the byte offset 'align_off' will be aligned to 'alignment',
    
    210
    +   which must be a power of two.
    
    211
    +
    
    212
    +   The address of the allocated memory is returned.
    
    213
    +
    
    214
    +   The GC can't in general handle pinned objects, so allocatePinned()
    
    215
    +   can only be used for ByteArrays / stg_ARR_WORDS at the moment.
    
    216
    +
    
    217
    +   On allocation failure, allocatePinned() returns NULL.
    
    218
    +   See Note [allocate failure].
    
    219
    + */
    
    220
    +
    
    221
    +/* Note [allocate failure]
    
    222
    +   ~~~~~~~~~~~~~~~~~~~~~~~
    
    223
    +
    
    224
    +   The allocation functions differ in how they handle failure to
    
    225
    +   allocate:
    
    226
    +
    
    227
    +    * on failure allocateMightFail() returns NULL
    
    228
    +    * on failure allocatePinned() returns NULL
    
    229
    +    * on failure allocate() terminates the RTS (and thus typically
    
    230
    +      the whole process)
    
    231
    +
    
    232
    +   Each of these functions tries _quite_ hard to avoid allocation
    
    233
    +   failure however. If the nursery is already full, then another block
    
    234
    +   is allocated from the global block pool. If we need to get memory
    
    235
    +   from the OS and that operation fails, or if we would exceed
    
    236
    +   maxHeapSize then we fail.
    
    237
    +
    
    238
    +   There are two main existing conventions within the RTS for handling
    
    239
    +   allocation failure.
    
    240
    +
    
    241
    +   1. Start from a primop that uses one of the MAYBE_GC_* macros to
    
    242
    +      provide an opportunity to GC. Then buried deeply within C code
    
    243
    +      called from the primop, use allocate().
    
    244
    +
    
    245
    +   2. Start from a primop that uses one of the MAYBE_GC_* macros to
    
    246
    +      provide an opportunity to GC. Use allocateMightFail() within the
    
    247
    +      C code called from the primop. If that fails, propagate the
    
    248
    +      failure up to the primop where it can throw a HeapOverflow
    
    249
    +      exception.
    
    250
    +
    
    251
    +   Being able to throw an exception is preferable, since it's more
    
    252
    +   polite, provides better reporting and potentially it can be
    
    253
    +   caught and handled by the user program.
    
    254
    +
    
    255
    +   An advantage of the first approach is that its simpler to implement.
    
    256
    +   It does not require any mechanism to propagate failure (and undoing
    
    257
    +   any effects along the way so the operation can be safely retried
    
    258
    +   after GC).
    
    259
    +
    
    260
    +   Arguably neither existing convention is ideal. One might imagine
    
    261
    +   that when failure from allocateMightFail() propagates to the top
    
    262
    +   level primop, the primop would not throw a HeapOverflow exception
    
    263
    +   but invoke the GC with a request to make available at least the
    
    264
    +   required number of words. The GC may be able to succeed, in which
    
    265
    +   case the original operation can be retried. Or if the GC is unable
    
    266
    +   to free enough memory then it can throw the HeapOverflow exception.
    
    267
    +   In practice however, though there is a mechanism (via HpAlloc) to
    
    268
    +   tell the GC how much memory was needed, this is not used to decide
    
    269
    +   if we have to fail the allocation, it is just used for error
    
    270
    +   reporting.
    
    271
    + */
    
    272
    +
    
    203 273
     /* memory allocator for executable memory */
    
    204 274
     typedef void* AdjustorWritable;
    
    205 275
     typedef void* AdjustorExecutable;
    

  • rts/include/rts/storage/Heap.h
    ... ... @@ -10,6 +10,7 @@
    10 10
     
    
    11 11
     #include "rts/storage/Closures.h"
    
    12 12
     
    
    13
    +/* Returns NULL on allocation failure */
    
    13 14
     StgMutArrPtrs *heap_view_closurePtrs(Capability *cap, StgClosure *closure);
    
    14 15
     
    
    15 16
     void heap_view_closure_ptrs_in_pap_payload(StgClosure *ptrs[], StgWord *nptrs
    

  • rts/rts.cabal
    ... ... @@ -400,6 +400,7 @@ library
    400 400
             asm-sources: StgCRunAsm.S
    
    401 401
     
    
    402 402
           c-sources: Adjustor.c
    
    403
    +                 AllocArray.c
    
    403 404
                      adjustor/AdjustorPool.c
    
    404 405
                      ExecPage.c
    
    405 406
                      Arena.c
    

  • rts/sm/Storage.c
    ... ... @@ -1065,46 +1065,31 @@ accountAllocation(Capability *cap, W_ n)
    1065 1065
      * overwriting closures].
    
    1066 1066
      */
    
    1067 1067
     
    
    1068
    -/* -----------------------------------------------------------------------------
    
    1069
    -   StgPtr allocate (Capability *cap, W_ n)
    
    1070
    -
    
    1071
    -   Allocates an area of memory n *words* large, from the nursery of
    
    1072
    -   the supplied Capability, or from the global block pool if the area
    
    1073
    -   requested is larger than LARGE_OBJECT_THRESHOLD.  Memory is not
    
    1074
    -   allocated from the current nursery block, so as not to interfere
    
    1075
    -   with Hp/HpLim.
    
    1076
    -
    
    1077
    -   The address of the allocated memory is returned. allocate() never
    
    1078
    -   fails; if it returns, the returned value is a valid address.  If
    
    1079
    -   the nursery is already full, then another block is allocated from
    
    1080
    -   the global block pool.  If we need to get memory from the OS and
    
    1081
    -   that operation fails, then the whole process will be killed.
    
    1082
    -   -------------------------------------------------------------------------- */
    
    1083
    -
    
    1084 1068
     /*
    
    1085
    - * Allocate some n words of heap memory; terminating
    
    1086
    - * on heap overflow
    
    1069
    + * Allocate some n words of heap memory; terminating on heap overflow.
    
    1070
    + *
    
    1071
    + * See Note [allocate and allocateMightFail].
    
    1087 1072
      */
    
    1088 1073
     StgPtr
    
    1089 1074
     allocate (Capability *cap, W_ n)
    
    1090 1075
     {
    
    1091 1076
         StgPtr p = allocateMightFail(cap, n);
    
    1092
    -    if (p == NULL) {
    
    1093
    -        reportHeapOverflow();
    
    1094
    -        // heapOverflow() doesn't exit (see #2592), but we aren't
    
    1077
    +    if (RTS_UNLIKELY(p == NULL)) {
    
    1078
    +        // reportHeapOverflow() doesn't exit (see #2592), but we aren't
    
    1095 1079
             // in a position to do a clean shutdown here: we
    
    1096 1080
             // either have to allocate the memory or exit now.
    
    1097 1081
             // Allocating the memory would be bad, because the user
    
    1098 1082
             // has requested that we not exceed maxHeapSize, so we
    
    1099 1083
             // just exit.
    
    1100
    -        stg_exit(EXIT_HEAPOVERFLOW);
    
    1084
    +        exitHeapOverflow();
    
    1101 1085
         }
    
    1102 1086
         return p;
    
    1103 1087
     }
    
    1104 1088
     
    
    1105 1089
     /*
    
    1106
    - * Allocate some n words of heap memory; returning NULL
    
    1107
    - * on heap overflow
    
    1090
    + * Allocate some n words of heap memory; returning NULL on heap overflow.
    
    1091
    + *
    
    1092
    + * See Note [allocate and allocateMightFail].
    
    1108 1093
      */
    
    1109 1094
     StgPtr
    
    1110 1095
     allocateMightFail (Capability *cap, W_ n)
    
    ... ... @@ -1303,6 +1288,9 @@ start_new_pinned_block(Capability *cap)
    1303 1288
     /* ---------------------------------------------------------------------------
    
    1304 1289
        Allocate a fixed/pinned object.
    
    1305 1290
     
    
    1291
    +   See Note [allocatePinned] for the interface. This describes the
    
    1292
    +   implementation.
    
    1293
    +
    
    1306 1294
        We allocate small pinned objects into a single block, allocating a
    
    1307 1295
        new block when the current one overflows.  The block is chained
    
    1308 1296
        onto the large_object_list of generation 0.