Skip to content

AMC pool crashes with C++ virtual functions - need guidance #309

@tunerooster

Description

@tunerooster

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

  1. Implemented all required format methods for AMC:

    • scan - scans object references
    • skip - returns next object address (reads size from object field, no virtual calls)
    • fwd - stores forwarding pointer in vtable slot with bit 0 set
    • isfwd - checks forwarding bit
    • pad - creates padding objects
  2. 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
  3. Fixed signal handler conflict - we were overriding SIGSEGV which MPS needs for write barriers

  4. Changed to ambiguous roots - Changed from mps_rank_exact() to mps_rank_ambig() based on Configura's documentation

  5. 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:

  1. MPS stores forwarding pointers in the first word of objects (as documented in your Scheme example)
  2. C++ stores vtable pointers in the first word of objects with virtual functions
  3. When AMC moves an object, it overwrites the vtable with a forwarding pointer
  4. 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

  1. Is AMC designed to work with C++ virtual functions at all? The Scheme example uses C with manual type dispatch via switch statements.

  2. 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?
  3. 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 - CRASH

Working Alternative

AMS pool works perfectly with the same codebase:

  • No write barriers
  • No object relocation
  • Virtual functions work normally

What We Need

  1. Is it possible to use AMC with C++ virtual functions? Given the vtable-forwarding conflict, it seems fundamentally incompatible.

  2. 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?
  3. 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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions