Matthew Pickering pushed to branch wip/stable-ipe-info at Glasgow Haskell Compiler / GHC

Commits:

5 changed files:

Changes:

  • compiler/GHC/StgToCmm/InfoTableProv.hs
    ... ... @@ -158,6 +158,9 @@ emitIpeBufferListNode this_mod ents dus0 = do
    158 158
               [ -- 'next' field
    
    159 159
                 zeroCLit platform
    
    160 160
     
    
    161
    +            -- 'node_id' field
    
    162
    +          , zeroCLit platform
    
    163
    +
    
    161 164
                 -- 'compressed' field
    
    162 165
               , int do_compress
    
    163 166
     
    

  • rts/IPE.c
    ... ... @@ -62,6 +62,22 @@ entry's containing IpeBufferListNode and its index in that node.
    62 62
     When the user looks up an IPE entry, we convert it to the user-facing
    
    63 63
     InfoProvEnt representation.
    
    64 64
     
    
    65
    +Note [Stable identifiers for IPE entries]
    
    66
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    67
    +
    
    68
    +Each IPE entry is given a stable identifier which remains the same across
    
    69
    +different runs of the executable (unlike the address of the info table).
    
    70
    +
    
    71
    +The identifier is a 64-bit word which consists of two parts.
    
    72
    +
    
    73
    +* The high 32-bits are a per-node identifier.
    
    74
    +* The low 32-bits are the index of the entry in the node.
    
    75
    +
    
    76
    +When a node is queued in the pending list by `registerInfoProvList` it is
    
    77
    +given a unique identifier from an incrementing global variable.
    
    78
    +
    
    79
    +The unique key can be computed by using the `IPE_ENTRY_KEY` macro.
    
    80
    +
    
    65 81
     */
    
    66 82
     
    
    67 83
     typedef struct {
    
    ... ... @@ -69,6 +85,13 @@ typedef struct {
    69 85
         uint32_t idx;
    
    70 86
     } IpeMapEntry;
    
    71 87
     
    
    88
    +// See Note [Stable identifiers for IPE entries]
    
    89
    +#define IPE_ENTRY_KEY(entry) \
    
    90
    +    MAKE_IPE_KEY((entry).node->node_id, (entry).idx)
    
    91
    +
    
    92
    +#define MAKE_IPE_KEY(module_id, idx) \
    
    93
    +    ((((uint64_t)(module_id)) << 32) | ((uint64_t)(idx)))
    
    94
    +
    
    72 95
     #if defined(THREADED_RTS)
    
    73 96
     static Mutex ipeMapLock;
    
    74 97
     #endif
    
    ... ... @@ -78,6 +101,9 @@ static HashTable *ipeMap = NULL;
    78 101
     // Accessed atomically
    
    79 102
     static IpeBufferListNode *ipeBufferList = NULL;
    
    80 103
     
    
    104
    +// A global counter which is used to give an IPE entry a unique value across runs.
    
    105
    +static StgWord next_module_id = 1; // Start at 1 to reserve 0 as "invalid"
    
    106
    +
    
    81 107
     static void decompressIPEBufferListNodeIfCompressed(IpeBufferListNode*);
    
    82 108
     static void updateIpeMap(void);
    
    83 109
     
    
    ... ... @@ -114,6 +140,7 @@ static InfoProvEnt ipeBufferEntryToIpe(const IpeBufferListNode *node, uint32_t i
    114 140
         return (InfoProvEnt) {
    
    115 141
                 .info = node->tables[idx],
    
    116 142
                 .prov = {
    
    143
    +                .info_prov_id  = MAKE_IPE_KEY(node->node_id, idx),
    
    117 144
                     .table_name = &strings[ent->table_name],
    
    118 145
                     .closure_desc = ent->closure_desc,
    
    119 146
                     .ty_desc = &strings[ent->ty_desc],
    
    ... ... @@ -181,9 +208,22 @@ A performance test for IPE registration and lookup can be found here:
    181 208
     https://gitlab.haskell.org/ghc/ghc/-/merge_requests/5724#note_370806
    
    182 209
     */
    
    183 210
     void registerInfoProvList(IpeBufferListNode *node) {
    
    211
    +
    
    212
    +        // Grab a fresh module_id
    
    213
    +    uint32_t module_id;
    
    214
    +    StgWord temp_module_id;
    
    215
    +    while (true) {
    
    216
    +        temp_module_id = next_module_id;
    
    217
    +        if (cas(&next_module_id, temp_module_id, temp_module_id+1) == temp_module_id) {
    
    218
    +            module_id = (uint32_t) temp_module_id;
    
    219
    +            break;
    
    220
    +        }
    
    221
    +
    
    222
    +    }
    
    184 223
         while (true) {
    
    185 224
             IpeBufferListNode *old = RELAXED_LOAD(&ipeBufferList);
    
    186 225
             node->next = old;
    
    226
    +        node->node_id = module_id;
    
    187 227
             if (cas_ptr((volatile void **) &ipeBufferList, old, node) == (void *) old) {
    
    188 228
                 return;
    
    189 229
             }
    
    ... ... @@ -205,6 +245,18 @@ bool lookupIPE(const StgInfoTable *info, InfoProvEnt *out) {
    205 245
         }
    
    206 246
     }
    
    207 247
     
    
    248
    +// Returns 0 when the info table is not present in the info table map.
    
    249
    +// See Note [Stable identifiers for IPE entries]
    
    250
    +uint64_t lookupIPEId(const StgInfoTable *info) {
    
    251
    +    updateIpeMap();
    
    252
    +    IpeMapEntry *map_ent = (IpeMapEntry *) lookupHashTable(ipeMap, (StgWord)(info));
    
    253
    +    if (map_ent){
    
    254
    +        return IPE_ENTRY_KEY(*map_ent);
    
    255
    +    } else {
    
    256
    +        return 0;
    
    257
    +    }
    
    258
    +}
    
    259
    +
    
    208 260
     void updateIpeMap(void) {
    
    209 261
         // Check if there's any work at all. If not so, we can circumvent locking,
    
    210 262
         // which decreases performance.
    

  • rts/ProfHeap.c
    ... ... @@ -230,9 +230,15 @@ closureIdentity( const StgClosure *p )
    230 230
                 return closure_type_names[info->type];
    
    231 231
             }
    
    232 232
         }
    
    233
    -    case HEAP_BY_INFO_TABLE: {
    
    234
    -        return get_itbl(p);
    
    233
    +    case HEAP_BY_INFO_TABLE:
    
    234
    +    {
    
    235
    +        uint64_t table_id = lookupIPEId(p->header.info);
    
    236
    +        if (table_id) {
    
    237
    +          return (void *) table_id;
    
    238
    +        } else {
    
    239
    +          return (void *) 0xffffffff;
    
    235 240
             }
    
    241
    +    }
    
    236 242
     
    
    237 243
         default:
    
    238 244
             barf("closureIdentity");
    

  • rts/eventlog/EventLog.c
    ... ... @@ -1472,7 +1472,7 @@ void postIPE(const InfoProvEnt *ipe)
    1472 1472
         CHECK(!ensureRoomForVariableEvent(&eventBuf, len));
    
    1473 1473
         postEventHeader(&eventBuf, EVENT_IPE);
    
    1474 1474
         postPayloadSize(&eventBuf, len);
    
    1475
    -    postWord64(&eventBuf, (StgWord) INFO_PTR_TO_STRUCT(ipe->info));
    
    1475
    +    postWord64(&eventBuf, (StgWord) (ipe->prov.info_prov_id));
    
    1476 1476
         postStringLen(&eventBuf, ipe->prov.table_name, table_name_len);
    
    1477 1477
         postStringLen(&eventBuf, closure_desc_buf, closure_desc_len);
    
    1478 1478
         postStringLen(&eventBuf, ipe->prov.ty_desc, ty_desc_len);
    

  • rts/include/rts/IPE.h
    ... ... @@ -14,6 +14,7 @@
    14 14
     #pragma once
    
    15 15
     
    
    16 16
     typedef struct InfoProv_ {
    
    17
    +    uint64_t   info_prov_id;
    
    17 18
         const char *table_name;
    
    18 19
         uint32_t closure_desc; // closure type
    
    19 20
         const char *ty_desc;
    
    ... ... @@ -68,18 +69,21 @@ GHC_STATIC_ASSERT(sizeof(IpeBufferEntry) % (WORD_SIZE_IN_BITS / 8) == 0, "sizeof
    68 69
     #define IPE_MAGIC_WORD 0x4950450049504500UL
    
    69 70
     
    
    70 71
     typedef struct {
    
    71
    -    StgWord magic;          // Must be IPE_MAGIC_WORD
    
    72
    +    StgWord64 magic;          // Must be IPE_MAGIC_WORD
    
    72 73
         IpeBufferEntry entries[]; // Flexible array member
    
    73 74
     } IpeBufferEntryBlock;
    
    74 75
     
    
    75 76
     typedef struct {
    
    76
    -    StgWord magic;          // Must be IPE_MAGIC_WORD
    
    77
    +    StgWord64 magic;          // Must be IPE_MAGIC_WORD
    
    77 78
         char string_table[];    // Flexible array member for string table
    
    78 79
     } IpeStringTableBlock;
    
    79 80
     
    
    80 81
     typedef struct IpeBufferListNode_ {
    
    81 82
         struct IpeBufferListNode_ *next;
    
    82 83
     
    
    84
    +    // This field is filled in when the node is registered.
    
    85
    +    uint32_t node_id;
    
    86
    +
    
    83 87
         // Everything below is read-only and generated by the codegen
    
    84 88
     
    
    85 89
         // This flag should be treated as a boolean
    
    ... ... @@ -112,6 +116,8 @@ void formatClosureDescIpe(const InfoProvEnt *ipe_buf, char *str_buf);
    112 116
     // Returns true on success, initializes `out`.
    
    113 117
     bool lookupIPE(const StgInfoTable *info, InfoProvEnt *out);
    
    114 118
     
    
    119
    +uint64_t lookupIPEId(const StgInfoTable *info);
    
    120
    +
    
    115 121
     #if defined(DEBUG)
    
    116 122
     void printIPE(const StgInfoTable *info);
    
    117 123
     #endif