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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions