Skip to content
This repository
Browse code

Add new Immix GC as the mature GC

  • Loading branch information...
commit 45be183889b9daac5e1115c1db3a697ca26a2e55 1 parent 0649d9d
authored
8  vm/builtin/tuple.cpp
@@ -178,6 +178,14 @@ namespace rubinius {
178 178
     }
179 179
   }
180 180
 
  181
+  void Tuple::Info::visit(Object* obj, ObjectVisitor& visit) {
  182
+    Tuple* tup = as<Tuple>(obj);
  183
+
  184
+    for(size_t i = 0; i < tup->num_fields(); i++) {
  185
+      visit.call(tup->field[i]);
  186
+    }
  187
+  }
  188
+
181 189
   void Tuple::Info::show(STATE, Object* self, int level) {
182 190
     Tuple* tup = as<Tuple>(self);
183 191
     size_t size = tup->num_fields();
1  vm/builtin/tuple.hpp
@@ -58,6 +58,7 @@ namespace rubinius {
58 58
       virtual void mark(Object* t, ObjectMark& mark);
59 59
       virtual void show(STATE, Object* self, int level);
60 60
       virtual void show_simple(STATE, Object* self, int level);
  61
+      virtual void visit(Object*, ObjectVisitor& visit);
61 62
     };
62 63
   };
63 64
 };
1  vm/builtin/variable_scope.hpp
@@ -96,6 +96,7 @@ namespace rubinius {
96 96
       virtual void set_field(STATE, Object*, size_t, Object*);
97 97
       virtual Object* get_field(STATE, Object*, size_t);
98 98
       virtual void auto_mark(Object*, ObjectMark&);
  99
+      virtual void auto_visit(Object*, ObjectVisitor&);
99 100
     };
100 101
   };
101 102
 }
33  vm/codegen/field_extract.rb
@@ -359,6 +359,21 @@ def generate_marks(cpp)
359 359
     return str
360 360
   end
361 361
 
  362
+  def generate_visits(cpp)
  363
+    str = ''
  364
+
  365
+    str << generate_visits(cpp.super) if cpp.super
  366
+
  367
+    cpp.fields.each do |name, type, idx|
  368
+      str << <<-EOF
  369
+    visit.call(target->#{name}());
  370
+      EOF
  371
+    end
  372
+
  373
+    return str
  374
+
  375
+  end
  376
+
362 377
   def generate_mark
363 378
     marks = generate_marks(self).rstrip
364 379
 
@@ -376,6 +391,23 @@ def generate_mark
376 391
     str
377 392
   end
378 393
 
  394
+  def generate_visit
  395
+    marks = generate_visits(self).rstrip
  396
+
  397
+    str = ''
  398
+
  399
+    str << <<-EOF unless marks.empty?
  400
+void #{@name}::Info::auto_visit(Object* _t, ObjectVisitor& visit) {
  401
+  #{@name}* target = as<#{@name}>(_t);
  402
+
  403
+#{marks}
  404
+}
  405
+
  406
+    EOF
  407
+
  408
+    str
  409
+  end
  410
+
379 411
   def kind_of_code(what)
380 412
     case @name
381 413
     when "Fixnum"
@@ -654,6 +686,7 @@ def write_if_new(path)
654 686
   parser.classes.each do |n, cpp|
655 687
     f.puts cpp.generate_typechecks
656 688
     f.puts cpp.generate_mark
  689
+    f.puts cpp.generate_visit
657 690
   end
658 691
 end
659 692
 
