-
Notifications
You must be signed in to change notification settings - Fork 85
Description
Problem Description
We're implementing a garbage collector for an interpreter written in C++ and have found that the AMC (Automatic Mostly-Copying) pool appears to be fundamentally incompatible with C++ virtual functions. The AMS (Automatic Mark-Sweep) pool works perfectly.
Setup
- MPS version: 1.118 (latest from master)
- Language: C++ with extensive use of virtual functions
- Platform: Linux x86_64 (Gentoo)
- Compiler: g++ 11.2.0
What We've Tried
-
Implemented all required format methods for AMC:
scan- scans object referencesskip- returns next object address (reads size from object field, no virtual calls)fwd- stores forwarding pointer in vtable slot with bit 0 setisfwd- checks forwarding bitpad- creates padding objects
-
Added object size field to base class to avoid virtual function calls during GC:
class Instance { public: size_t mps_object_size; // Set by Alloc() // ... vtable pointer is here implicitly
-
Fixed signal handler conflict - we were overriding SIGSEGV which MPS needs for write barriers
-
Changed to ambiguous roots - Changed from
mps_rank_exact()tomps_rank_ambig()based on Configura's documentation -
Registered thread stack - Using
mps_root_create_thread()which should handle stack/register ambiguity
The Problem
Even with these fixes, AMC crashes when regular application code tries to access objects. The crash occurs when calling virtual functions:
#0 Instance::ClassPtr() - tries to call virtual ClassDesc()
#1 Instance::IsSpecies() - calls ClassPtr()
#2 Symbol::IsEqual() - virtual function
#3 Table::Lookup() - during normal operation
Our Understanding - The Fundamental Conflict
After extensive research, we've identified what appears to be a fundamental incompatibility:
- MPS stores forwarding pointers in the first word of objects (as documented in your Scheme example)
- C++ stores vtable pointers in the first word of objects with virtual functions
- When AMC moves an object, it overwrites the vtable with a forwarding pointer
- Result: Virtual function calls crash because the vtable pointer is destroyed
We found NO documentation addressing this conflict:
- The MPS guide only shows a C example with manual type dispatch
- No C++ examples with virtual functions exist in the MPS repository
- No open-source C++ projects using MPS with virtual functions found
- This vtable-forwarding conflict is not mentioned anywhere in MPS docs
Questions
-
Is AMC designed to work with C++ virtual functions at all? The Scheme example uses C with manual type dispatch via switch statements.
-
If AMC can work with C++, what's the correct approach? Do we need to:
- Check for forwarding before every object access?
- Use some MPS API to "fix" pointers before use?
- Structure our code differently?
-
Is this a documentation gap? Should the AMC documentation explicitly state it's incompatible with C++ virtual functions?
Minimal Reproduction
// With MPS_POOL_TYPE=AMC environment variable
Instance* obj = Symbol::Intern("test"); // Allocates via MPS
// ... later, after GC might have run ...
obj->IsEqual(other); // Virtual function call - CRASHWorking Alternative
AMS pool works perfectly with the same codebase:
- No write barriers
- No object relocation
- Virtual functions work normally
What We Need
-
Is it possible to use AMC with C++ virtual functions? Given the vtable-forwarding conflict, it seems fundamentally incompatible.
-
How does Configura handle this? They claim to use MPS with C++. Do they:
- Avoid virtual functions entirely?
- Use a modified MPS that doesn't overwrite the first word?
- Have a different C++ object layout?
- Use some undocumented workaround?
-
What are our options? Should we:
- Give up on AMC and stick with AMS (which works)?
- Redesign our C++ code to avoid virtual functions?
- Modify MPS to handle vtables differently?
- Use headers to offset the vtable from the forwarding location?
Request
Please clarify whether AMC can work with C++ virtual functions, given that:
- Forwarding pointers overwrite the first word (vtable pointer)
- This breaks virtual dispatch permanently
If it IS possible, please provide guidance on the correct approach. If it's NOT possible, this should be documented as a known limitation for C++ users.
Thank you for maintaining this excellent memory management system!