Skip to content

Commit 14c254e

Browse files
emerybergerclaude
andcommitted
feat: add Windows platform support
Add Windows port of the Mesh allocator with the following features: - Platform abstraction layer (src/platform/) for cross-platform memory operations - VirtualAlloc2/MapViewOfFile3 for page-granular meshing on Windows 10 1803+ - MSVC compiler compatibility (intrinsics, attributes, types) - Windows threading (TLS via TlsAlloc, background mesh thread) - Vectored Exception Handler for mesh write barriers - CMake build support for Windows (mesh.dll, mesh_static.lib) - Windows-specific memory stats (GetProcessMemoryInfo) - Malloc-free debug printf using vendored printf library New files: - src/platform/vmem_windows.cc - Modern Windows memory APIs - src/platform/exception_handler_windows.cc - VEH for meshing - src/runtime_windows.cc - Windows-specific runtime - src/memory_stats_windows.cc - RSS measurement - src/testing/fragmenter_windows.cc - Windows test - src/debug_printf.h, printf.c, putchar.c - Malloc-free debug output Build: cmake -B build-win && cmake --build build-win --config Release 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 757d0f9 commit 14c254e

34 files changed

+3886
-88
lines changed

CLAUDE.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,100 @@ Randomized allocation only. Useful for:
330330
### Platform Support
331331
- **Linux**: Full support (primary platform)
332332
- **macOS**: Full support
333+
- **Windows**: Experimental support (allocation only, no meshing yet)
333334
- **64-bit only**: Current implementation requires 64-bit address space
334335
336+
### Windows Port Notes
337+
338+
The Windows port required significant platform abstraction work. Key learnings:
339+
340+
#### MSVC Compiler Compatibility (src/common.h)
341+
- `__PRETTY_FUNCTION__` → `__FUNCSIG__`
342+
- `ssize_t` not available; use `SSIZE_T` from `<BaseTsd.h>`
343+
- GCC builtins need reimplementation using MSVC intrinsics:
344+
- `__builtin_popcountl` → `__popcnt64` (from `<intrin.h>`)
345+
- `__builtin_ctzl` → `_BitScanForward64`
346+
- `__builtin_clzl` → `_BitScanReverse64`
347+
- `__builtin_ffsl` → Custom implementation using `_BitScanForward64`
348+
- `__builtin_prefetch` → No-op (performance hint only)
349+
- `__builtin_unreachable` → `__assume(0)`
350+
- **Attribute placement**: MSVC's `__declspec(align(x))` and `__declspec(restrict)` must come BEFORE the type, but GCC/Clang's `__attribute__` can come after. Made these empty macros since code uses GCC-style placement.
351+
- `__restrict__` → `__restrict`
352+
- `pid_t` not available; typedef to `int`
353+
354+
#### Threading (src/thread_local_heap.h)
355+
- pthread API not available on Windows; implemented compatibility layer:
356+
- `pthread_t` → `DWORD` (thread ID)
357+
- `pthread_key_t` → `DWORD` (TLS index)
358+
- `pthread_key_create` → `TlsAlloc`
359+
- `pthread_getspecific` → `TlsGetValue`
360+
- `pthread_setspecific` → `TlsSetValue`
361+
- `pthread_self` → `GetCurrentThreadId`
362+
363+
#### Memory Management
364+
- `mmap`/`munmap` → `VirtualAlloc`/`VirtualFree` (src/platform/vmem_windows.cc)
365+
- `madvise(MADV_DONTNEED)` → `DiscardVirtualMemory` or `VirtualAlloc(MEM_RESET)`
366+
- `mprotect` → `VirtualProtect`
367+
- No `fork()` on Windows; fork-related code wrapped in `#if !defined(_WIN32)`
368+
369+
#### Locking (src/internal.h)
370+
- `PosixLockType` not available; created `WindowsLockedHeap` wrapper using `std::mutex`
371+
372+
#### Exception Handling (src/platform/exception_handler_windows.cc)
373+
- Unix signal handlers (`SIGSEGV`, `SIGBUS`) → Windows Vectored Exception Handler (VEH)
374+
- Installed via `AddVectoredExceptionHandler` in DllMain
375+
376+
#### Build System
377+
- **Use CMake for Windows builds** (not Bazel)
378+
- CMake used for cross-platform builds
379+
- Windows libraries needed: `kernel32`, `psapi`, `advapi32`
380+
- Disable CMake auto-export for DLL; use explicit `__declspec(dllexport)` via `MESH_EXPORT` macro
381+
- Build commands:
382+
```bash
383+
# Configure (from repo root)
384+
cmake -B build-win -DCMAKE_BUILD_TYPE=Release
385+
386+
# Build
387+
cmake --build build-win --config Release
388+
389+
# Test executable location
390+
build-win/bin/Release/fragmenter_test.exe
391+
```
392+
393+
#### Windows Meshing Implementation (Win10 1803+)
394+
395+
Full meshing is now supported on Windows 10 version 1803 and later using modern memory APIs:
396+
397+
**Key APIs Used**:
398+
- `VirtualAlloc2` with `MEM_RESERVE_PLACEHOLDER` - Reserves address space as placeholder
399+
- `MapViewOfFile3` with `MEM_REPLACE_PLACEHOLDER` - Maps file section into placeholder with page-granular offsets
400+
- `UnmapViewOfFile2` with `MEM_PRESERVE_PLACEHOLDER` - Unmaps while preserving placeholder
401+
402+
**How Meshing Works on Windows**:
403+
1. Arena created with `CreateFileMappingW(INVALID_HANDLE_VALUE, ...)` (pagefile-backed)
404+
2. Address space reserved as placeholder via `VirtualAlloc2`
405+
3. Initial mapping via `MapViewOfFile3` into placeholder
406+
4. During mesh: `mapSharedFixed()` remaps virtual address to different physical offset
407+
5. Two virtual addresses now share same physical memory
408+
409+
**Fallback for Older Windows**:
410+
- `hasPageGranularMapping()` returns false on pre-1803 Windows
411+
- Meshing disabled, allocation-only mode used
412+
- Legacy `MapViewOfFileEx` requires 64KB-aligned offsets (incompatible with 4KB page meshing)
413+
414+
#### Current Limitations on Windows
415+
- **No fork handling**: Windows doesn't have `fork()`
416+
- **No background mesh thread**: Mesh triggering via `epoll_wait`/`recv` interception not applicable
417+
- **Requires Win10 1803+**: Older Windows versions fall back to allocation-only mode
418+
419+
#### Key Windows-Specific Files
420+
- `src/runtime_windows.cc` - SizeMap data, measurePssKiB implementation
421+
- `src/memory_stats_windows.cc` - GetProcessMemoryInfo wrapper
422+
- `src/platform/vmem_windows.cc` - Modern API support (VirtualAlloc2, MapViewOfFile3), memory operations
423+
- `src/platform/exception_handler_windows.cc` - Vectored Exception Handler for meshing faults
424+
- `src/platform/platform.h` - Platform detection macros and FileHandle abstraction
425+
- `src/platform/vmem.h` - Cross-platform memory API declarations
426+
335427
### Integration
336428
- **Drop-in replacement**: No code changes required
337429
- **Standard API**: Full malloc/free/realloc/memalign support

CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
cmake_minimum_required(VERSION 3.13.0)
2-
3-
project(Mesh CXX C)
1+
cmake_minimum_required(VERSION 3.16)
42

3+
project(Mesh VERSION 1.0.0 LANGUAGES CXX C)
54

65
SET(CMAKE_BUILD_TYPE "" CACHE STRING "Just making sure the default CMAKE_BUILD_TYPE configurations won't interfere" FORCE)
76
set(CMAKE_C_STANDARD 11)
87
set(CMAKE_CXX_STANDARD 17)
8+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
99

1010
#Set output folders
1111
set(CMAKE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)

src/BUILD

Lines changed: 105 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,19 @@ config_setting(
4949
visibility = ["//visibility:private"],
5050
)
5151

52-
NO_BUILTIN_MALLOC = [
53-
"-fno-builtin-malloc",
54-
"-fno-builtin-free",
55-
]
52+
config_setting(
53+
name = "windows",
54+
constraint_values = ["@platforms//os:windows"],
55+
visibility = ["//visibility:private"],
56+
)
57+
58+
NO_BUILTIN_MALLOC = select({
59+
":windows": [], # MSVC doesn't use these flags
60+
"//conditions:default": [
61+
"-fno-builtin-malloc",
62+
"-fno-builtin-free",
63+
],
64+
})
5665