169  vm/gc.cpp
@@ -51,15 +51,8 @@ namespace rubinius {
51 51
   void GarbageCollector::scan_object(Object* obj) {
52 52
     Object* slot;
53 53
 
54  
-    // If this object's refs are weak, then add it to the weak_refs
55  
-    // vector and don't look at it otherwise.
56  
-    if(obj->RefsAreWeak) {
57  
-      if(!weak_refs) {
58  
-        weak_refs = new ObjectArray(0);
59  
-      }
60  
-
61  
-      weak_refs->push_back(obj);
62  
-      return;
  54
+    if(watched_p(obj)) {
  55
+      std::cout << "detected " << obj << " during scan_object.\n";
63 56
     }
64 57
 
65 58
     if(obj->klass() && obj->klass()->reference_p()) {
@@ -72,6 +65,17 @@ namespace rubinius {
72 65
       if(slot) obj->ivars(object_memory->state, slot);
73 66
     }
74 67
 
  68
+    // If this object's refs are weak, then add it to the weak_refs
  69
+    // vector and don't look at it otherwise.
  70
+    if(obj->RefsAreWeak) {
  71
+      if(!weak_refs) {
  72
+        weak_refs = new ObjectArray(0);
  73
+      }
  74
+
  75
+      weak_refs->push_back(obj);
  76
+      return;
  77
+    }
  78
+
75 79
     TypeInfo* ti = object_memory->type_info[obj->obj_type];
76 80
     assert(ti);
77 81
 
@@ -147,6 +151,153 @@ namespace rubinius {
147 151
 
148 152
       call_frame = call_frame->previous;
149 153
     }
  154
+  }
  155
+
  156
+  void GarbageCollector::visit_variable_scope(VariableScope* scope, ObjectVisitor& visit) {
  157
+    scope->update(visit.call(scope->self()),
  158
+                  visit.call(scope->module()),
  159
+                  visit.call(scope->block()));
  160
+
  161
+    for(int i = 0; i < scope->number_of_locals(); i++) {
  162
+      Object* local = scope->get_local(i);
  163
+      if(local->reference_p()) {
  164
+        scope->set_local(i, visit.call(local));
  165
+      }
  166
+    }
  167
+
  168
+    VariableScope* parent = scope->parent();
  169
+    if(parent && parent->reference_p()) {
  170
+      if(parent->stack_allocated_p()) {
  171
+        saw_variable_scope(parent);
  172
+      } else {
  173
+        scope->update_parent((VariableScope*)visit.call(parent));
  174
+      }
  175
+    }
  176
+  }
  177
+
  178
+  void GarbageCollector::visit_call_frame(CallFrame* top_call_frame, ObjectVisitor& visit) {
  179
+    CallFrame* call_frame = top_call_frame;
  180
+    while(call_frame) {
  181
+      if(call_frame->name && call_frame->name->reference_p()) {
  182
+        call_frame->name = (Symbol*)visit.call(call_frame->name);
  183
+      }
  184
+
  185
+      if(call_frame->cm && call_frame->cm->reference_p()) {
  186
+        call_frame->cm = (CompiledMethod*)visit.call(call_frame->cm);
  187
+      }
  188
+
  189
+      for(int i = 0; i < call_frame->stack_size; i++) {
  190
+        Object* obj = call_frame->stk[i];
  191
+        if(obj && obj->reference_p()) {
  192
+          call_frame->stk[i] = visit.call(obj);
  193
+        }
  194
+      }
  195
+
  196
+      if(call_frame->top_scope) {
  197
+        if(call_frame->top_scope->stack_allocated_p()) {
  198
+          visit_variable_scope(call_frame->top_scope, visit);
  199
+        } else {
  200
+          call_frame->top_scope = (VariableScope*)visit.call(call_frame->top_scope);
  201
+        }
  202
+      }
  203
+
  204
+      if(call_frame->scope) {
  205
+        if(call_frame->scope->stack_allocated_p()) {
  206
+          visit_variable_scope(call_frame->scope, visit);
  207
+        } else {
  208
+          call_frame->scope = (VariableScope*)visit.call(call_frame->scope);
  209
+        }
  210
+      }
  211
+
  212
+      call_frame = call_frame->previous;
  213
+    }
  214
+  }
  215
+
  216
+  void GarbageCollector::visit_roots(Roots& roots, ObjectVisitor& visit) {
  217
+    Root* root = static_cast<Root*>(roots.head());
  218
+    while(root) {
  219
+      Object* tmp = root->get();
  220
+      if(tmp->reference_p()) {
  221
+        visit.call(tmp);
  222
+      }
  223
+
  224
+      root = static_cast<Root*>(root->next());
  225
+    }
  226
+  }
  227
+
  228
+  void GarbageCollector::visit_call_frames_list(CallFrameLocationList& call_frames,
  229
+      ObjectVisitor& visit) {
  230
+
  231
+    // Walk all the call frames
  232
+    for(CallFrameLocationList::const_iterator i = call_frames.begin();
  233
+        i != call_frames.end();
  234
+        i++) {
  235
+      CallFrame** loc = *i;
  236
+      visit_call_frame(*loc, visit);
  237
+    }
  238
+  }
  239
+
  240
+  class UnmarkVisitor : public ObjectVisitor {
  241
+    std::vector<Object*> stack_;
  242
+    ObjectMemory* object_memory_;
  243
+
  244
+  public:
  245
+
  246
+    UnmarkVisitor(ObjectMemory* om)
  247
+      : object_memory_(om)
  248
+    {}
  249
+
  250
+    Object* call(Object* obj) {
  251
+      if(watched_p(obj)) {
  252
+        std::cout << "detected " << obj << " during unmarking.\n";
  253
+      }
  254
+
  255
+      if(obj->reference_p() && obj->marked_p()) {
  256
+        obj->clear_mark();
  257
+        stack_.push_back(obj);
  258
+      }
  259
+
  260
+      return obj;
  261
+    }
  262
+
  263
+    /* Understands how to read the inside of an object and find all references
  264
+     * located within. It copies the objects pointed to, but does not follow into
  265
+     * those further (ie, not recursive) */
  266
+    void visit_object(Object* obj) {
  267
+      if(obj->klass() && obj->klass()->reference_p()) {
  268
+        call(obj->klass());
  269
+      }
  270
+
  271
+      if(obj->ivars() && obj->ivars()->reference_p()) {
  272
+        call(obj->ivars());
  273
+      }
  274
+
  275
+      TypeInfo* ti = object_memory_->type_info[obj->obj_type];
  276
+      assert(ti);
  277
+
  278
+      ti->visit(obj, *this);
  279
+    }
  280
+
  281
+    void drain_stack() {
  282
+      while(!stack_.empty()) {
  283
+        Object* obj = stack_.back();
  284
+        stack_.pop_back();
  285
+
  286
+        if(watched_p(obj)) {
  287
+          std::cout << "detected " << obj << " in unmarking stack.\n";
  288
+        }
  289
+
  290
+        visit_object(obj);
  291
+      }
  292
+    }
  293
+  };
  294
+
  295
+  void GarbageCollector::unmark_all(Roots &roots, CallFrameLocationList& call_frames) {
  296
+    UnmarkVisitor visit(object_memory);
  297
+
  298
+    visit_roots(roots, visit);
  299
+    visit_call_frames_list(call_frames, visit);
150 300
 
  301
+    visit.drain_stack();
151 302
   }
152 303
 }
14  vm/gc.hpp
@@ -13,9 +13,14 @@ namespace rubinius {
13 13
 
14 14
   typedef std::vector<Object*> ObjectArray;
15 15
 
  16
+  class ObjectVisitor {
  17
+  public:
  18
+    virtual ~ObjectVisitor() { }
  19
+    virtual Object* call(Object*) = 0;
  20
+  };
16 21
 
17 22
   class GarbageCollector {
18  
-    public:
  23
+  public:
19 24
 
20 25
     ObjectMemory* object_memory;
21 26
     ObjectArray* weak_refs;
@@ -28,12 +33,19 @@ namespace rubinius {
28 33
     void walk_call_frame(CallFrame* top_call_frame);
29 34
     void saw_variable_scope(VariableScope* scope);
30 35
 
  36
+    void visit_variable_scope(VariableScope* scope, ObjectVisitor& visit);
  37
+    void visit_call_frame(CallFrame* top, ObjectVisitor& visit);
  38
+
31 39
     Object* mark_object(Object* obj) {
32 40
       if(!obj || !obj->reference_p()) return obj;
33 41
       Object* tmp = saw_object(obj);
34 42
       if(tmp) return tmp;
35 43
       return obj;
36 44
     }
  45
+
  46
+    void visit_roots(Roots& roots, ObjectVisitor& visit);
  47
+    void visit_call_frames_list(CallFrameLocationList& call_frames, ObjectVisitor& visit);
  48
+    void unmark_all(Roots &roots, CallFrameLocationList& call_frames);
37 49
   };
38 50
 
39 51
 }
13  vm/gc_baker.cpp
@@ -29,6 +29,10 @@ namespace rubinius {
29 29
   Object* BakerGC::saw_object(Object* obj) {
30 30
     Object* copy;
31 31
 
  32
+    if(watched_p(obj)) {
  33
+      std::cout << "detected " << obj << " during baker collection\n";
  34
+    }
  35
+
32 36
     if(!obj->reference_p()) return obj;
33 37
 
34 38
     if(obj->zone != YoungObjectZone) return obj;
@@ -56,6 +60,10 @@ namespace rubinius {
56 60
       ctx->post_copy(as<MethodContext>(obj));
57 61
     }
58 62
 
  63
+    if(watched_p(copy)) {
  64
+      std::cout << "detected " << copy << " during baker collection (2)\n";
  65
+    }
  66
+
59 67
     obj->set_forward(object_memory->state, copy);
60 68
     return copy;
61 69
   }
@@ -102,7 +110,7 @@ namespace rubinius {
102 110
         assert(tmp->zone == MatureObjectZone);
103 111
         assert(!tmp->forwarded_p());
104 112
 
105  
-        /* Remove the Remember bit, since we're clearing the set. */
  113
+        // Remove the Remember bit, since we're clearing the set.
106 114
         tmp->Remember = 0;
107 115
         scan_object(tmp);
108 116
       }
@@ -153,6 +161,9 @@ namespace rubinius {
153 161
           tmp = *oi;
154 162
           assert(tmp->zone == MatureObjectZone);
155 163
           scan_object(tmp);
  164
+          if(watched_p(tmp)) {
  165
+            std::cout << "detected " << tmp << " during scan of promoted objects.\n";
  166
+          }
156 167
         }
157 168
 
158 169
         delete cur;
8  vm/gc_baker.hpp
@@ -15,12 +15,14 @@
15 15
 
16 16
 #include "call_frame_list.hpp"
17 17
 
  18
+#include "object_watch.hpp"
  19
+
18 20
 namespace rubinius {
19 21
 
20 22
   class ObjectMemory;
21 23
 
22 24
   class BakerGC : public GarbageCollector {
23  
-    public:
  25
+  public:
24 26
 
25 27
     /* Fields */
26 28
     Heap heap_a;
@@ -60,6 +62,10 @@ namespace rubinius {
60 62
         obj = (Object*)current->allocate(bytes);
61 63
       }
62 64
 
  65
+      if(watched_p(obj)) {
  66
+        std::cout << "detected " << obj << " during baker allocation.\n";
  67
+      }
  68
+
63 69
       obj->init_header(YoungObjectZone, bytes);
64 70
 
65 71
 #ifdef RBX_GC_STATS
105  vm/gc_immix.cpp
... ...
@@ -0,0 +1,105 @@
  1
+#include "gc_immix.hpp"
  2
+#include "objectmemory.hpp"
  3
+
  4
+namespace rubinius {
  5
+  void ImmixGC::ObjectDescriber::added_chunk(int count) {
  6
+#ifdef IMMIX_DEBUG
  7
+    std::cout << "Added a chunk: " << count << "\n";
  8
+#endif
  9
+
  10
+    if(object_memory_) {
  11
+      object_memory_->collect_mature_now = true;
  12
+    }
  13
+  }
  14
+
  15
+  void ImmixGC::ObjectDescriber::set_forwarding_pointer(immix::Address from, immix::Address to) {
  16
+    from.as<Object>()->set_forward(object_memory_->state, to.as<Object>());
  17
+  }
  18
+
  19
+  ImmixGC::ImmixGC(ObjectMemory* om)
  20
+    : GarbageCollector(om)
  21
+    , allocator_(gc_.block_allocator())
  22
+  {
  23
+    gc_.describer().set_object_memory(om, this);
  24
+  }
  25
+
  26
+  ImmixGC::~ImmixGC() {
  27
+    // TODO free data
  28
+  }
  29
+
  30
+  Object* ImmixGC::allocate(int bytes) {
  31
+    Object* obj = allocator_.allocate(bytes).as<Object>();
  32
+    obj->init_header(MatureObjectZone, bytes);
  33
+    obj->InImmix = 1;
  34
+    return obj;
  35
+  }
  36
+
  37
+  Object* ImmixGC::saw_object(Object* obj) {
  38
+    if(watched_p(obj)) {
  39
+      std::cout << "detected " << obj << " during immix scanning.\n";
  40
+    }
  41
+
  42
+    immix::Address fwd = gc_.mark_address(immix::Address(obj), allocator_);
  43
+    return fwd.as<Object>();
  44
+  }
  45
+
  46
+  void ImmixGC::collect(Roots &roots, CallFrameLocationList& call_frames) {
  47
+    Object* tmp;
  48
+
  49
+    Root* root = static_cast<Root*>(roots.head());
  50
+    while(root) {
  51
+      tmp = root->get();
  52
+      if(tmp->reference_p()) {
  53
+        saw_object(tmp);
  54
+      }
  55
+
  56
+      root = static_cast<Root*>(root->next());
  57
+    }
  58
+
  59
+    // Walk all the call frames
  60
+    for(CallFrameLocationList::const_iterator i = call_frames.begin();
  61
+        i != call_frames.end();
  62
+        i++) {
  63
+      CallFrame** loc = *i;
  64
+      walk_call_frame(*loc);
  65
+    }
  66
+
  67
+    gc_.process_mark_stack(allocator_);
  68
+
  69
+    // Cleanup all weakrefs seen
  70
+    // TODO support weakrefs!
  71
+
  72
+    // Sweep up the garbage
  73
+    gc_.sweep_blocks();
  74
+
  75
+    // This resets the allocator state to sync it up with the BlockAllocator
  76
+    // properly.
  77
+    allocator_.get_new_block();
  78
+
  79
+    ObjectArray *current_rs = object_memory->remember_set;
  80
+
  81
+    int cleared = 0;
  82
+
  83
+    for(ObjectArray::iterator oi = current_rs->begin();
  84
+        oi != current_rs->end();
  85
+        oi++) {
  86
+      tmp = *oi;
  87
+      // unremember_object throws a NULL in to remove an object
  88
+      // so we don't have to compact the set in unremember
  89
+      if(tmp) {
  90
+        assert(tmp->zone == MatureObjectZone);
  91
+        assert(!tmp->forwarded_p());
  92
+
  93
+        if(!tmp->Marked) {
  94
+          cleared++;
  95
+          *oi = NULL;
  96
+        }
  97
+      }
  98
+    }
  99
+
  100
+#ifdef IMMIX_DEBUG
  101
+    std::cout << "Immix: RS size cleared: " << cleared << "\n";
  102
+#endif
  103
+  }
  104
+
  105
+}
92  vm/gc_immix.hpp
... ...
@@ -0,0 +1,92 @@
  1
+#ifndef RBX_GC_IMMIX
  2
+#define RBX_GC_IMMIX
  3
+
  4
+#include "util/immix.hpp"
  5
+#include "gc.hpp"
  6
+
  7
+namespace rubinius {
  8
+  class ObjectMemory;
  9
+  class ImmixGC;
  10
+
  11
+  class ImmixGC : public GarbageCollector {
  12
+    class ObjectDescriber {
  13
+      ObjectMemory* object_memory_;
  14
+      ImmixGC* gc_;
  15
+
  16
+    public:
  17
+      ObjectDescriber()
  18
+        : object_memory_(0)
  19
+      {}
  20
+
  21
+      void set_object_memory(ObjectMemory* om, ImmixGC* gc) {
  22
+        object_memory_ = om;
  23
+        gc_ = gc;
  24
+      }
  25
+
  26
+      void added_chunk(int count);
  27
+
  28
+      void set_forwarding_pointer(immix::Address from, immix::Address to);
  29
+
  30
+      immix::Address forwarding_pointer(immix::Address cur) {
  31
+        Object* obj = cur.as<Object>();
  32
+
  33
+        if(obj->forwarded_p()) return obj->forward();
  34
+
  35
+        return immix::Address::null();
  36
+      }
  37
+
  38
+      immix::Address copy(immix::Address original, immix::Allocator& alloc) {
  39
+        Object* orig = original.as<Object>();
  40
+
  41
+        immix::Address copy_addr = alloc.allocate(orig->size_in_bytes());
  42
+        Object* copy = copy_addr.as<Object>();
  43
+
  44
+        copy->initialize_copy(orig, 0);
  45
+        copy->copy_body(orig);
  46
+
  47
+        copy->zone = MatureObjectZone;
  48
+        copy->InImmix = 1;
  49
+
  50
+        return copy_addr;
  51
+      }
  52
+
  53
+      void walk_pointers(immix::Address addr, immix::Marker<ObjectDescriber>& mark) {
  54
+        gc_->scan_object(addr.as<Object>());
  55
+      }
  56
+
  57
+      int size(immix::Address addr) {
  58
+        return addr.as<Object>()->size_in_bytes();
  59
+      }
  60
+
  61
+      bool mark_address(immix::Address addr, immix::MarkStack& ms) {
  62
+        Object* obj = addr.as<Object>();
  63
+
  64
+        if(obj->marked_p()) return false;
  65
+        obj->mark();
  66
+
  67
+        ms.push_back(addr);
  68
+
  69
+        // If this is a young object, let the GC know not to try and mark
  70
+        // the block it's in.
  71
+        if(obj->young_object_p() || !obj->InImmix) {
  72
+          return false;
  73
+        }
  74
+        return true;
  75
+      }
  76
+    };
  77
+
  78
+    immix::GC<ObjectDescriber> gc_;
  79
+    immix::ExpandingAllocator allocator_;
  80
+
  81
+  public:
  82
+    ImmixGC(ObjectMemory* om);
  83
+    virtual ~ImmixGC();
  84
+
  85
+    Object* allocate(int bytes);
  86
+
  87
+    virtual Object* saw_object(Object*);
  88
+    void collect(Roots &roots, CallFrameLocationList& call_frame);
  89
+  };
  90
+}
  91
+
  92
+#endif
8  vm/gc_marksweep.cpp
@@ -113,12 +113,11 @@ namespace rubinius {
113 113
   }
114 114
 
115 115
   Object* MarkSweepGC::saw_object(Object* obj) {
116  
-
117 116
 #ifdef RBX_GC_STATS
118 117
     stats::GCStats::get()->objects_seen++;
119 118
 #endif
120 119
 
121  
-    if(obj->young_object_p()) {
  120
+    if(obj->young_object_p() || obj->InImmix) {
122 121
       if(obj->marked_p()) return NULL;
123 122
 
124 123
       obj->mark();
@@ -182,12 +181,13 @@ namespace rubinius {
182 181
     std::list<Entry*>::iterator i;
183 182
 
184 183
     for(i = entries.begin(); i != entries.end();) {
185  
-      if((*i)->unmarked_p()) {
  184
+      Entry* ent = *i;
  185
+      if(ent->unmarked_p() && !ent->header->to_object()->marked_p()) {
186 186
         free_object(*i);
187 187
         if(free_entries) delete *i;
188 188
         i = entries.erase(i);
189 189
       } else {
190  
-        (*i)->clear();
  190
+        ent->clear();
191 191
         i++;
192 192
       }
193 193
     }
23  vm/object_watch.hpp
... ...
@@ -0,0 +1,23 @@
  1
+#ifndef RBX_OBJECT_WATCH
  2
+#define RBX_OBJECT_WATCH
  3
+
  4
+// #define ENABLE_OBJECT_WATCH
  5
+
  6
+namespace rubinius {
  7
+  class Object;
  8
+
  9
+  extern Object* object_watch;
  10
+
  11
+#ifdef ENABLE_OBJECT_WATCH
  12
+  static inline bool watched_p(Object* obj) {
  13
+    return obj == object_watch;
  14
+  }
  15
+#else
  16
+  static inline bool watched_p(Object* obj) {
  17
+    return false;
  18
+  }
  19
+#endif
  20
+}
  21
+
  22
+#endif
  23
+
45  vm/objectmemory.cpp
@@ -10,12 +10,22 @@
10 10
 
11 11
 namespace rubinius {
12 12
 
  13
+  Object* object_watch = 0;
  14
+
13 15
   /* ObjectMemory methods */
14  
-  ObjectMemory::ObjectMemory(STATE, size_t young_bytes):
15  
-      state(state),
16  
-      young(this, young_bytes),
17  
-      mature(this),
18  
-      contexts(cContextHeapSize) {
  16
+  ObjectMemory::ObjectMemory(STATE, size_t young_bytes)
  17
+    : state(state)
  18
+    , young(this, young_bytes)
  19
+    , mature(this)
  20
+    , immix_(this)
  21
+    , contexts(cContextHeapSize)
  22
+  {
  23
+
  24
+    // TODO Not sure where this code should be...
  25
+    if(char* num = getenv("WATCH")) {
  26
+      object_watch = (Object*)strtol(num, NULL, 10);
  27
+      std::cout << "Watching for " << object_watch << "\n";
  28
+    }
19 29
 
20 30
     remember_set = new ObjectArray(0);
21 31
 
@@ -41,6 +51,8 @@ namespace rubinius {
41 51
     young.free_objects();
42 52
     mature.free_objects();
43 53
 
  54
+    // TODO free immix data
  55
+
44 56
     delete remember_set;
45 57
 
46 58
     for(size_t i = 0; i < LastObjectType; i++) {
@@ -77,8 +89,16 @@ namespace rubinius {
77 89
     stats::GCStats::get()->objects_promoted++;
78 90
 #endif
79 91
 
80  
-    Object* copy = mature.copy_object(obj);
  92
+    Object* copy = immix_.allocate(obj->size_in_bytes());
  93
+    copy->initialize_copy(obj, 0);
  94
+    copy->copy_body(obj);
  95
+
81 96
     copy->zone = MatureObjectZone;
  97
+
  98
+    if(watched_p(obj)) {
  99
+      std::cout << "detected object " << obj << " during promotion.\n";
  100
+    }
  101
+
82 102
     return copy;
83 103
   }
84 104
 
@@ -91,7 +111,10 @@ namespace rubinius {
91 111
   }
92 112
 
93 113
   void ObjectMemory::collect_mature(Roots &roots, CallFrameLocationList& call_frames) {
  114
+    immix_.collect(roots, call_frames);
94 115
     mature.collect(roots, call_frames);
  116
+
  117
+    immix_.unmark_all(roots, call_frames);
95 118
     young.clear_marks();
96 119
     clear_context_marks();
97 120
   }
@@ -145,10 +168,18 @@ namespace rubinius {
145 168
       if(obj == NULL) {
146 169
         collect_young_now = true;
147 170
         state->interrupts.check = true;
148  
-        obj = mature.allocate(bytes, &collect_mature_now);
  171
+
  172
+        obj = immix_.allocate(bytes);
  173
+        if(collect_mature_now) {
  174
+          state->interrupts.check = true;
  175
+        }
149 176
       }
150 177
     }
151 178
 
  179
+    if(watched_p(obj)) {
  180
+      std::cout << "detected " << obj << " during allocation\n";
  181
+    }
  182
+
152 183
     obj->clear_fields();
153 184
     return obj;
154 185
   }
7  vm/objectmemory.hpp
@@ -3,6 +3,8 @@
3 3
 
4 4
 #include "gc_marksweep.hpp"
5 5
 #include "gc_baker.hpp"
  6
+#include "gc_immix.hpp"
  7
+
6 8
 #include "prelude.hpp"
7 9
 #include "type_info.hpp"
8 10
 
@@ -47,6 +49,9 @@ namespace rubinius {
47 49
     ObjectArray *remember_set;
48 50
     BakerGC young;
49 51
     MarkSweepGC mature;
  52
+
  53
+    ImmixGC immix_;
  54
+
50 55
     Heap contexts;
51 56
     size_t last_object_id;
52 57
     TypeInfo* type_info[(int)LastObjectType];
@@ -159,7 +164,7 @@ namespace rubinius {
159 164
     void write_barrier(Object* target, Object* val) {
160 165
       if(target->Remember) return;
161 166
       if(!REFERENCE_P(val)) return;
162  
-      if(target->zone != MatureObjectZone) return;
  167
+      if(target->zone == YoungObjectZone) return;
163 168
       if(val->zone != YoungObjectZone) return;
164 169
 
165 170
       remember_object(target);
8  vm/oop.hpp
@@ -110,9 +110,9 @@ const int cUndef = 0x22L;
110 110
   typedef enum
111 111
   {
112 112
     UnspecifiedZone  = 0,
113  
-    MatureObjectZone = 1,
114  
-    YoungObjectZone  = 2,
115  
-    LargeObjectZone  = 3,
  113
+    LargeObjectZone  = 1,
  114
+    MatureObjectZone = 2,
  115
+    YoungObjectZone  = 3,
116 116
   } gc_zone;
117 117
 
118 118
   /* the sizeof(class ObjectHeader) must an increment of the platform 
@@ -151,6 +151,8 @@ const int cUndef = 0x22L;
151 151
         unsigned int IsTainted              : 1;
152 152
         unsigned int IsFrozen               : 1;
153 153
         unsigned int RefsAreWeak            : 1;
  154
+
  155
+        unsigned int InImmix                : 1;
154 156
       };
155 157
       uint32_t all_flags;
156 158
     };
531  vm/test/test_gc_immix.hpp
... ...
@@ -0,0 +1,531 @@
  1
+
  2
+#include <cxxtest/TestSuite.h>
  3
+#include "util/immix.hpp"
  4
+
  5
+struct SimpleObject {
  6
+  bool marked, body_checked;
  7
+  immix::Address fwd;
  8
+  int magic, size;
  9
+  SimpleObject* sub;
  10
+};
  11
+
  12
+class SimpleObjectDescriber {
  13
+public:
  14
+  typedef SimpleObject Object;
  15
+
  16
+  void set_forwarding_pointer(immix::Address from, immix::Address to) {
  17
+    from.as<SimpleObject>()->fwd = to;
  18
+  }
  19
+
  20
+  immix::Address forwarding_pointer(immix::Address cur) {
  21
+    SimpleObject* obj = cur.as<SimpleObject>();
  22
+    if(obj->fwd.is_null()) return 0;
  23
+    return obj->fwd;
  24
+  }
  25
+
  26
+  immix::Address copy(immix::Address original, immix::Allocator& alloc) {
  27
+    immix::Address copy_addr = alloc.allocate(sizeof(SimpleObject));
  28
+    SimpleObject* copy = copy_addr.as<SimpleObject>();
  29
+    SimpleObject* orig = original.as<SimpleObject>();
  30
+
  31
+    copy->fwd = 0;
  32
+    copy->magic = orig->magic;
  33
+    copy->sub = orig->sub;
  34
+
  35
+    return copy;
  36
+  }
  37
+
  38
+  bool mark_address(immix::Address addr, immix::MarkStack& ms) {
  39
+    SimpleObject* obj = addr.as<SimpleObject>();
  40
+    if(obj->marked) return false;
  41
+
  42
+    obj->marked = true;
  43
+    ms.push_back(obj);
  44
+    return true;
  45
+  }
  46
+
  47
+  void walk_pointers(immix::Address addr, immix::Marker<SimpleObjectDescriber>& mark) {
  48
+    SimpleObject* obj = addr.as<SimpleObject>();
  49
+    obj->body_checked = true;
  50
+    if(obj->sub) {
  51
+      mark.mark_address(obj->sub);
  52
+    }
  53
+  }
  54
+
  55
+  int size(immix::Address addr) {
  56
+    SimpleObject* obj = addr.as<SimpleObject>();
  57
+
  58
+    if(obj->size == 0) return sizeof(SimpleObject);
  59
+
  60
+    return obj->size;
  61
+  }
  62
+
  63
+  void added_chunk(int size) { }
  64
+};
  65
+
  66
+class TestImmixGC : public CxxTest::TestSuite {
  67
+public:
  68
+
  69
+  typedef immix::GC<SimpleObjectDescriber> GC;
  70
+
  71
+  GC* gc;
  72
+
  73
+  void setUp() {
  74
+    gc = new GC;
  75
+  }
  76
+
  77
+  void tearDown() {
  78
+    delete gc;
  79
+  }
  80
+
  81
+  void test_get_block() {
  82
+    immix::Block& block = gc->get_block();
  83
+    TS_ASSERT_EQUALS(block.size(), immix::cBlockSize);
  84
+    TS_ASSERT(block.address() != 0);
  85
+    TS_ASSERT_EQUALS(block.status(), immix::cFree);
  86
+    TS_ASSERT_EQUALS(block.lines_used(), 0);
  87
+  }
  88
+
  89
+  void test_Block_is_line_free() {
  90
+    immix::Block& block = gc->get_block();
  91
+    TS_ASSERT(block.is_line_free(0));
  92
+    block.mark_line(0);
  93
+    TS_ASSERT(!block.is_line_free(0));
  94
+  }
  95
+
  96
+  void test_Block_address_of_line() {
  97
+    immix::Block& block = gc->get_block();
  98
+    immix::Address top = block.address();
  99
+    TS_ASSERT_EQUALS(block.address_of_line(0), top);
  100
+    TS_ASSERT_EQUALS(block.address_of_line(1), top + immix::cLineSize);
  101
+  }
  102
+
  103
+  void test_SingleBlockAllocator_allocate() {
  104
+    immix::Block& block = gc->get_block();
  105
+    immix::SingleBlockAllocator alloc(block);
  106
+    immix::Address addr = alloc.allocate(24);
  107
+    immix::Address top  = block.first_address();
  108
+    TS_ASSERT_EQUALS(addr, top);
  109
+
  110
+    immix::Address another = alloc.allocate(24);
  111
+    TS_ASSERT_EQUALS(another, top + 24);
  112
+  }
  113
+
  114
+  void test_SingleBlockAllocator_allocate_checks_mark_on_spill() {
  115
+    immix::Block& block = gc->get_block();
  116
+    immix::Address top  = block.address();
  117
+
  118
+    block.mark_line(1);
  119
+    immix::SingleBlockAllocator alloc(block);
  120
+    alloc.allocate(96);
  121
+
  122
+    immix::Address addr = alloc.allocate(64);
  123
+    TS_ASSERT_EQUALS(addr, top + (immix::cLineSize * 2));
  124
+  }
  125
+
  126
+  void test_SingleBlockAllocator_allocate_spans_next_line() {
  127
+    immix::Block& block = gc->get_block();
  128
+    immix::Address top = block.first_address();
  129
+
  130
+    immix::SingleBlockAllocator alloc(block);
  131
+    int size = immix::cLineSize - sizeof(immix::BlockHeader) - 4;
  132
+    alloc.allocate(size);
  133
+    TS_ASSERT(sizeof(SimpleObject) > 4);
  134
+    immix::Address addr = alloc.allocate(sizeof(SimpleObject));
  135
+
  136
+    TS_ASSERT_EQUALS(addr, top + size);
  137
+
  138
+    immix::Address addr2 = alloc.allocate(immix::cLineSize + 4);
  139
+    immix::Address addr3 = alloc.allocate(4);
  140
+    TS_ASSERT_EQUALS(addr2, addr + sizeof(SimpleObject));
  141
+    TS_ASSERT_EQUALS(addr3, addr2 + (immix::cLineSize + 4));
  142
+  }
  143
+
  144
+  void test_SingleBlockAllocator_allocate_spans_lines() {
  145
+    immix::Block& block = gc->get_block();
  146
+    immix::Address top = block.first_address();
  147
+
  148
+    immix::SingleBlockAllocator alloc(block);
  149
+    alloc.allocate(24);
  150
+    int size = (immix::cLineSize * 2) + 32;
  151
+    immix::Address big = alloc.allocate(size);
  152
+    TS_ASSERT_EQUALS(big, top + 24);
  153
+
  154
+    immix::Address addr2 = alloc.allocate(24);
  155
+    TS_ASSERT_EQUALS(addr2, big + size);
  156
+  }
  157
+
  158
+  void test_SingleBlockAllocator_allocate_skips_marked_lines() {
  159
+    immix::Block& block = gc->get_block();
  160
+    immix::Address top  = block.address();
  161
+    block.mark_line(0);
  162
+    block.mark_line(2);
  163
+    block.mark_line(4);
  164
+    block.mark_line(5);
  165
+    block.mark_line(7);
  166
+
  167
+    immix::SingleBlockAllocator alloc(block);
  168
+    immix::Address addr = alloc.allocate(24);
  169
+    TS_ASSERT_EQUALS(addr, top + immix::cLineSize);
  170
+
  171
+    immix::Address addr2 = alloc.allocate(24);
  172
+    TS_ASSERT_EQUALS(addr2, addr + 24);
  173
+
  174
+    immix::Address addr3 = alloc.allocate(128);
  175
+    TS_ASSERT_EQUALS(addr3, top + (immix::cLineSize * 3));
  176
+
  177
+    immix::Address addr4 = alloc.allocate(156);
  178
+    TS_ASSERT_EQUALS(addr4, top + (immix::cLineSize * 8));
  179
+  }
  180
+
  181
+  void test_SingleBlockAllocator_allocate_indicates_failure() {
  182
+    immix::Block& block = gc->get_block();
  183
+    immix::Address top  = block.address();
  184
+
  185
+    for(int i = 0; i < immix::cLineTableSize; i++) {
  186
+      block.mark_line(i);
  187
+    }
  188
+
  189
+    block.free_line(1);
  190
+    immix::SingleBlockAllocator alloc(block);
  191
+    immix::Address small = alloc.allocate(24);
  192
+    TS_ASSERT(!small.is_null());
  193
+
  194
+    immix::Address addr = alloc.allocate(156);
  195
+    TS_ASSERT(addr.is_null());
  196
+  }
  197
+
  198
+  void test_sweep_blocks_frees_empty_blocks() {
  199
+    immix::Block& block = gc->get_block();
  200
+
  201
+    gc->sweep_blocks();
  202
+    immix::Block& block2 = gc->get_block();
  203
+    TS_ASSERT_EQUALS(&block, &block2);
  204
+  }
  205
+
  206
+  void test_sweep_blocks_sorts_blocks() {
  207
+    immix::Block& block  = gc->get_block();
  208
+    immix::Block& block2 = gc->get_block();
  209
+    immix::Block& block3 = gc->get_block();
  210
+
  211
+    block.set_status(immix::cUnavailable);
  212
+    block2.set_status(immix::cRecyclable);
  213
+    gc->evacuate_block(block3);
  214
+    TS_ASSERT_EQUALS(block3.status(), immix::cEvacuate);
  215
+
  216
+    gc->sweep_blocks();
  217
+
  218
+    TS_ASSERT_EQUALS(block3.status(), immix::cFree);
  219
+  }
  220
+
  221
+  void test_Block_update_stats() {
  222
+    immix::Block& block = gc->get_block();
  223
+
  224
+    block.mark_line(0);
  225
+    block.mark_line(1);
  226
+    block.mark_line(3);
  227
+    block.mark_line(5);
  228
+    block.mark_line(7);
  229
+    block.mark_line(10);
  230
+
  231
+    block.update_stats();
  232
+    TS_ASSERT_EQUALS(block.status(), immix::cRecyclable);
  233
+    TS_ASSERT_EQUALS(block.holes(), 5);
  234
+    TS_ASSERT_EQUALS(block.lines_used(), 6);
  235
+  }
  236
+
  237
+  void test_Block_update_stats_finds_empty_blocks() {
  238
+    immix::Block& block = gc->get_block();
  239
+
  240
+    block.set_status(immix::cRecyclable);
  241
+    block.update_stats();
  242
+    TS_ASSERT_EQUALS(block.status(), immix::cFree);
  243
+    TS_ASSERT_EQUALS(block.holes(), 1);
  244
+    TS_ASSERT_EQUALS(block.lines_used(), 0);
  245
+  }
  246
+
  247
+  void test_Block_update_stats_finds_unavailable_blocks() {
  248
+    immix::Block& block = gc->get_block();
  249
+
  250
+    for(int i = 0; i < immix::cLineTableSize; i++) {
  251
+      block.mark_line(i);
  252
+    }
  253
+
  254
+    block.update_stats();
  255
+    TS_ASSERT_EQUALS(block.status(), immix::cUnavailable);
  256
+    TS_ASSERT_EQUALS(block.holes(), 0);
  257
+    TS_ASSERT_EQUALS(block.lines_used(), immix::cLineTableSize);
  258
+  }
  259
+
  260
+  void test_get_block_returns_recyclable_blocks() {
  261
+    immix::Block& block  = gc->get_block();
  262
+    block.set_status(immix::cRecyclable);
  263
+
  264
+    gc->sweep_blocks();
  265
+
  266
+    immix::Block& block2 = gc->get_block();
  267
+
  268
+    TS_ASSERT_EQUALS(&block2, &block);
  269
+  }
  270
+
  271
+  void test_mark_address_updates_block() {
  272
+    immix::Block& block = gc->get_block();
  273
+    immix::SingleBlockAllocator alloc(block);
  274
+    immix::Address addr = alloc.allocate(24);
  275
+
  276
+    TS_ASSERT(block.is_line_free(0));
  277
+    gc->mark_address(addr, alloc);
  278
+    TS_ASSERT(!block.is_line_free(0));
  279
+  }
  280
+
  281
+  void test_mark_address_ignores_already_marked_objects() {
  282
+    immix::Block& block = gc->get_block();
  283
+    immix::SingleBlockAllocator alloc(block);
  284
+    immix::Address addr = alloc.allocate(24);
  285
+
  286
+    addr.as<SimpleObject>()->marked = true;
  287
+
  288
+    TS_ASSERT(block.is_line_free(0));
  289
+    gc->mark_address(addr, alloc);
  290
+    TS_ASSERT(block.is_line_free(0));
  291
+  }
  292
+
  293
+  void test_mark_address_returns_forwarding_pointer() {
  294
+    immix::Block& block = gc->get_block();
  295
+    immix::SingleBlockAllocator alloc(block);
  296
+    immix::Address addr = alloc.allocate(24);
  297
+
  298
+    // Clear out fwd to be sure it's not set
  299
+    addr.as<SimpleObject>()->fwd = 0;
  300
+
  301
+    immix::Block& block2 = gc->get_block();
  302
+    immix::SingleBlockAllocator alloc2(block2);
  303
+    immix::Address addr2 = alloc2.allocate(24);
  304
+
  305
+    gc->describer().set_forwarding_pointer(addr, addr2);
  306
+
  307
+    immix::Address out = gc->mark_address(addr, alloc);
  308
+
  309
+    TS_ASSERT_EQUALS(addr.as<SimpleObject>()->fwd, addr2);
  310
+    TS_ASSERT_EQUALS(out, addr2);
  311
+  }
  312
+
  313
+  void test_mark_address_can_move_objects() {
  314
+    immix::Block& block = gc->get_block();
  315
+    immix::SingleBlockAllocator alloc(block);
  316
+    immix::Address addr = alloc.allocate(sizeof(SimpleObject));
  317
+
  318
+    addr.as<SimpleObject>()->magic = 0xdecafbad;
  319
+
  320
+    immix::Block& dest = gc->get_block();
  321
+    immix::SingleBlockAllocator dest_alloc(dest);
  322
+
  323
+    block.set_status(immix::cEvacuate);
  324
+
  325
+    immix::Address redirect = gc->mark_address(addr, dest_alloc);
  326
+
  327
+    immix::Address fwd = gc->describer().forwarding_pointer(addr);
  328
+    TS_ASSERT_EQUALS(fwd, dest.first_address());
  329
+    TS_ASSERT_EQUALS(fwd, redirect);
  330
+
  331
+    TS_ASSERT_EQUALS(fwd.as<SimpleObject>()->magic, 0xdecafbad);
  332
+  }
  333
+
  334
+  void test_mark_address_calls_describer() {
  335
+    immix::Block& block = gc->get_block();
  336
+    immix::SingleBlockAllocator alloc(block);
  337
+    immix::Address addr = alloc.allocate(sizeof(SimpleObject));
  338
+
  339
+    SimpleObject* obj = addr.as<SimpleObject>();
  340
+    immix::Address addr2 = alloc.allocate(sizeof(SimpleObject));
  341
+    obj->sub = addr2.as<SimpleObject>();
  342
+
  343
+    obj->marked = false;
  344
+
  345
+    gc->mark_address(addr, alloc);
  346
+
  347
+    TS_ASSERT_EQUALS(obj->marked, true);
  348
+    TS_ASSERT_EQUALS(gc->mark_stack().size(), 1);
  349
+    TS_ASSERT_EQUALS(gc->mark_stack()[0], addr);
  350
+  }
  351
+
  352
+  void test_mark_address_marks_all_lines_for_object() {
  353
+    immix::Block& block = gc->get_block();
  354
+    immix::SingleBlockAllocator alloc(block);
  355
+    int size = immix::cLineSize - sizeof(immix::BlockHeader) - 4;
  356
+    alloc.allocate(size);
  357
+    TS_ASSERT(sizeof(SimpleObject) > 4);
  358
+    immix::Address addr = alloc.allocate(sizeof(SimpleObject));
  359
+
  360
+    gc->mark_address(addr, alloc);
  361
+    TS_ASSERT(!block.is_line_free(0));
  362
+    TS_ASSERT(!block.is_line_free(1));
  363
+
  364
+    int big_size = immix::cLineSize * 3;
  365
+    immix::Address addr2 = alloc.allocate(big_size);
  366
+    addr2.as<SimpleObject>()->size = big_size;
  367
+
  368
+    gc->mark_address(addr2, alloc);
  369
+    TS_ASSERT(!block.is_line_free(1));
  370
+    TS_ASSERT(!block.is_line_free(2));
  371
+    TS_ASSERT(!block.is_line_free(3));
  372
+    TS_ASSERT(!block.is_line_free(4));
  373
+  }
  374
+
  375
+  void test_process_mark_stack() {
  376
+    immix::Block& block = gc->get_block();
  377
+    immix::SingleBlockAllocator alloc(block);
  378
+    immix::Address addr = alloc.allocate(sizeof(SimpleObject));
  379
+    immix::Address addr2 = alloc.allocate(sizeof(SimpleObject));
  380
+
  381
+    SimpleObject* obj = addr.as<SimpleObject>();
  382
+    SimpleObject* sub = addr2.as<SimpleObject>();
  383
+
  384
+    obj->marked = false;
  385
+    obj->sub = sub;
  386
+    obj->body_checked = false;
  387
+
  388
+    sub->marked = false;
  389
+    sub->sub = 0;
  390
+    sub->body_checked = false;
  391
+
  392
+    gc->mark_address(addr, alloc);
  393
+    TS_ASSERT_EQUALS(obj->marked, true);
  394
+
  395
+    gc->process_mark_stack(alloc);
  396
+    TS_ASSERT_EQUALS(obj->body_checked, true);
  397
+    TS_ASSERT_EQUALS(sub->marked, true);
  398
+    TS_ASSERT_EQUALS(sub->body_checked, true);
  399
+  }
  400
+