Matthew Pickering pushed to branch wip/stable-ipe-info at Glasgow Haskell Compiler / GHC
Commits:
-
5d14950e
by Matthew Pickering at 2025-07-01T19:30:09+01:00
-
c4de29a4
by Matthew Pickering at 2025-07-02T12:35:06+01:00
-
59ec75c4
by Matthew Pickering at 2025-07-02T12:35:06+01:00
11 changed files:
- compiler/GHC/Cmm.hs
- compiler/GHC/CmmToAsm/PPC/Ppr.hs
- compiler/GHC/CmmToAsm/Ppr.hs
- compiler/GHC/CmmToLlvm/Data.hs
- compiler/GHC/StgToCmm/InfoTableProv.hs
- rts/IPE.c
- rts/ProfHeap.c
- rts/eventlog/EventLog.c
- rts/include/rts/IPE.h
- testsuite/tests/rts/ipe/ipeMap.c
- testsuite/tests/rts/ipe/ipe_lib.c
Changes:
| ... | ... | @@ -278,6 +278,7 @@ data SectionType |
| 278 | 278 | | InitArray -- .init_array on ELF, .ctor on Windows
|
| 279 | 279 | | FiniArray -- .fini_array on ELF, .dtor on Windows
|
| 280 | 280 | | CString
|
| 281 | + | IPE
|
|
| 281 | 282 | | OtherSection String
|
| 282 | 283 | deriving (Show)
|
| 283 | 284 | |
| ... | ... | @@ -298,6 +299,7 @@ sectionProtection (Section t _) = case t of |
| 298 | 299 | CString -> ReadOnlySection
|
| 299 | 300 | Data -> ReadWriteSection
|
| 300 | 301 | UninitialisedData -> ReadWriteSection
|
| 302 | + IPE -> ReadWriteSection
|
|
| 301 | 303 | (OtherSection _) -> ReadWriteSection
|
| 302 | 304 | |
| 303 | 305 | {-
|
| ... | ... | @@ -557,4 +559,5 @@ pprSectionType s = doubleQuotes $ case s of |
| 557 | 559 | InitArray -> text "initarray"
|
| 558 | 560 | FiniArray -> text "finiarray"
|
| 559 | 561 | CString -> text "cstring"
|
| 562 | + IPE -> text "ipe"
|
|
| 560 | 563 | OtherSection s' -> text s' |
| ... | ... | @@ -285,6 +285,9 @@ pprAlignForSection platform seg = line $ |
| 285 | 285 | Data
|
| 286 | 286 | | ppc64 -> text ".align 3"
|
| 287 | 287 | | otherwise -> text ".align 2"
|
| 288 | + IPE
|
|
| 289 | + | ppc64 -> text ".align 3"
|
|
| 290 | + | otherwise -> text ".align 2"
|
|
| 288 | 291 | ReadOnlyData
|
| 289 | 292 | | ppc64 -> text ".align 3"
|
| 290 | 293 | | otherwise -> text ".align 2"
|
| ... | ... | @@ -236,6 +236,10 @@ pprGNUSectionHeader config t suffix = |
| 236 | 236 | | OSMinGW32 <- platformOS platform
|
| 237 | 237 | -> text ".rdata"
|
| 238 | 238 | | otherwise -> text ".rodata.str"
|
| 239 | + IPE
|
|
| 240 | + | OSMinGW32 <- platformOS platform
|
|
| 241 | + -> text ".rdata"
|
|
| 242 | + | otherwise -> text ".ipe"
|
|
| 239 | 243 | OtherSection _ ->
|
| 240 | 244 | panic "PprBase.pprGNUSectionHeader: unknown section type"
|
| 241 | 245 | flags = case t of
|
| ... | ... | @@ -248,6 +252,10 @@ pprGNUSectionHeader config t suffix = |
| 248 | 252 | | OSMinGW32 <- platformOS platform
|
| 249 | 253 | -> empty
|
| 250 | 254 | | otherwise -> text ",\"aMS\"," <> sectionType platform "progbits" <> text ",1"
|
| 255 | + IPE
|
|
| 256 | + | OSMinGW32 <- platformOS platform
|
|
| 257 | + -> empty
|
|
| 258 | + | otherwise -> text ",\"a\"," <> sectionType platform "progbits"
|
|
| 251 | 259 | _ -> empty
|
| 252 | 260 | {-# SPECIALIZE pprGNUSectionHeader :: NCGConfig -> SectionType -> CLabel -> SDoc #-}
|
| 253 | 261 | {-# SPECIALIZE pprGNUSectionHeader :: NCGConfig -> SectionType -> CLabel -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable
|
| ... | ... | @@ -262,6 +270,7 @@ pprXcoffSectionHeader t = case t of |
| 262 | 270 | RelocatableReadOnlyData -> text ".csect .text[PR] # RelocatableReadOnlyData"
|
| 263 | 271 | CString -> text ".csect .text[PR] # CString"
|
| 264 | 272 | UninitialisedData -> text ".csect .data[BS]"
|
| 273 | + IPE -> text ".csect .text[PR] #IPE"
|
|
| 265 | 274 | _ -> panic "pprXcoffSectionHeader: unknown section type"
|
| 266 | 275 | {-# SPECIALIZE pprXcoffSectionHeader :: SectionType -> SDoc #-}
|
| 267 | 276 | {-# SPECIALIZE pprXcoffSectionHeader :: SectionType -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable
|
| ... | ... | @@ -276,6 +285,7 @@ pprDarwinSectionHeader t = case t of |
| 276 | 285 | InitArray -> text ".section\t__DATA,__mod_init_func,mod_init_funcs"
|
| 277 | 286 | FiniArray -> panic "pprDarwinSectionHeader: fini not supported"
|
| 278 | 287 | CString -> text ".section\t__TEXT,__cstring,cstring_literals"
|
| 288 | + IPE -> text ".const"
|
|
| 279 | 289 | OtherSection _ -> panic "pprDarwinSectionHeader: unknown section type"
|
| 280 | 290 | {-# SPECIALIZE pprDarwinSectionHeader :: SectionType -> SDoc #-}
|
| 281 | 291 | {-# SPECIALIZE pprDarwinSectionHeader :: SectionType -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable |
| ... | ... | @@ -145,7 +145,7 @@ llvmSectionType p t = case t of |
| 145 | 145 | CString -> case platformOS p of
|
| 146 | 146 | OSMinGW32 -> fsLit ".rdata$str"
|
| 147 | 147 | _ -> fsLit ".rodata.str"
|
| 148 | - |
|
| 148 | + IPE -> fsLit ".ipe"
|
|
| 149 | 149 | InitArray -> panic "llvmSectionType: InitArray"
|
| 150 | 150 | FiniArray -> panic "llvmSectionType: FiniArray"
|
| 151 | 151 | OtherSection _ -> panic "llvmSectionType: unknown section type"
|
| ... | ... | @@ -66,6 +66,28 @@ construction, the 'compressed' field of each IPE buffer list node is examined. |
| 66 | 66 | If the field indicates that the data has been compressed, the entry data and
|
| 67 | 67 | strings table are decompressed before continuing with the normal IPE map
|
| 68 | 68 | construction.
|
| 69 | + |
|
| 70 | +Note [IPE Stripping and magic words]
|
|
| 71 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 72 | + |
|
| 73 | +For systems which support ELF executables:
|
|
| 74 | + |
|
| 75 | +The metadata part of IPE info is placed into a separate ELF section (.ipe).
|
|
| 76 | +This can then be stripped afterwards if you don't require the metadata
|
|
| 77 | + |
|
| 78 | +```
|
|
| 79 | +-- Remove the section
|
|
| 80 | +objcopy --remove-section .ipe <your-exe>
|
|
| 81 | +-- Repack and compress the executable
|
|
| 82 | +upx <your-exe>
|
|
| 83 | +```
|
|
| 84 | + |
|
| 85 | +The .ipe section starts with a magic 64-bit word "IPE\nIPE\n`, encoded as ascii.
|
|
| 86 | + |
|
| 87 | +The RTS checks to see if the .ipe section starts with the magic word. If the
|
|
| 88 | +section has been stripped then it won't start with the magic word and the
|
|
| 89 | +metadata won't be accessible for the info tables.
|
|
| 90 | + |
|
| 69 | 91 | -}
|
| 70 | 92 | |
| 71 | 93 | emitIpeBufferListNode ::
|
| ... | ... | @@ -124,11 +146,21 @@ emitIpeBufferListNode this_mod ents dus0 = do |
| 124 | 146 | ipe_buffer_lbl :: CLabel
|
| 125 | 147 | ipe_buffer_lbl = mkIPELabel this_mod
|
| 126 | 148 | |
| 149 | + -- A magic word we can use to see if the IPE information has been stripped
|
|
| 150 | + -- or not
|
|
| 151 | + -- See Note [IPE Stripping and magic words]
|
|
| 152 | + -- "IPE\nIPE\n", null terminated.
|
|
| 153 | + ipe_header :: CmmStatic
|
|
| 154 | + ipe_header = CmmStaticLit (CmmInt 0x4950450049504500 W64)
|
|
| 155 | + |
|
| 127 | 156 | ipe_buffer_node :: [CmmStatic]
|
| 128 | 157 | ipe_buffer_node = map CmmStaticLit
|
| 129 | 158 | [ -- 'next' field
|
| 130 | 159 | zeroCLit platform
|
| 131 | 160 | |
| 161 | + -- 'node_id' field
|
|
| 162 | + , zeroCLit platform
|
|
| 163 | + |
|
| 132 | 164 | -- 'compressed' field
|
| 133 | 165 | , int do_compress
|
| 134 | 166 | |
| ... | ... | @@ -164,13 +196,13 @@ emitIpeBufferListNode this_mod ents dus0 = do |
| 164 | 196 | |
| 165 | 197 | -- Emit the strings table
|
| 166 | 198 | emitDecl $ CmmData
|
| 167 | - (Section Data strings_lbl)
|
|
| 168 | - (CmmStaticsRaw strings_lbl strings)
|
|
| 199 | + (Section IPE strings_lbl)
|
|
| 200 | + (CmmStaticsRaw strings_lbl (ipe_header : strings))
|
|
| 169 | 201 | |
| 170 | 202 | -- Emit the list of IPE buffer entries
|
| 171 | 203 | emitDecl $ CmmData
|
| 172 | - (Section Data entries_lbl)
|
|
| 173 | - (CmmStaticsRaw entries_lbl entries)
|
|
| 204 | + (Section IPE entries_lbl)
|
|
| 205 | + (CmmStaticsRaw entries_lbl (ipe_header : entries))
|
|
| 174 | 206 | |
| 175 | 207 | -- Emit the IPE buffer list node
|
| 176 | 208 | emitDecl $ CmmData
|
| ... | ... | @@ -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,9 +101,22 @@ 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 uint32_t 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 | |
| 110 | +// Check whether the IpeBufferListNode has the relevant magic words.
|
|
| 111 | +// See Note [IPE Stripping and magic words]
|
|
| 112 | +static inline bool ipe_node_valid(const IpeBufferListNode *node) {
|
|
| 113 | + return node &&
|
|
| 114 | + node->entries_block &&
|
|
| 115 | + node->string_table_block &&
|
|
| 116 | + node->entries_block->magic == IPE_MAGIC_WORD &&
|
|
| 117 | + node->string_table_block->magic == IPE_MAGIC_WORD;
|
|
| 118 | +}
|
|
| 119 | + |
|
| 84 | 120 | #if defined(THREADED_RTS)
|
| 85 | 121 | |
| 86 | 122 | void initIpe(void) { initMutex(&ipeMapLock); }
|
| ... | ... | @@ -99,11 +135,12 @@ static InfoProvEnt ipeBufferEntryToIpe(const IpeBufferListNode *node, uint32_t i |
| 99 | 135 | {
|
| 100 | 136 | CHECK(idx < node->count);
|
| 101 | 137 | CHECK(!node->compressed);
|
| 102 | - const char *strings = node->string_table;
|
|
| 103 | - const IpeBufferEntry *ent = &node->entries[idx];
|
|
| 138 | + const char *strings = node->string_table_block->string_table;
|
|
| 139 | + const IpeBufferEntry *ent = &node->entries_block->entries[idx];
|
|
| 104 | 140 | return (InfoProvEnt) {
|
| 105 | 141 | .info = node->tables[idx],
|
| 106 | 142 | .prov = {
|
| 143 | + .info_prov_id = MAKE_IPE_KEY(node->node_id, idx),
|
|
| 107 | 144 | .table_name = &strings[ent->table_name],
|
| 108 | 145 | .closure_desc = ent->closure_desc,
|
| 109 | 146 | .ty_desc = &strings[ent->ty_desc],
|
| ... | ... | @@ -121,19 +158,23 @@ static InfoProvEnt ipeBufferEntryToIpe(const IpeBufferListNode *node, uint32_t i |
| 121 | 158 | static void traceIPEFromHashTable(void *data STG_UNUSED, StgWord key STG_UNUSED,
|
| 122 | 159 | const void *value) {
|
| 123 | 160 | const IpeMapEntry *map_ent = (const IpeMapEntry *)value;
|
| 124 | - const InfoProvEnt ipe = ipeBufferEntryToIpe(map_ent->node, map_ent->idx);
|
|
| 125 | - traceIPE(&ipe);
|
|
| 161 | + if (ipe_node_valid(map_ent->node)){
|
|
| 162 | + const InfoProvEnt ipe = ipeBufferEntryToIpe(map_ent->node, map_ent->idx);
|
|
| 163 | + traceIPE(&ipe);
|
|
| 164 | + }
|
|
| 126 | 165 | }
|
| 127 | 166 | |
| 128 | 167 | void dumpIPEToEventLog(void) {
|
| 129 | 168 | // Dump pending entries
|
| 130 | 169 | IpeBufferListNode *node = RELAXED_LOAD(&ipeBufferList);
|
| 131 | 170 | while (node != NULL) {
|
| 132 | - decompressIPEBufferListNodeIfCompressed(node);
|
|
| 171 | + if (ipe_node_valid(node)){
|
|
| 172 | + decompressIPEBufferListNodeIfCompressed(node);
|
|
| 133 | 173 | |
| 134 | - for (uint32_t i = 0; i < node->count; i++) {
|
|
| 135 | - const InfoProvEnt ent = ipeBufferEntryToIpe(node, i);
|
|
| 136 | - traceIPE(&ent);
|
|
| 174 | + for (uint32_t i = 0; i < node->count; i++) {
|
|
| 175 | + const InfoProvEnt ent = ipeBufferEntryToIpe(node, i);
|
|
| 176 | + traceIPE(&ent);
|
|
| 177 | + }
|
|
| 137 | 178 | }
|
| 138 | 179 | node = node->next;
|
| 139 | 180 | }
|
| ... | ... | @@ -170,6 +211,8 @@ void registerInfoProvList(IpeBufferListNode *node) { |
| 170 | 211 | while (true) {
|
| 171 | 212 | IpeBufferListNode *old = RELAXED_LOAD(&ipeBufferList);
|
| 172 | 213 | node->next = old;
|
| 214 | + uint32_t module_id = next_module_id++;
|
|
| 215 | + node->node_id = module_id;
|
|
| 173 | 216 | if (cas_ptr((volatile void **) &ipeBufferList, old, node) == (void *) old) {
|
| 174 | 217 | return;
|
| 175 | 218 | }
|
| ... | ... | @@ -183,7 +226,7 @@ void formatClosureDescIpe(const InfoProvEnt *ipe_buf, char *str_buf) { |
| 183 | 226 | bool lookupIPE(const StgInfoTable *info, InfoProvEnt *out) {
|
| 184 | 227 | updateIpeMap();
|
| 185 | 228 | IpeMapEntry *map_ent = (IpeMapEntry *) lookupHashTable(ipeMap, (StgWord)info);
|
| 186 | - if (map_ent) {
|
|
| 229 | + if (map_ent && ipe_node_valid(map_ent->node)) {
|
|
| 187 | 230 | *out = ipeBufferEntryToIpe(map_ent->node, map_ent->idx);
|
| 188 | 231 | return true;
|
| 189 | 232 | } else {
|
| ... | ... | @@ -191,6 +234,18 @@ bool lookupIPE(const StgInfoTable *info, InfoProvEnt *out) { |
| 191 | 234 | }
|
| 192 | 235 | }
|
| 193 | 236 | |
| 237 | +// Returns 0 when the info table is not present in the info table map.
|
|
| 238 | +// See Note [Stable identifiers for IPE entries]
|
|
| 239 | +uint64_t lookupIPEId(const StgInfoTable *info) {
|
|
| 240 | + updateIpeMap();
|
|
| 241 | + IpeMapEntry *map_ent = (IpeMapEntry *) lookupHashTable(ipeMap, (StgWord)(info));
|
|
| 242 | + if (map_ent){
|
|
| 243 | + return IPE_ENTRY_KEY(*map_ent);
|
|
| 244 | + } else {
|
|
| 245 | + return 0;
|
|
| 246 | + }
|
|
| 247 | +}
|
|
| 248 | + |
|
| 194 | 249 | void updateIpeMap(void) {
|
| 195 | 250 | // Check if there's any work at all. If not so, we can circumvent locking,
|
| 196 | 251 | // which decreases performance.
|
| ... | ... | @@ -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");
|
| ... | ... | @@ -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);
|
| ... | ... | @@ -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;
|
| ... | ... | @@ -63,9 +64,26 @@ typedef struct { |
| 63 | 64 | |
| 64 | 65 | GHC_STATIC_ASSERT(sizeof(IpeBufferEntry) % (WORD_SIZE_IN_BITS / 8) == 0, "sizeof(IpeBufferEntry) must be a multiple of the word size");
|
| 65 | 66 | |
| 67 | +// The magic word is IPE\nIPE\n, which occupies the full 64 bit width of a word.
|
|
| 68 | +// See Note [IPE Stripping and magic words]
|
|
| 69 | +#define IPE_MAGIC_WORD 0x4950450049504500UL
|
|
| 70 | + |
|
| 71 | +typedef struct {
|
|
| 72 | + StgWord magic; // Must be IPE_MAGIC_WORD
|
|
| 73 | + IpeBufferEntry entries[]; // Flexible array member
|
|
| 74 | +} IpeBufferEntryBlock;
|
|
| 75 | + |
|
| 76 | +typedef struct {
|
|
| 77 | + StgWord magic; // Must be IPE_MAGIC_WORD
|
|
| 78 | + char string_table[]; // Flexible array member for string table
|
|
| 79 | +} IpeStringTableBlock;
|
|
| 80 | + |
|
| 66 | 81 | typedef struct IpeBufferListNode_ {
|
| 67 | 82 | struct IpeBufferListNode_ *next;
|
| 68 | 83 | |
| 84 | + // This field is filled in when the node is registered.
|
|
| 85 | + uint32_t node_id;
|
|
| 86 | + |
|
| 69 | 87 | // Everything below is read-only and generated by the codegen
|
| 70 | 88 | |
| 71 | 89 | // This flag should be treated as a boolean
|
| ... | ... | @@ -76,10 +94,10 @@ typedef struct IpeBufferListNode_ { |
| 76 | 94 | // When TNTC is enabled, these will point to the entry code
|
| 77 | 95 | // not the info table itself.
|
| 78 | 96 | const StgInfoTable **tables;
|
| 79 | - IpeBufferEntry *entries;
|
|
| 97 | + IpeBufferEntryBlock *entries_block;
|
|
| 80 | 98 | StgWord entries_size; // decompressed size
|
| 81 | 99 | |
| 82 | - const char *string_table;
|
|
| 100 | + const IpeStringTableBlock *string_table_block;
|
|
| 83 | 101 | StgWord string_table_size; // decompressed size
|
| 84 | 102 | |
| 85 | 103 | // Shared by all entries
|
| ... | ... | @@ -98,6 +116,8 @@ void formatClosureDescIpe(const InfoProvEnt *ipe_buf, char *str_buf); |
| 98 | 116 | // Returns true on success, initializes `out`.
|
| 99 | 117 | bool lookupIPE(const StgInfoTable *info, InfoProvEnt *out);
|
| 100 | 118 | |
| 119 | +uint64_t lookupIPEId(const StgInfoTable *info);
|
|
| 120 | + |
|
| 101 | 121 | #if defined(DEBUG)
|
| 102 | 122 | void printIPE(const StgInfoTable *info);
|
| 103 | 123 | #endif |
| ... | ... | @@ -48,7 +48,8 @@ HaskellObj shouldFindOneIfItHasBeenRegistered(Capability *cap) { |
| 48 | 48 | // Allocate buffers for IPE buffer list node
|
| 49 | 49 | IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
|
| 50 | 50 | node->tables = malloc(sizeof(StgInfoTable *));
|
| 51 | - node->entries = malloc(sizeof(IpeBufferEntry));
|
|
| 51 | + node->entries_block = malloc(sizeof(StgWord64) + sizeof(IpeBufferEntry));
|
|
| 52 | + node->entries_block->magic = IPE_MAGIC_WORD;
|
|
| 52 | 53 | |
| 53 | 54 | StringTable st;
|
| 54 | 55 | init_string_table(&st);
|
| ... | ... | @@ -61,9 +62,13 @@ HaskellObj shouldFindOneIfItHasBeenRegistered(Capability *cap) { |
| 61 | 62 | node->compressed = 0;
|
| 62 | 63 | node->count = 1;
|
| 63 | 64 | node->tables[0] = get_itbl(fortyTwo);
|
| 64 | - node->entries[0] = makeAnyProvEntry(cap, &st, 42);
|
|
| 65 | + node->entries_block->entries[0] = makeAnyProvEntry(cap, &st, 42);
|
|
| 65 | 66 | node->entries_size = sizeof(IpeBufferEntry);
|
| 66 | - node->string_table = st.buffer;
|
|
| 67 | + |
|
| 68 | + IpeStringTableBlock *string_table_block = malloc(sizeof(StgWord64) + st.size);
|
|
| 69 | + string_table_block->magic = IPE_MAGIC_WORD;
|
|
| 70 | + memcpy(string_table_block->string_table, st.buffer, st.size);
|
|
| 71 | + node->string_table_block = string_table_block;
|
|
| 67 | 72 | node->string_table_size = st.size;
|
| 68 | 73 | |
| 69 | 74 | registerInfoProvList(node);
|
| ... | ... | @@ -90,7 +95,8 @@ void shouldFindTwoIfTwoHaveBeenRegistered(Capability *cap, |
| 90 | 95 | // Allocate buffers for IPE buffer list node
|
| 91 | 96 | IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
|
| 92 | 97 | node->tables = malloc(sizeof(StgInfoTable *));
|
| 93 | - node->entries = malloc(sizeof(IpeBufferEntry));
|
|
| 98 | + node->entries_block = malloc(sizeof(StgWord64) + sizeof(IpeBufferEntry));
|
|
| 99 | + node->entries_block->magic = IPE_MAGIC_WORD;
|
|
| 94 | 100 | |
| 95 | 101 | StringTable st;
|
| 96 | 102 | init_string_table(&st);
|
| ... | ... | @@ -103,9 +109,12 @@ void shouldFindTwoIfTwoHaveBeenRegistered(Capability *cap, |
| 103 | 109 | node->compressed = 0;
|
| 104 | 110 | node->count = 1;
|
| 105 | 111 | node->tables[0] = get_itbl(twentyThree);
|
| 106 | - node->entries[0] = makeAnyProvEntry(cap, &st, 23);
|
|
| 112 | + node->entries_block->entries[0] = makeAnyProvEntry(cap, &st, 23);
|
|
| 107 | 113 | node->entries_size = sizeof(IpeBufferEntry);
|
| 108 | - node->string_table = st.buffer;
|
|
| 114 | + IpeStringTableBlock *string_table_block = malloc(sizeof(StgWord64) + st.size);
|
|
| 115 | + string_table_block->magic = IPE_MAGIC_WORD;
|
|
| 116 | + memcpy(string_table_block->string_table, st.buffer, st.size);
|
|
| 117 | + node->string_table_block = string_table_block;
|
|
| 109 | 118 | node->string_table_size = st.size;
|
| 110 | 119 | |
| 111 | 120 | registerInfoProvList(node);
|
| ... | ... | @@ -121,7 +130,8 @@ void shouldFindTwoFromTheSameList(Capability *cap) { |
| 121 | 130 | // Allocate buffers for IPE buffer list node
|
| 122 | 131 | IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
|
| 123 | 132 | node->tables = malloc(sizeof(StgInfoTable *) * 2);
|
| 124 | - node->entries = malloc(sizeof(IpeBufferEntry) * 2);
|
|
| 133 | + node->entries_block = malloc(sizeof(StgWord64) + sizeof(IpeBufferEntry) * 2);
|
|
| 134 | + node->entries_block->magic = IPE_MAGIC_WORD;
|
|
| 125 | 135 | |
| 126 | 136 | StringTable st;
|
| 127 | 137 | init_string_table(&st);
|
| ... | ... | @@ -133,10 +143,13 @@ void shouldFindTwoFromTheSameList(Capability *cap) { |
| 133 | 143 | node->count = 2;
|
| 134 | 144 | node->tables[0] = get_itbl(one);
|
| 135 | 145 | node->tables[1] = get_itbl(two);
|
| 136 | - node->entries[0] = makeAnyProvEntry(cap, &st, 1);
|
|
| 137 | - node->entries[1] = makeAnyProvEntry(cap, &st, 2);
|
|
| 146 | + node->entries_block->entries[0] = makeAnyProvEntry(cap, &st, 1);
|
|
| 147 | + node->entries_block->entries[1] = makeAnyProvEntry(cap, &st, 2);
|
|
| 138 | 148 | node->entries_size = sizeof(IpeBufferEntry) * 2;
|
| 139 | - node->string_table = st.buffer;
|
|
| 149 | + IpeStringTableBlock *string_table_block = malloc(sizeof(StgWord64) + st.size);
|
|
| 150 | + string_table_block->magic = IPE_MAGIC_WORD;
|
|
| 151 | + memcpy(string_table_block->string_table, st.buffer, st.size);
|
|
| 152 | + node->string_table_block = string_table_block;
|
|
| 140 | 153 | node->string_table_size = st.size;
|
| 141 | 154 | |
| 142 | 155 | registerInfoProvList(node);
|
| ... | ... | @@ -152,7 +165,11 @@ void shouldDealWithAnEmptyList(Capability *cap, HaskellObj fortyTwo) { |
| 152 | 165 | IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
|
| 153 | 166 | node->count = 0;
|
| 154 | 167 | node->next = NULL;
|
| 155 | - node->string_table = "";
|
|
| 168 | + IpeStringTableBlock *string_table_block = malloc(sizeof(StgWord64));
|
|
| 169 | + string_table_block->magic = IPE_MAGIC_WORD;
|
|
| 170 | + |
|
| 171 | + node->entries_block = malloc(sizeof(StgWord64));
|
|
| 172 | + node->entries_block->magic = IPE_MAGIC_WORD;
|
|
| 156 | 173 | |
| 157 | 174 | registerInfoProvList(node);
|
| 158 | 175 |
| ... | ... | @@ -64,7 +64,8 @@ IpeBufferListNode *makeAnyProvEntries(Capability *cap, int start, int end) { |
| 64 | 64 | // Allocate buffers for IpeBufferListNode
|
| 65 | 65 | IpeBufferListNode *node = malloc(sizeof(IpeBufferListNode));
|
| 66 | 66 | node->tables = malloc(sizeof(StgInfoTable *) * n);
|
| 67 | - node->entries = malloc(sizeof(IpeBufferEntry) * n);
|
|
| 67 | + node->entries_block = malloc(sizeof(StgWord64) + sizeof(IpeBufferEntry) * n);
|
|
| 68 | + node->entries_block->magic = IPE_MAGIC_WORD;
|
|
| 68 | 69 | |
| 69 | 70 | StringTable st;
|
| 70 | 71 | init_string_table(&st);
|
| ... | ... | @@ -83,14 +84,19 @@ IpeBufferListNode *makeAnyProvEntries(Capability *cap, int start, int end) { |
| 83 | 84 | for (int i=start; i < end; i++) {
|
| 84 | 85 | HaskellObj closure = rts_mkInt(cap, 42);
|
| 85 | 86 | node->tables[i] = get_itbl(closure);
|
| 86 | - node->entries[i] = makeAnyProvEntry(cap, &st, i);
|
|
| 87 | + node->entries_block->entries[i] = makeAnyProvEntry(cap, &st, i);
|
|
| 87 | 88 | }
|
| 88 | 89 | |
| 89 | 90 | // Set the rest of the fields
|
| 90 | 91 | node->next = NULL;
|
| 91 | 92 | node->compressed = 0;
|
| 92 | 93 | node->count = n;
|
| 93 | - node->string_table = st.buffer;
|
|
| 94 | + |
|
| 95 | + IpeStringTableBlock *string_table_block =
|
|
| 96 | + malloc(sizeof(StgWord64) + st.size);
|
|
| 97 | + string_table_block->magic = IPE_MAGIC_WORD;
|
|
| 98 | + memcpy(string_table_block->string_table, st.buffer, st.size);
|
|
| 99 | + node->string_table_block = string_table_block;
|
|
| 94 | 100 | |
| 95 | 101 | return node;
|
| 96 | 102 | } |