5766
COMMON_DEFINES = [] + select({
5867
":disable_meshing": ["MESHING_ENABLED=0"],
@@ -75,11 +84,14 @@ COMMON_DEFINES = [] + select({
7584
"//conditions:default": [],
7685
})
7786

78-
COMMON_LINKOPTS = [
79-
"-lm",
80-
"-lpthread",
81-
"-ldl",
82-
] + select({
87+
COMMON_LINKOPTS = select({
88+
":windows": [], # Windows uses different linking model
89+
"//conditions:default": [
90+
"-lm",
91+
"-lpthread",
92+
"-ldl",
93+
],
94+
}) + select({
8395
"@platforms//os:linux": [
8496
"-Wl,--no-as-needed",
8597
"-Wl,--no-add-needed",
@@ -101,6 +113,16 @@ COMMON_LINKOPTS = [
101113
"//conditions:default": [],
102114
})
103115

116+
# Windows-specific link libraries
117+
WINDOWS_LINKOPTS = select({
118+
":windows": [
119+
"kernel32.lib",
120+
"psapi.lib",
121+
"advapi32.lib",
122+
],
123+
"//conditions:default": [],
124+
})
125+
104126
ARCH_LINKOPTS = select({
105127
"@platforms//cpu:x86_64": [
106128
"-march=westmere",
@@ -132,17 +154,29 @@ cc_library(
132154
"global_heap.cc",
133155
"measure_rss.cc",
134156
"meshable_arena.cc",
135-
"real.cc",
136-
"runtime.cc",
137157
"thread_local_heap.cc",
138158
] + select({
139-
"@platforms//os:macos": ["memory_stats_macos.cc"],
140-
"@platforms//os:linux": ["memory_stats_linux.cc"],
159+
"@platforms//os:macos": [
160+
"memory_stats_macos.cc",
161+
"real.cc",
162+
"runtime.cc",
163+
],
164+
"@platforms//os:linux": [
165+
"memory_stats_linux.cc",
166+
"real.cc",
167+
"runtime.cc",
168+
],
169+
":windows": [
170+
"memory_stats_windows.cc",
171+
"platform/vmem_windows.cc",
172+
"platform/exception_handler_windows.cc",
173+
],
141174
"//conditions:default": [],
142175
}),
143176
hdrs = glob([
144177
"*.h",
145178
"plasma/*.h",
179+
"platform/*.h",
146180
"rng/*.h",
147181
"gnu_wrapper.cc",
148182
"mac_wrapper.cc",
@@ -152,7 +186,7 @@ cc_library(
152186
]),
153187
copts = NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS,
154188
defines = COMMON_DEFINES,
155-
linkopts = COMMON_LINKOPTS + ARCH_LINKOPTS + LTO_LINKOPTS,
189+
linkopts = COMMON_LINKOPTS + WINDOWS_LINKOPTS + ARCH_LINKOPTS + LTO_LINKOPTS,
156190
linkstatic = True,
157191
visibility = ["//visibility:private"],
158192
deps = [
@@ -329,14 +363,25 @@ MESH_SHARED_SRCS = [
329363
"libmesh.cc",
330364
"measure_rss.cc",
331365
"meshable_arena.cc",
366+
"thread_local_heap.cc",
367+
]
368+
369+
# Unix-specific sources (real.cc and runtime.cc contain pthread/signal handling)
370+
MESH_UNIX_SRCS = [
332371
"real.cc",
333372
"runtime.cc",
334-
"thread_local_heap.cc",
373+
]
374+
375+
# Windows-specific sources
376+
MESH_WINDOWS_SRCS = [
377+
"platform/vmem_windows.cc",
378+
"platform/exception_handler_windows.cc",
335379
]
336380

337381
MESH_SHARED_HDRS = glob([
338382
"*.h",
339383
"plasma/*.h",
384+
"platform/*.h",
340385
"rng/*.h",
341386
"static/*.h",
342387
])
@@ -359,7 +404,7 @@ cc_library(
359404

360405
cc_binary(
361406
name = "libmesh.dylib",
362-
srcs = MESH_SHARED_SRCS + [
407+
srcs = MESH_SHARED_SRCS + MESH_UNIX_SRCS + [
363408
"memory_stats_macos.cc",
364409
],
365410
copts = ["-Isrc"] + NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS,
@@ -375,7 +420,7 @@ cc_binary(
375420

376421
cc_binary(
377422
name = "libmesh.so",
378-
srcs = MESH_SHARED_SRCS + [
423+
srcs = MESH_SHARED_SRCS + MESH_UNIX_SRCS + [
379424
"memory_stats_linux.cc",
380425
],
381426
copts = ["-Isrc"] + NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS,
@@ -389,9 +434,27 @@ cc_binary(
389434
],
390435
)
391436

437+
# Windows DLL - uses alloc8 for CRT interposition
438+
# Note: On Windows, mesh is typically loaded via alloc8's DllMain mechanism
439+
cc_binary(
440+
name = "mesh.dll",
441+
srcs = MESH_SHARED_SRCS + MESH_WINDOWS_SRCS + [
442+
"memory_stats_windows.cc",
443+
],
444+
copts = ["-Isrc"] + NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS,
445+
defines = COMMON_DEFINES,
446+
linkopts = WINDOWS_LINKOPTS,
447+
linkshared = True,
448+
target_compatible_with = ["@platforms//os:windows"],
449+
visibility = ["//visibility:public"],
450+
deps = [
451+
":mesh-textual-hdrs",
452+
],
453+
)
454+
392455
cc_library(
393456
name = "mesh-static-macos",
394-
srcs = MESH_SHARED_SRCS + [
457+
srcs = MESH_SHARED_SRCS + MESH_UNIX_SRCS + [
395458
"memory_stats_macos.cc",
396459
],
397460
copts = ["-Isrc"] + NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS,
@@ -407,7 +470,7 @@ cc_library(
407470

408471
cc_library(
409472
name = "mesh-static-linux",
410-
srcs = MESH_SHARED_SRCS + [
473+
srcs = MESH_SHARED_SRCS + MESH_UNIX_SRCS + [
411474
"memory_stats_linux.cc",
412475
],
413476
copts = ["-Isrc"] + NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS,
@@ -421,6 +484,22 @@ cc_library(
421484
],
422485
)
423486

487+
cc_library(
488+
name = "mesh-static-windows",
489+
srcs = MESH_SHARED_SRCS + MESH_WINDOWS_SRCS + [
490+
"memory_stats_windows.cc",
491+
],
492+
copts = ["-Isrc"] + NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS,
493+
defines = COMMON_DEFINES,
494+
linkopts = WINDOWS_LINKOPTS,
495+
linkstatic = True,
496+
target_compatible_with = ["@platforms//os:windows"],
497+
visibility = ["//visibility:private"],
498+
deps = [
499+
":mesh-textual-hdrs",
500+
],
501+
)
502+
424503
cc_static_library(
425504
name = "mesh_static_macos",
426505
deps = [":mesh-static-macos"],
@@ -434,3 +513,10 @@ cc_static_library(
434513
target_compatible_with = ["@platforms//os:linux"],
435514
visibility = ["//visibility:public"],
436515
)
516+
517+
cc_static_library(
518+
name = "mesh_static_windows",
519+
deps = [":mesh-static-windows"],
520+
target_compatible_with = ["@platforms//os:windows"],
521+
visibility = ["//visibility:public"],
522+
)

0 commit comments

Comments
 (0)