Description
FAF's custom allocator identifies itself as HEAPMNG Ver.0.87.
Under severe late-game workloads, Heap Total can grow in approximately 31-32 MiB steps while Heap Committed remains substantially lower. Reverse engineering identified two allocator bounds that are relevant to complete 4 GiB page bookkeeping and high-address free-region coalescing.
Relevant locations in the tested executable:
- initialization near
0x00957E00;
- page-map allocation immediate at
0x00957E35;
- right-neighbour coalescing bound at
0x00958107;
- growth handling near
0x00958400;
- page-to-region descriptor fill at
0x0095854F (REP STOS).
Game-level evidence and run comparison:
FAForever/fa#7195
Analysis
Page-map allocation
A 32-bit address space contains:
4 KiB page indices.
At four bytes per page-map entry, the arithmetic full-range capacity is:
0x100000 * 4 = 0x400000 bytes
The tested code currently allocates 0x3FF000 bytes at 0x00957E35.
Right-neighbour coalescing bound
The tested free/coalescing path compares the end page against 0xC0000, which corresponds to 3 GiB.
The candidate changes the bound to 0x100000, the one-past-end page index for the full 4 GiB range. This permits right-neighbour lookup for pages in the 3-4 GiB range while still skipping lookup at exactly 0x100000.
The original design intent of the smaller values is not documented. They may encode a legacy 3 GiB address-space assumption, a sentinel, or an intentionally excluded top range. This must be verified before official integration.
Course of Action
Apply the following two immediate-value changes to the tested executable family.
Change 1
Virtual address:
Instruction:
PUSH 0x003FF000
->
PUSH 0x00400000
Bytes:
68 00 F0 3F 00
->
68 00 00 40 00
Change 2
Virtual address:
Instruction:
CMP EAX,0x000C0000
->
CMP EAX,0x00100000
Bytes:
3D 00 00 0C 00
->
3D 00 00 10 00
Tested local baseline:
size: 12,541,952 bytes
SHA256: db05b62768fd1cea4cbc2fdce4bbf0778fe85353cd1fc3b1048db330914d08b6
Allocator candidate:
size: 12,541,952 bytes
SHA256: 22cb537385a402ad2ad1671c5cc63da8a1a29e9af9c91769d6dc0ddc27ac855e
The local baseline already contains unrelated tread-animation and particle-capacity changes. Those changes are not part of this proposal and did not alter the two HEAPMNG instruction locations.
Relative to that baseline, the candidate changes only the two immediate values above plus the PE checksum.
Evidence
The allocator candidate was used in the lowest-growth documented run together with a private same-unit/same-tick M28 queue-length cache:
Heap Total / Committed = 1.406 GiB / approximately 1.30-1.33 GiB
A separate cache-active run reached:
1.930 GiB / approximately 1.24-1.26 GiB
The candidate's independent contribution is not isolated, but it is part of a configuration that used approximately 0.524 GiB less Heap Total at the selected comparison point.
The candidate was also tested as a universal high-memory fix and did not make every path above roughly 2 GiB safe. It should therefore be treated as a bookkeeping/reuse candidate, not as a universal 4 GiB crash fix.
Validation already completed
- PE entry point and section-table integrity;
- Large Address Aware preservation;
- independent PE checksum recalculation;
- GNU and LLVM disassembly agreement;
- full-file byte-difference audit;
- preservation of the existing local baseline changes;
- full-range page-map arithmetic;
- 4 GiB one-past-end boundary checks;
- randomized virtual-region stress-model coverage.
These are static/model checks. They do not replace Windows runtime instrumentation.
Test Plan
Instrument the following before deciding on integration:
Growth path
- requested reserve and commit sizes;
VirtualAlloc return address;
- first and last page indices;
- current Heap Total and Committed;
- largest reusable free region before growth.
Page-map fill around 0x0095854F
- map base;
- destination start and end;
- descriptor value;
- page count;
- whether the write remains within the allocated map.
Free/coalescing path
- freed region base and size;
- left and right neighbour page indices;
- whether a lookup is skipped by a bound;
- neighbour free/allocated state;
- merge result;
- all events at or above page index
0xC0000.
Run the four-cell test matrix defined in fa#7195, at least three repetitions per cell.
Pass criteria:
- consistently lower Heap Total without lower simulation correctness;
- no out-of-range page-map access;
- no free-list corruption;
- no invalid merge;
- no vanilla or modded long-game regression.
Out of scope
- The M28 cache implementation is tracked separately in
maudlin27/M28AI#394.
- Failed Lua-GC experiments and external-module crash diagnostics are preserved in the technical archive and are not part of this allocator patch proposal.
Attachment
UPLOAD_2_BINARY_TECHNICAL_EVIDENCE.zip
Description
FAF's custom allocator identifies itself as
HEAPMNG Ver.0.87.Under severe late-game workloads, Heap Total can grow in approximately 31-32 MiB steps while Heap Committed remains substantially lower. Reverse engineering identified two allocator bounds that are relevant to complete 4 GiB page bookkeeping and high-address free-region coalescing.
Relevant locations in the tested executable:
0x00957E00;0x00957E35;0x00958107;0x00958400;0x0095854F(REP STOS).Game-level evidence and run comparison:
FAForever/fa#7195
Analysis
Page-map allocation
A 32-bit address space contains:
4 KiB page indices.
At four bytes per page-map entry, the arithmetic full-range capacity is:
The tested code currently allocates
0x3FF000bytes at0x00957E35.Right-neighbour coalescing bound
The tested free/coalescing path compares the end page against
0xC0000, which corresponds to 3 GiB.The candidate changes the bound to
0x100000, the one-past-end page index for the full 4 GiB range. This permits right-neighbour lookup for pages in the 3-4 GiB range while still skipping lookup at exactly0x100000.The original design intent of the smaller values is not documented. They may encode a legacy 3 GiB address-space assumption, a sentinel, or an intentionally excluded top range. This must be verified before official integration.
Course of Action
Apply the following two immediate-value changes to the tested executable family.
Change 1
Virtual address:
Instruction:
Bytes:
Change 2
Virtual address:
Instruction:
Bytes:
Tested local baseline:
Allocator candidate:
The local baseline already contains unrelated tread-animation and particle-capacity changes. Those changes are not part of this proposal and did not alter the two HEAPMNG instruction locations.
Relative to that baseline, the candidate changes only the two immediate values above plus the PE checksum.
Evidence
The allocator candidate was used in the lowest-growth documented run together with a private same-unit/same-tick M28 queue-length cache:
A separate cache-active run reached:
The candidate's independent contribution is not isolated, but it is part of a configuration that used approximately
0.524 GiBless Heap Total at the selected comparison point.The candidate was also tested as a universal high-memory fix and did not make every path above roughly 2 GiB safe. It should therefore be treated as a bookkeeping/reuse candidate, not as a universal 4 GiB crash fix.
Validation already completed
These are static/model checks. They do not replace Windows runtime instrumentation.
Test Plan
Instrument the following before deciding on integration:
Growth path
VirtualAllocreturn address;Page-map fill around
0x0095854FFree/coalescing path
0xC0000.Run the four-cell test matrix defined in
fa#7195, at least three repetitions per cell.Pass criteria:
Out of scope
maudlin27/M28AI#394.Attachment
UPLOAD_2_BINARY_TECHNICAL_EVIDENCE.zip