Я предполагаю, что ваш GC является точным GC.Сначала вам нужно определить , когда ваш GC, возможно, вызывается.Обычный сценарий состоит в том, чтобы каждая распределяющая подпрограмма могла вызывать GC.
Вам необходимо написать подпрограмму, которая сканирует ваш стек вызовов на наличие локальных корней.Поэтому вам нужно иметь механизм, который регистрирует эти локальные переменные в вашем GC.Другими словами, вы должны явно указать ваш интерпретатор стека вызовов (или использовать какой-то подход с продолжением подход).
Возможностьможет быть явно ваши локальные кадры как некоторые struct
.Посмотрите, например, на то, что делает среда выполнения Ocaml (прочитайте ее раздел §20.5 Жизнь в гармонии с сборщиком мусора ) или на мой старый (необслуживаемый) Qish GC.Например, вы можете принять соглашение о том, что каждый локальный кадр интерпретатора находится в некоторой локальной переменной _
(a struct
), и использовать это.В моем проекте bismon я бы кодировал что-то, почти эквивалентное (после расширения препроцессора) этому, для подпрограммы C crout
, имеющей аргумент-указатель a
и два локальных указателя b
и c
void crout(struct callingframe_st *cf, LispObject*a) {
struct mycallframe_st {
struct callingframe_st* from;
int nbloc;
LispObject* aa;
LispObject* bb;
LispObject* cc;
} _;
memset(&_, 0, sizeof(_));
_.from = cf;
_.nbloc = 3; // the current frame has 3 locals: aa, bb, cc
_.aa = a;
#define a _.aa
#define b _.bb
#define c _.cc
Затем следует тело crout
.Это передало бы (struct callingframe_st*)(&_)
соответствующим подпрограммам.В конце убедитесь, что #undef a
и т. Д. ... Ваш GC, вызванный из ваших процедур распределения, должен принять (struct callingframe_st *)(&_)
в качестве аргумента (давая текущий кадр вызова).
Так что, конечно, ваш b_cons
при условии, что он может косвенно вызвать ваш GC, должен быть объявлен как
LispObject* b_cons(struct callingframe_st*cf,
LispObject * car, LispObject * cdr);
В противном случае вам нужно определить , когда ваш GC вызывается.
Вам необходимо понять, как работает сборщик мусора (и разницу между точным и консервативным сборщиком мусора).Я настоятельно рекомендую прочитать справочник GC или хотя бы старую бумагу Пола Уилсона Uniprocessor Garbage Collection .Вы можете принять соглашение о том, что все ваши подпрограммы следуют стилю A-нормальной формы (поэтому вы никогда не кодируете напрямую в C f(g(x),h(x,y))
со всеми f
, g
, h
, возможно, выполняющими объектраспределение).
Вы также можете использовать некоторые существующие точные GC, такие как Ravenbrook MPS .
В противном случае, используйте некоторые консервативные GC как GC Боэма .
Смотрите также исходный код существующих интерпретаторов свободного программного обеспечения, имеющих немного GC.
Читайте также Queinnec's LispВ Small Pieces book
Мне нужно вручную толкать и вставлять объекты, чтобы вызывать функции C.
Это может бытьхорошая идея (но тогда вам нужно переписать большую часть кода, и вы действительно можете определить свой собственный байт-код механизм).Посмотрите, что Lua или Nim или Ocaml интерпретатор байт-кода или Emacs Elisp интерпретатор делает.
В завершение вы можете подумать (это действительно сложно, и я не рекомендую идти по этому пути, поскольку это займет много лет работы) написание некоторого GCC плагина для генерации и / или добавления ad-hocметаданные фрейма вызова и / или сгенерируйте код, связанный с фреймом вызова, чтобы помочь вашему точному GC.Это действительно сложно.IIRC, CLASP делает нечто подобное (выше Clang, а не GCC).
Не забывайте, что сборка мусора - это целая программа .