diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc index 0785897d2..d1aef97a4 100644 --- a/src/libexpr/attr-set.cc +++ b/src/libexpr/attr-set.cc @@ -7,26 +7,25 @@ namespace nix { -/* Allocate a new array of attributes for an attribute set with a specific - capacity. The space is implicitly reserved after the Bindings - structure. */ -Bindings * EvalState::allocBindings(size_t capacity) +/* Allocate a new array of attributes for an attribute set with a + specific capacity. The space is implicitly reserved after the + Bindings structure. */ +Ptr Bindings::allocBindings(size_t capacity) { - if (capacity > std::numeric_limits::max()) + if (capacity >= 1UL << Object::miscBits) throw Error("attribute set of size %d is too big", capacity); - return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity); + return gc.alloc(Bindings::wordsFor(capacity), capacity); } void EvalState::mkAttrs(Value & v, size_t capacity) { + v.type = tAttrs; if (capacity == 0) { - v = vEmptySet; + v.attrs = emptyBindings; return; } - clearValue(v); - v.type = tAttrs; - v.attrs = allocBindings(capacity); + v.attrs = Bindings::allocBindings(capacity); nrAttrsets++; nrAttrsInAttrsets += capacity; } diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 3119a1848..02241ce74 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -2,6 +2,7 @@ #include "nixexpr.hh" #include "symbol-table.hh" +#include "gc.hh" #include @@ -30,19 +31,22 @@ struct Attr by its size and its capacity, the capacity being the number of Attr elements allocated after this structure, while the size corresponds to the number of elements already inserted in this structure. */ -class Bindings +class Bindings : public Object { public: typedef uint32_t size_t; private: - size_t size_, capacity_; + // FIXME: eliminate size_. We can just rely on capacity by sorting + // null entries towards the end of the vector. + size_t size_; Attr attrs[0]; - Bindings(size_t capacity) : size_(0), capacity_(capacity) { } +public: + // FIXME: make private + Bindings(size_t capacity) : Object(tBindings, capacity), size_(0) { } Bindings(const Bindings & bindings) = delete; -public: size_t size() const { return size_; } bool empty() const { return !size_; } @@ -51,7 +55,7 @@ public: void push_back(const Attr & attr) { - assert(size_ < capacity_); + assert(size_ < capacity()); attrs[size_++] = attr; } @@ -73,7 +77,7 @@ public: void sort(); - size_t capacity() { return capacity_; } + size_t capacity() const { return getMisc(); } /* Returns the attributes in lexicographically sorted order. */ std::vector lexicographicOrder() const @@ -88,7 +92,19 @@ public: return res; } - friend class EvalState; + Size words() const + { + return wordsFor(capacity()); + } + + static Size wordsFor(size_t capacity) + { + return 2 + 3 * capacity; // FIXME + } + + static Ptr allocBindings(size_t capacity); + + friend class GC; }; diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc index 3e0c78f28..922b44fcf 100644 --- a/src/libexpr/common-eval-args.cc +++ b/src/libexpr/common-eval-args.cc @@ -30,7 +30,7 @@ MixEvalArgs::MixEvalArgs() Bindings * MixEvalArgs::getAutoArgs(EvalState & state) { - Bindings * res = state.allocBindings(autoArgs.size()); + Bindings * res = Bindings::allocBindings(autoArgs.size()); for (auto & i : autoArgs) { Value * v = state.allocValue(); if (i.second[0] == 'E') diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index c27116e3b..492f6f372 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -78,18 +78,5 @@ inline void EvalState::forceList(Value & v, const Pos & pos) throwTypeError("value is %1% while a list was expected, at %2%", v, pos); } -/* Note: Various places expect the allocated memory to be zeroed. */ -inline void * allocBytes(size_t n) -{ - void * p; -#if HAVE_BOEHMGC - p = GC_MALLOC(n); -#else - p = calloc(n, 1); -#endif - if (!p) throw std::bad_alloc(); - return p; -} - } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d8e10d9f2..784fadf99 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -19,24 +19,14 @@ #include #include -#if HAVE_BOEHMGC - -#include -#include - -#endif - namespace nix { +// FIXME static char * dupString(const char * s) { char * t; -#if HAVE_BOEHMGC - t = GC_STRDUP(s); -#else t = strdup(s); -#endif if (!t) throw std::bad_alloc(); return t; } @@ -85,6 +75,7 @@ static void printValue(std::ostream & str, std::set & active, con str << "}"; break; } + case tList0: case tList1: case tList2: case tListN: @@ -108,9 +99,11 @@ static void printValue(std::ostream & str, std::set & active, con case tPrimOpApp: str << ""; break; +#if 0 case tExternal: str << *v.external; break; +#endif case tFloat: str << v.fpoint; break; @@ -130,10 +123,10 @@ std::ostream & operator << (std::ostream & str, const Value & v) } -const Value *getPrimOp(const Value &v) { +const Value * getPrimOp(const Value & v) { const Value * primOp = &v; while (primOp->type == tPrimOpApp) { - primOp = primOp->primOpApp.left; + primOp = primOp->app.left; } assert(primOp->type == tPrimOp); return primOp; @@ -149,7 +142,7 @@ string showType(const Value & v) case tPath: return "a path"; case tNull: return "null"; case tAttrs: return "a set"; - case tList1: case tList2: case tListN: return "a list"; + case tList0: case tList1: case tList2: case tListN: return "a list"; case tThunk: return "a thunk"; case tApp: return "a function application"; case tLambda: return "a function"; @@ -158,21 +151,14 @@ string showType(const Value & v) return fmt("the built-in function '%s'", string(v.primOp->name)); case tPrimOpApp: return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name)); +#if 0 case tExternal: return v.external->showType(); - case tFloat: return "a float"; - } - abort(); -} - - -#if HAVE_BOEHMGC -/* Called when the Boehm GC runs out of memory. */ -static void * oomHandler(size_t requested) -{ - /* Convert this to a proper C++ exception. */ - throw std::bad_alloc(); -} #endif + case tFloat: return "a float"; + default: + abort(); + } +} static Symbol getName(const AttrName & name, EvalState & state, Env & env) @@ -194,46 +180,6 @@ void initGC() { if (gcInitialised) return; -#if HAVE_BOEHMGC - /* Initialise the Boehm garbage collector. */ - - /* Don't look for interior pointers. This reduces the odds of - misdetection a bit. */ - GC_set_all_interior_pointers(0); - - /* We don't have any roots in data segments, so don't scan from - there. */ - GC_set_no_dls(1); - - GC_INIT(); - - GC_set_oom_fn(oomHandler); - - /* Set the initial heap size to something fairly big (25% of - physical RAM, up to a maximum of 384 MiB) so that in most cases - we don't need to garbage collect at all. (Collection has a - fairly significant overhead.) The heap size can be overridden - through libgc's GC_INITIAL_HEAP_SIZE environment variable. We - should probably also provide a nix.conf setting for this. Note - that GC_expand_hp() causes a lot of virtual, but not physical - (resident) memory to be allocated. This might be a problem on - systems that don't overcommit. */ - if (!getenv("GC_INITIAL_HEAP_SIZE")) { - size_t size = 32 * 1024 * 1024; -#if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) - size_t maxSize = 384 * 1024 * 1024; - long pageSize = sysconf(_SC_PAGESIZE); - long pages = sysconf(_SC_PHYS_PAGES); - if (pageSize != -1) - size = (pageSize * pages) / 4; // 25% of RAM - if (size > maxSize) size = maxSize; -#endif - debug(format("setting initial heap size to %1% bytes") % size); - GC_expand_hp(size); - } - -#endif - gcInitialised = true; } @@ -311,7 +257,9 @@ EvalState::EvalState(const Strings & _searchPath, ref store) assert(gcInitialised); +#if 0 static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes"); +#endif /* Initialise the Nix expression search path. */ if (!evalSettings.pureEval) { @@ -340,9 +288,7 @@ EvalState::EvalState(const Strings & _searchPath, ref store) } } - clearValue(vEmptySet); - vEmptySet.type = tAttrs; - vEmptySet.attrs = allocBindings(0); + emptyBindings = Bindings::allocBindings(0); createBaseEnv(); } @@ -441,9 +387,9 @@ Value * EvalState::addConstant(const string & name, Value & v) Value * v2 = allocValue(); *v2 = v; staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; - baseEnv.values[baseEnvDispl++] = v2; + baseEnv->values[baseEnvDispl++] = v2; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; - baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v2)); + baseEnv->values[0]->attrs->push_back(Attr(symbols.create(name2), v2)); return v2; } @@ -462,15 +408,15 @@ Value * EvalState::addPrimOp(const string & name, v->type = tPrimOp; v->primOp = new PrimOp(primOp, arity, sym); staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; - baseEnv.values[baseEnvDispl++] = v; - baseEnv.values[0]->attrs->push_back(Attr(sym, v)); + baseEnv->values[baseEnvDispl++] = v; + baseEnv->values[0]->attrs->push_back(Attr(sym, v)); return v; } Value & EvalState::getBuiltin(const string & name) { - return *baseEnv.values[0]->attrs->find(symbols.create(name))->value; + return *baseEnv->values[0]->attrs->find(symbols.create(name))->value; } @@ -556,8 +502,9 @@ Value & mkString(Value & v, const string & s, const PathSet & context) mkString(v, s.c_str()); if (!context.empty()) { size_t n = 0; + // FIXME: store as Symbols? v.string.context = (const char * *) - allocBytes((context.size() + 1) * sizeof(char *)); + malloc((context.size() + 1) * sizeof(char *)); for (auto & i : context) v.string.context[n++] = dupString(i.c_str()); v.string.context[n] = 0; @@ -598,50 +545,41 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) } -std::atomic nrValuesFreed{0}; - -void finalizeValue(void * obj, void * data) -{ - nrValuesFreed++; -} - -Value * EvalState::allocValue() +Ptr EvalState::allocValue() { nrValues++; - auto v = (Value *) allocBytes(sizeof(Value)); - //GC_register_finalizer_no_order(v, finalizeValue, nullptr, nullptr, nullptr); - return v; + return gc.alloc(Value::words()); } -Env & EvalState::allocEnv(size_t size) +Ptr EvalState::allocEnv(size_t size) { - if (size > std::numeric_limits::max()) + if (size > std::numeric_limits::max()) // FIXME throw Error("environment size %d is too big", size); nrEnvs++; nrValuesInEnvs += size; - Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *)); - env->size = (decltype(Env::size)) size; - env->type = Env::Plain; + auto env = gc.alloc(Env::wordsFor(size), size); + // FIXME /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */ - return *env; + return env; } void EvalState::mkList(Value & v, size_t size) { - clearValue(v); - if (size == 1) + if (size == 0) + v.type = tList0; + else if (size == 1) v.type = tList1; else if (size == 2) v.type = tList2; else { v.type = tListN; - v.bigList.size = size; - v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0; + v.bigList = gc.alloc>( + PtrList::wordsFor(size), tValueList, size); } nrListElems += size; } @@ -704,25 +642,25 @@ Value * ExprVar::maybeThunk(EvalState & state, Env & env) Value * ExprString::maybeThunk(EvalState & state, Env & env) { nrAvoided++; - return &v; + return &*v; } Value * ExprInt::maybeThunk(EvalState & state, Env & env) { nrAvoided++; - return &v; + return &*v; } Value * ExprFloat::maybeThunk(EvalState & state, Env & env) { nrAvoided++; - return &v; + return &*v; } Value * ExprPath::maybeThunk(EvalState & state, Env & env) { nrAvoided++; - return &v; + return &*v; } @@ -732,13 +670,13 @@ void EvalState::evalFile(const Path & path_, Value & v) FileEvalCache::iterator i; if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) { - v = i->second; + v = *i->second; return; } Path path2 = resolveExprPath(path); if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) { - v = i->second; + v = *i->second; return; } @@ -755,14 +693,18 @@ void EvalState::evalFile(const Path & path_, Value & v) fileParseCache[path2] = e; try { - eval(e, v); + auto v2 = allocValue(); + eval(e, *v2); + + v = *v2; + + if (path != path2) fileEvalCache.emplace(path, v2); + fileEvalCache.emplace(path2, std::move(v2)); } catch (Error & e) { addErrorPrefix(e, "while evaluating the file '%1%':\n", path2); throw; } - fileEvalCache[path2] = v; - if (path != path2) fileEvalCache[path] = v; } @@ -815,24 +757,24 @@ void Expr::eval(EvalState & state, Env & env, Value & v) void ExprInt::eval(EvalState & state, Env & env, Value & v) { - v = this->v; + v = *this->v; } void ExprFloat::eval(EvalState & state, Env & env, Value & v) { - v = this->v; + v = *this->v; } void ExprString::eval(EvalState & state, Env & env, Value & v) { - v = this->v; + v = *this->v; } void ExprPath::eval(EvalState & state, Env & env, Value & v) { - v = this->v; + v = *this->v; } @@ -877,7 +819,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) if (hasOverrides) { Value * vOverrides = (*v.attrs)[overrides->second.displ].value; state.forceAttrs(*vOverrides); - Bindings * newBnds = state.allocBindings(v.attrs->size() + vOverrides->attrs->size()); + Bindings * newBnds = Bindings::allocBindings(v.attrs->size() + vOverrides->attrs->size()); for (auto & i : *v.attrs) newBnds->push_back(i); for (auto & i : *vOverrides->attrs) { @@ -1065,7 +1007,7 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos) Value * primOp = &fun; while (primOp->type == tPrimOpApp) { argsDone++; - primOp = primOp->primOpApp.left; + primOp = primOp->app.left; } assert(primOp->type == tPrimOp); auto arity = primOp->primOp->arity; @@ -1078,8 +1020,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos) Value * vArgs[arity]; auto n = arity - 1; vArgs[n--] = &arg; - for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left) - vArgs[n--] = arg->primOpApp.right; + for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->app.left) + vArgs[n--] = arg->app.right; /* And call the primop. */ nrPrimOpCalls++; @@ -1089,8 +1031,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos) Value * fun2 = allocValue(); *fun2 = fun; v.type = tPrimOpApp; - v.primOpApp.left = fun2; - v.primOpApp.right = &arg; + v.app.left = fun2; + v.app.right = &arg; } } @@ -1379,7 +1321,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) NixFloat nf = 0; bool first = !forceString; - ValueType firstType = tString; + Tag firstType = tString; for (auto & i : *es) { Value vTmp; @@ -1594,8 +1536,10 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, return coerceToString(pos, *i->value, context, coerceMore, copyToStore); } +#if 0 if (v.type == tExternal) return v.external->coerceToString(pos, context, coerceMore, copyToStore); +#endif if (coerceMore) { @@ -1692,6 +1636,7 @@ bool EvalState::eqValues(Value & v1, Value & v2) case tNull: return true; + case tList0: case tList1: case tList2: case tListN: @@ -1727,8 +1672,10 @@ bool EvalState::eqValues(Value & v1, Value & v2) case tPrimOpApp: return false; +#if 0 case tExternal: return *v1.external == *v2.external; +#endif case tFloat: return v1.fpoint == v2.fpoint; @@ -1751,10 +1698,6 @@ void EvalState::printStats() uint64_t bValues = nrValues * sizeof(Value); uint64_t bAttrsets = nrAttrsets * sizeof(Bindings) + nrAttrsInAttrsets * sizeof(Attr); -#if HAVE_BOEHMGC - GC_word heapSize, totalBytes; - GC_get_heap_usage_safe(&heapSize, 0, 0, 0, &totalBytes); -#endif if (showStats) { auto outPath = getEnv("NIX_SHOW_STATS_PATH","-"); std::fstream fs; @@ -1804,13 +1747,6 @@ void EvalState::printStats() topObj.attr("nrLookups", nrLookups); topObj.attr("nrPrimOpCalls", nrPrimOpCalls); topObj.attr("nrFunctionCalls", nrFunctionCalls); -#if HAVE_BOEHMGC - { - auto gc = topObj.object("gc"); - gc.attr("heapSize", heapSize); - gc.attr("totalBytes", totalBytes); - } -#endif if (countCalls) { { @@ -1893,6 +1829,7 @@ size_t valueSize(Value & v) sz += doValue(*i.value); } break; + case tList0: case tList1: case tList2: case tListN: @@ -1914,14 +1851,16 @@ size_t valueSize(Value & v) sz += doEnv(*v.lambda.env); break; case tPrimOpApp: - sz += doValue(*v.primOpApp.left); - sz += doValue(*v.primOpApp.right); + sz += doValue(*v.app.left); + sz += doValue(*v.app.right); break; +#if 0 case tExternal: if (seen.find(v.external) != seen.end()) break; seen.insert(v.external); sz += v.external->valueSize(seen); break; +#endif default: ; } @@ -1949,6 +1888,7 @@ size_t valueSize(Value & v) } +#if 0 string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const { throw TypeError(format("cannot coerce %1% to a string, at %2%") % @@ -1965,6 +1905,7 @@ bool ExternalValueBase::operator==(const ExternalValueBase & b) const std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) { return v.print(str); } +#endif EvalSettings evalSettings; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index a314e01e0..2da34e634 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -6,6 +6,7 @@ #include "symbol-table.hh" #include "hash.hh" #include "config.hh" +#include "gc.hh" #include #include @@ -32,13 +33,26 @@ struct PrimOp }; -struct Env +struct Env : Object { Env * up; + // FIXME: use misc field unsigned short size; // used by ‘valueSize’ unsigned short prevWith:14; // nr of levels up to next `with' environment - enum { Plain = 0, HasWithExpr, HasWithAttrs } type:2; + enum { Plain = 0, HasWithExpr, HasWithAttrs } type:2; // FIXME: fold into type? Value * values[0]; + + Env(unsigned short size) : Object(tEnv, 0), size(size) {} + + Size words() const + { + return wordsFor(size); + } + + static Size wordsFor(unsigned short size) + { + return 3 + size; // FIXME + } }; @@ -83,7 +97,7 @@ public: mode. */ std::optional allowedPaths; - Value vEmptySet; + Ptr emptyBindings; const ref store; @@ -91,19 +105,11 @@ private: SrcToStore srcToStore; /* A cache from path names to parse trees. */ -#if HAVE_BOEHMGC - typedef std::map, traceable_allocator > > FileParseCache; -#else typedef std::map FileParseCache; -#endif FileParseCache fileParseCache; /* A cache from path names to values. */ -#if HAVE_BOEHMGC - typedef std::map, traceable_allocator > > FileEvalCache; -#else - typedef std::map FileEvalCache; -#endif + typedef std::map> FileEvalCache; FileEvalCache fileEvalCache; SearchPath searchPath; @@ -213,7 +219,7 @@ public: /* The base environment, containing the builtin functions and values. */ - Env & baseEnv; + Ptr baseEnv; /* The same, but used during parsing to resolve variables. */ StaticEnv staticBaseEnv; // !!! should be private @@ -260,13 +266,13 @@ public: void autoCallFunction(Bindings & args, Value & fun, Value & res); /* Allocation primitives. */ - Value * allocValue(); - Env & allocEnv(size_t size); + Ptr allocValue(); + Ptr allocEnv(size_t size); + // Note: the resulting Value is only reachable as long as vAttrs + // is reachable. Value * allocAttr(Value & vAttrs, const Symbol & name); - Bindings * allocBindings(size_t capacity); - void mkList(Value & v, size_t length); void mkAttrs(Value & v, size_t capacity); void mkThunk_(Value & v, Expr * expr); diff --git a/src/libexpr/gc.cc b/src/libexpr/gc.cc new file mode 100644 index 000000000..af0cbb693 --- /dev/null +++ b/src/libexpr/gc.cc @@ -0,0 +1,287 @@ +#include "gc.hh" +#include "value.hh" +#include "attr-set.hh" +#include "eval.hh" + +namespace nix { + +GC gc; + +GC::GC() +{ + // FIXME: placement new + frontSentinel = (Ptr *) malloc(sizeof(Ptr)); + backSentinel = (Ptr *) malloc(sizeof(Ptr)); + + frontSentinel->prev = nullptr; + frontSentinel->next = backSentinel; + + backSentinel->prev = frontSentinel; + backSentinel->next = nullptr; +} + +GC::~GC() +{ + size_t n = 0; + for (Ptr * p = frontSentinel->next; p != backSentinel; p = p->next) + n++; + if (n) + warn("%d GC roots still exist on exit", n); + assert(!frontSentinel->prev); + assert(!backSentinel->next); +} + +void GC::gc() +{ + size_t marked = 0; + + std::stack stack; + + for (Ptr * p = frontSentinel->next; p != backSentinel; p = p->next) { + if (!p->value) continue; + + stack.push(p->value); + + while (!stack.empty()) { + auto obj = stack.top(); + stack.pop(); + + // FIXME: ensure this gets inlined. + auto push = [&](Object * p) { if (p) { /* FIXME */ assert(isObject(p)); stack.push(p); } }; + + //printError("MARK %x", obj); + + if (!obj->isMarked()) { + marked++; + obj->mark(); + switch (obj->type) { + + case tFree: + printError("reached a freed object at %x", obj); + abort(); + + case tBindings: { + auto obj2 = (Bindings *) obj; + for (auto i = obj2->attrs; i < obj2->attrs + obj2->size_; ++i) + push(i->value); + break; + } + + case tValueList: { + auto obj2 = (PtrList *) obj; + for (auto i = obj2->elems; i < obj2->elems + obj2->size(); ++i) + push(*i); + break; + } + + case tEnv: { + auto obj2 = (Env *) obj; + push(obj2->up); + if (obj2->type != Env::HasWithExpr) + for (auto i = obj2->values; i < obj2->values + obj2->size; ++i) + push(*i); + break; + } + + case tInt: + case tBool: + case tNull: + case tList0: + case tFloat: + break; + + case tString: + // FIXME + break; + + case tPath: + // FIXME + break; + + case tAttrs: + push(((Value *) obj)->attrs); + break; + + case tList1: + push(((Value *) obj)->smallList[0]); + break; + + case tList2: + push(((Value *) obj)->smallList[0]); + push(((Value *) obj)->smallList[1]); + break; + + case tListN: + push(((Value *) obj)->bigList); + break; + + case tThunk: + push(((Value *) obj)->thunk.env); + break; + + case tApp: + case tPrimOpApp: + push(((Value *) obj)->app.left); + push(((Value *) obj)->app.right); + break; + + case tLambda: + push(((Value *) obj)->lambda.env); + break; + + case tBlackhole: + // FIXME + break; + + case tPrimOp: + // FIXME: GC primops? + break; + + default: + printError("don't know how to traverse object at %x (tag %d)", obj, obj->type); + abort(); + } + } + } + } + + Size totalObjectsFreed = 0; + Size totalWordsFreed = 0; + + for (auto & arenaList : arenaLists) { + + for (auto & arena : arenaList.arenas) { + auto [objectsFreed, wordsFreed] = arena.freeUnmarked(); + totalObjectsFreed += objectsFreed; + totalWordsFreed += wordsFreed; + } + + std::sort(arenaList.arenas.begin(), arenaList.arenas.end(), + [](const Arena & a, const Arena & b) { + return b.free < a.free; + }); + } + + printError("freed %d bytes in %d dead objects, keeping %d objects", + totalWordsFreed * WORD_SIZE, totalObjectsFreed, marked); +} + +std::pair GC::Arena::freeUnmarked() +{ + Size objectsFreed = 0; + Size wordsFreed = 0; + + auto end = start + size; + auto pos = start; + + Free * curFree = nullptr; + Free * * freeLink = &firstFree; + + free = 0; + + while (pos < end) { + auto obj = (Object *) pos; + auto tag = obj->type; + + auto linkFree = [&]() { + *freeLink = curFree; + freeLink = &curFree->next; + }; + + Size objSize; + if (tag >= tInt && tag <= tFloat) { + objSize = ((Value *) obj)->words(); + } else { + switch (tag) { + case tFree: + objSize = ((Free *) obj)->words(); + break; + case tBindings: + objSize = ((Bindings *) obj)->words(); + break; + case tValueList: + objSize = ((PtrList *) obj)->words(); + break; + case tEnv: + objSize = ((Env *) obj)->words(); + break; + default: + abort(); + //throw Error("GC encountered invalid object with tag %d", tag); + } + } + + // Merge current object into the previous free object. + auto mergeFree = [&]() { + //printError("MERGE %x %x %d", curFree, obj, curFree->size() + objSize); + assert(curFree->words() >= 1); + if (curFree->words() == 1) { + linkFree(); + free += 1; + } + curFree->setSize(curFree->words() + objSize); + free += objSize; + }; + + if (tag == tFree) { + if (curFree) { + // Merge this object into the previous free + // object. + mergeFree(); + } else { + curFree = (Free *) obj; + if (curFree->words() > 1) { + linkFree(); + free += curFree->words(); + } + } + } else { + + if (obj->isMarked()) { + // Unmark to prepare for the next GC run. + curFree = nullptr; + obj->unmark(); + } else { + //printError("FREE %x %d %d", obj, obj->type, objSize); + objectsFreed += 1; + wordsFreed += objSize; + if (curFree) { + mergeFree(); + } else { + // Convert to a free object. + curFree = (Free *) obj; + curFree->type = tFree; + curFree->setSize(objSize); + linkFree(); + free += objSize; + } + } + } + + pos += objSize; + } + + assert(pos == end); + + *freeLink = nullptr; + + return {objectsFreed, wordsFreed}; +} + +bool GC::isObject(void * p) +{ + for (auto & arenaList : arenaLists) { + for (auto & arena : arenaList.arenas) { + if (p >= arena.start && p < arena.start + arena.size) + return true; + } + } + return false; +} + +GC::ArenaList::ArenaList() + : nextSize(1024) +{ +} + +} diff --git a/src/libexpr/gc.hh b/src/libexpr/gc.hh new file mode 100644 index 000000000..1b27c96b6 --- /dev/null +++ b/src/libexpr/gc.hh @@ -0,0 +1,346 @@ +#pragma once + +#include "logging.hh" + +#include +#include +#include + +namespace nix { + +typedef unsigned long Word; +typedef size_t Size; // size in words + +enum Tag { + tFree = 3, + + // Misc types + tBindings, + tValueList, + tEnv, + + // Value tags + tInt, + tBool, + tString, + tPath, + tNull, + tAttrs, + tList0, + tList1, + tList2, + tListN, + tThunk, + tApp, + tLambda, + tBlackhole, + tPrimOp, + tPrimOpApp, + tExternal, + tFloat +}; + +constexpr size_t WORD_SIZE = 8; + +struct Object +{ + friend class GC; + +public: + constexpr static unsigned int miscBits = 58; + +private: + unsigned long misc:58; + +public: // FIXME + Tag type:5; + +private: + bool marked:1; + + void unmark() + { + marked = false; + } + +protected: + + Object(Tag type, unsigned long misc) : misc(misc), type(type), marked(false) { } + + bool isMarked() + { + return marked; + } + + void mark() + { + marked = true; + } + + void setMisc(unsigned int misc) + { + this->misc = misc; + } + + unsigned int getMisc() const + { + return misc; + } +}; + +template +struct PtrList : Object +{ + T * elems[0]; + + PtrList(Tag type, Size size) : Object(type, size) + { + for (Size i = 0; i < size; i++) + elems[i] = nullptr; + } + + Size size() const { return getMisc(); } + Size words() const { return wordsFor(size()); } + static Size wordsFor(Size size) { return 1 + size; } +}; + +template +struct Ptr +{ + Ptr * prev = nullptr, * next = nullptr; + T * value = nullptr; + + Ptr() { } + + Ptr(Ptr * next, T * value) : next(next), value(value) + { + assert(value); + assert(next == next->prev->next); + prev = next->prev; + next->prev = this; + prev->next = this; + } + + Ptr(const Ptr & p) + { + if (p.value) { + auto & p2 = const_cast(p); + value = p2.value; + next = &p2; + prev = p2.prev; + prev->next = this; + p2.prev = this; + } + } + + Ptr(Ptr && p) + { + *this = std::move(p); + } + + Ptr & operator =(Ptr && p) + { + reset(); + if (p.value) { + value = p.value; + next = p.next; + prev = p.prev; + p.value = nullptr; + prev->next = this; + next->prev = this; + } + return *this; + } + + Ptr & operator =(const Ptr & p) + { + throw Error("NOT IMPLEMENTED = PTR &"); + } + + ~Ptr() + { + reset(); + } + + void reset() + { + if (value) { + assert(next); + assert(prev); + assert(next->prev == this); + next->prev = prev; + assert(prev->next == this); + prev->next = next; + value = nullptr; + } + } + + T * operator ->() + { + return value; + } + + operator T * () + { + return value; + } + + operator T & () + { + assert(value); + return *value; + } +}; + +struct Free : Object +{ + Free * next; + + Free(Size size) : Object(tFree, size), next(nullptr) { } + + // return size in words + Size words() const { return getMisc(); } + + void setSize(Size size) { assert(size >= 1); setMisc(size); } +}; + +struct GC +{ + +private: + + Ptr * frontSentinel; + Ptr * backSentinel; + + struct Arena + { + Size size; // in words + Size free; // words free + Free * firstFree; + Word * start; + + Arena(Size size) + : size(size) + , free(size) + , start(new Word[size]) + { + assert(size >= 2); + firstFree = new (start) Free(size); + } + + Arena(const Arena & arena) = delete; + + Arena(Arena && arena) + { + *this = std::move(arena); + } + + Arena & operator =(Arena && arena) + { + size = arena.size; + free = arena.free; + firstFree = arena.firstFree; + start = arena.start; + arena.start = nullptr; + return *this; + } + + ~Arena() + { + delete[] start; + } + + Object * alloc(Size size) + { + assert(size >= 2); + + Free * * prev = &firstFree; + + while (Free * freeObj = *prev) { + //printError("LOOK %x %d %x", freeObj, freeObj->words(), freeObj->next); + assert(freeObj->words() >= 2); + if (freeObj->words() == size) { + *prev = freeObj->next; + assert(free >= size); + free -= size; + return (Object *) freeObj; + } else if (freeObj->words() >= size + 2) { + // Split this free object. + auto newSize = freeObj->words() - size; + freeObj->setSize(newSize); + assert(free >= size); + free -= size; + return (Object *) (((Word *) freeObj) + newSize); + } else if (freeObj->words() == size + 1) { + // Return this free object and add a padding word. + *prev = freeObj->next; + freeObj->setSize(1); + assert(free >= size + 1); + free -= size + 1; + return (Object *) (((Word *) freeObj) + 1); + } else { + assert(freeObj->words() < size); + prev = &freeObj->next; + } + } + + return nullptr; + } + + std::pair freeUnmarked(); + }; + + // Note: arenas are sorted by ascending amount of free space. + struct ArenaList + { + Size nextSize; + std::vector arenas; + ArenaList(); + }; + + std::array arenaLists; + +public: + + GC(); + ~GC(); + + template + Ptr alloc(Size size, const Args & ... args) + { + ArenaList & arenaList = size == 3 ? arenaLists[0] : arenaLists[1]; + + for (int i = 0; i < 3; i++) { + + for (auto j = arenaList.arenas.rbegin(); j != arenaList.arenas.rend(); ++j) { + auto & arena = *j; + auto raw = arena.alloc(size); + if (raw) { + auto obj = new (raw) T(args...); + //printError("ALLOC %x", obj); + return Ptr((Ptr *) frontSentinel->next, obj); + } + } + + if (i == 0) { + printError("allocation of %d bytes failed, GCing...", size * WORD_SIZE); + gc(); + } else { + Size arenaSize = std::max(arenaList.nextSize, size); + arenaList.nextSize = arenaSize * 2; // FIXME: overflow + printError("allocating arena of %d bytes", arenaSize * WORD_SIZE); + arenaList.arenas.emplace_back(arenaSize); + } + } + + throw Error("allocation of %d bytes failed", size); + } + + void gc(); + + bool isObject(void * p); +}; + +extern GC gc; + +} diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 21a4d7917..e3a98a47b 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -248,7 +248,7 @@ void DrvInfo::setMeta(const string & name, Value * v) { getMeta(); Bindings * old = meta; - meta = state->allocBindings(1 + (old ? old->size() : 0)); + meta = Bindings::allocBindings(1 + (old ? old->size() : 0)); Symbol sym = state->symbols.create(name); if (old) for (auto i : *old) diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index d7860fc6a..526f251c3 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -69,11 +69,7 @@ public: }; -#if HAVE_BOEHMGC -typedef list > DrvInfos; -#else -typedef list DrvInfos; -#endif +typedef std::list DrvInfos; /* If value `v' denotes a derivation, return a DrvInfo object diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 3f6017957..50e824c30 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -52,9 +52,9 @@ static void parseJSON(EvalState & state, const char * & s, Value & v) skipWhitespace(s); while (1) { if (values.empty() && *s == ']') break; - Value * v2 = state.allocValue(); + Ptr v2 = state.allocValue(); parseJSON(state, s, *v2); - values.push_back(v2); + values.push_back(std::move(v2)); skipWhitespace(s); if (*s == ']') break; if (*s != ',') throw JSONParseError("expected ',' or ']' after JSON array element"); @@ -76,9 +76,9 @@ static void parseJSON(EvalState & state, const char * & s, Value & v) skipWhitespace(s); if (*s != ':') throw JSONParseError("expected ':' in JSON object"); s++; - Value * v2 = state.allocValue(); + Ptr v2 = state.allocValue(); parseJSON(state, s, *v2); - attrs[state.symbols.create(name)] = v2; + attrs.emplace(state.symbols.create(name), std::move(v2)); skipWhitespace(s); if (*s == '}') break; if (*s != ',') throw JSONParseError("expected ',' or '}' after JSON member"); @@ -98,7 +98,7 @@ static void parseJSON(EvalState & state, const char * & s, Value & v) else if (isdigit(*s) || *s == '-' || *s == '.' ) { // Buffer into a string first, then use built-in C++ conversions std::string tmp_number; - ValueType number_type = tInt; + Tag number_type = tInt; while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') { if (*s == '.' || *s == 'e' || *s == 'E') diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index ccd5293e4..59c60e4c9 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -13,11 +13,6 @@ ifneq ($(OS), FreeBSD) libexpr_LDFLAGS += -ldl endif -# The dependency on libgc must be propagated (i.e. meaning that -# programs/libraries that use libexpr must explicitly pass -lgc), -# because inline functions in libexpr's header files call libgc. -libexpr_LDFLAGS_PROPAGATED = $(BDW_GC_LIBS) - libexpr_ORDER_AFTER := $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh $(d)/parser-tab.cc $(d)/parser-tab.hh: $(d)/parser.y diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 665a42987..88206789c 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -92,8 +92,11 @@ std::ostream & operator << (std::ostream & str, const Expr & e); struct ExprInt : Expr { NixInt n; - Value v; - ExprInt(NixInt n) : n(n) { mkInt(v, n); }; + Ptr v; + ExprInt(NixInt n) : n(n) { + v = gc.alloc(Value::words()); + mkInt(v, n); + }; COMMON_METHODS Value * maybeThunk(EvalState & state, Env & env); }; @@ -101,8 +104,11 @@ struct ExprInt : Expr struct ExprFloat : Expr { NixFloat nf; - Value v; - ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); }; + Ptr v; + ExprFloat(NixFloat nf) : nf(nf) { + v = gc.alloc(Value::words()); + mkFloat(v, nf); + }; COMMON_METHODS Value * maybeThunk(EvalState & state, Env & env); }; @@ -110,8 +116,11 @@ struct ExprFloat : Expr struct ExprString : Expr { Symbol s; - Value v; - ExprString(const Symbol & s) : s(s) { mkString(v, s); }; + Ptr v; + ExprString(const Symbol & s) : s(s) { + v = gc.alloc(Value::words()); + mkString(v, s); + }; COMMON_METHODS Value * maybeThunk(EvalState & state, Env & env); }; @@ -126,8 +135,11 @@ struct ExprIndStr : Expr struct ExprPath : Expr { string s; - Value v; - ExprPath(const string & s) : s(s) { mkPathNoCopy(v, this->s.c_str()); }; + Ptr v; + ExprPath(const string & s) : s(s) { + v = gc.alloc(Value::words()); + mkPathNoCopy(v, this->s.c_str()); + }; COMMON_METHODS Value * maybeThunk(EvalState & state, Env & env); }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 39073725e..75c62e153 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -130,8 +130,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args if (args[0]->attrs->empty()) state.evalFile(realPath, v); else { - Env * env = &state.allocEnv(args[0]->attrs->size()); - env->up = &state.baseEnv; + auto env = state.allocEnv(args[0]->attrs->size()); + env->up = state.baseEnv; StaticEnv staticEnv(false, &state.staticBaseEnv); @@ -243,15 +243,17 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu case tPath: t = "path"; break; case tNull: t = "null"; break; case tAttrs: t = "set"; break; - case tList1: case tList2: case tListN: t = "list"; break; + case tList0: case tList1: case tList2: case tListN: t = "list"; break; case tLambda: case tPrimOp: case tPrimOpApp: t = "lambda"; break; +#if 0 case tExternal: t = args[0]->external->typeOf(); break; +#endif case tFloat: t = "float"; break; default: abort(); } @@ -348,15 +350,12 @@ struct CompareValues }; -#if HAVE_BOEHMGC -typedef list > ValueList; -#else -typedef list ValueList; -#endif +typedef list> ValueList; static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) { +#if 0 state.forceAttrs(*args[0], pos); /* Get the start set. */ @@ -417,6 +416,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar unsigned int n = 0; for (auto & i : res) v.listElems()[n++] = i; +#endif + abort(); } @@ -1603,7 +1604,8 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V auto len = args[1]->listSize(); - ValueVector right, wrong; + // Note: these Values are reachable via args[0]. + std::vector right, wrong; for (unsigned int n = 0; n < len; ++n) { auto vElem = args[1]->listElems()[n]; @@ -2107,10 +2109,10 @@ RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun) void EvalState::createBaseEnv() { - baseEnv.up = 0; + baseEnv->up = 0; /* Add global constants such as `true' to the base environment. */ - Value v; + auto v = allocValue(); /* `builtins' must be first! */ mkAttrs(v, 128); @@ -2287,7 +2289,7 @@ void EvalState::createBaseEnv() mkList(v, searchPath.size()); int n = 0; for (auto & i : searchPath) { - v2 = v.listElems()[n++] = allocValue(); + v2 = v->listElems()[n++] = allocValue(); mkAttrs(*v2, 2); mkString(*allocAttr(*v2, symbols.create("path")), i.second); mkString(*allocAttr(*v2, symbols.create("prefix")), i.first); @@ -2301,7 +2303,7 @@ void EvalState::createBaseEnv() /* Now that we've added all primops, sort the `builtins' set, because attribute lookups expect it to be sorted. */ - baseEnv.values[0]->attrs->sort(); + baseEnv->values[0]->attrs->sort(); } diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 72e413e44..73f2843aa 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -56,7 +56,7 @@ void printValueAsJSON(EvalState & state, bool strict, break; } - case tList1: case tList2: case tListN: { + case tList0: case tList1: case tList2: case tListN: { auto list(out.list()); for (unsigned int n = 0; n < v.listSize(); ++n) { auto placeholder(list.placeholder()); @@ -65,9 +65,11 @@ void printValueAsJSON(EvalState & state, bool strict, break; } +#if 0 case tExternal: v.external->printValueAsJSON(state, strict, out, context); break; +#endif case tFloat: out.write(v.fpoint); @@ -85,11 +87,13 @@ void printValueAsJSON(EvalState & state, bool strict, printValueAsJSON(state, strict, v, out, context); } +#if 0 void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, JSONPlaceholder & out, PathSet & context) const { throw TypeError(format("cannot convert %1% to JSON") % showType()); } +#endif } diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 00b1918a8..d00b1c621 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -119,7 +119,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, break; - case tList1: case tList2: case tListN: { + case tList0: case tList1: case tList2: case tListN: { XMLOpenElement _(doc, "list"); for (unsigned int n = 0; n < v.listSize(); ++n) printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen); @@ -144,9 +144,11 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, break; } +#if 0 case tExternal: v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen); break; +#endif case tFloat: doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str())); @@ -158,11 +160,13 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, } +#if 0 void ExternalValueBase::printValueAsXML(EvalState & state, bool strict, bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const { doc.writeEmptyElement("unevaluated"); } +#endif void printValueAsXML(EvalState & state, bool strict, bool location, diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index e1ec87d3b..ee0008996 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -1,35 +1,10 @@ #pragma once #include "symbol-table.hh" - -#if HAVE_BOEHMGC -#include -#endif +#include "gc.hh" namespace nix { - -typedef enum { - tInt = 1, - tBool, - tString, - tPath, - tNull, - tAttrs, - tList1, - tList2, - tListN, - tThunk, - tApp, - tLambda, - tBlackhole, - tPrimOp, - tPrimOpApp, - tExternal, - tFloat -} ValueType; - - class Bindings; struct Env; struct Expr; @@ -46,6 +21,7 @@ class JSONPlaceholder; typedef int64_t NixInt; typedef double NixFloat; +#if 0 /* External values must descend from ExternalValueBase, so that * type-agnostic nix functions (e.g. showType) can be implemented */ @@ -90,11 +66,11 @@ class ExternalValueBase }; std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); +#endif -struct Value +struct Value : Object { - ValueType type; union { NixInt integer; @@ -127,10 +103,7 @@ struct Value const char * path; Bindings * attrs; - struct { - size_t size; - Value * * elems; - } bigList; + PtrList * bigList; Value * smallList[2]; struct { Env * env; @@ -144,46 +117,38 @@ struct Value ExprLambda * fun; } lambda; PrimOp * primOp; - struct { - Value * left, * right; - } primOpApp; - ExternalValueBase * external; + //ExternalValueBase * external; NixFloat fpoint; }; + Value() : Object(tBlackhole, 0) { } + bool isList() const { - return type == tList1 || type == tList2 || type == tListN; + return type >= tList0 && type <= tListN; } Value * * listElems() { - return type == tList1 || type == tList2 ? smallList : bigList.elems; + return type == tList0 || type == tList1 || type == tList2 ? smallList : bigList->elems; } const Value * const * listElems() const { - return type == tList1 || type == tList2 ? smallList : bigList.elems; + return type == tList0 || type == tList1 || type == tList2 ? smallList : bigList->elems; } size_t listSize() const { - return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size; + return type == tList0 ? 0 : type == tList1 ? 1 : type == tList2 ? 2 : bigList->size(); } + + constexpr static Size words() { return 3; } // FIXME }; -/* After overwriting an app node, be sure to clear pointers in the - Value to ensure that the target isn't kept alive unnecessarily. */ -static inline void clearValue(Value & v) -{ - v.app.left = v.app.right = 0; -} - - static inline void mkInt(Value & v, NixInt n) { - clearValue(v); v.type = tInt; v.integer = n; } @@ -191,7 +156,6 @@ static inline void mkInt(Value & v, NixInt n) static inline void mkFloat(Value & v, NixFloat n) { - clearValue(v); v.type = tFloat; v.fpoint = n; } @@ -199,7 +163,6 @@ static inline void mkFloat(Value & v, NixFloat n) static inline void mkBool(Value & v, bool b) { - clearValue(v); v.type = tBool; v.boolean = b; } @@ -207,7 +170,6 @@ static inline void mkBool(Value & v, bool b) static inline void mkNull(Value & v) { - clearValue(v); v.type = tNull; } @@ -247,7 +209,6 @@ void mkString(Value & v, const char * s); static inline void mkPathNoCopy(Value & v, const char * s) { - clearValue(v); v.type = tPath; v.path = s; } @@ -262,13 +223,8 @@ void mkPath(Value & v, const char * s); size_t valueSize(Value & v); -#if HAVE_BOEHMGC -typedef std::vector > ValueVector; -typedef std::map, gc_allocator > > ValueMap; -#else -typedef std::vector ValueVector; -typedef std::map ValueMap; -#endif +typedef std::vector> ValueVector; // FIXME: make more efficient +typedef std::map> ValueMap; // FIXME: use Bindings? } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 4ed34e54d..60dadfab7 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -244,9 +244,6 @@ void printVersion(const string & programName) std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl; if (verbosity > lvlInfo) { Strings cfg; -#if HAVE_BOEHMGC - cfg.push_back("gc"); -#endif #if HAVE_SODIUM cfg.push_back("signed-caches"); #endif diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 7b9a88281..9c021a263 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -19,7 +19,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv) if (pathExists(manifestFile)) { Value v; state.evalFile(manifestFile, v); - Bindings & bindings(*state.allocBindings(0)); + auto bindings = Bindings::allocBindings(0); getDerivations(state, v, "", bindings, elems, false); } return elems; @@ -42,7 +42,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, /* Construct the whole top level derivation. */ PathSet references; - Value manifest; + auto manifest = state.allocValue(); state.mkList(manifest, elems.size()); unsigned int n = 0; for (auto & i : elems) { @@ -51,8 +51,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, as the meta attributes. */ Path drvPath = keepDerivations ? i.queryDrvPath() : ""; - Value & v(*state.allocValue()); - manifest.listElems()[n++] = &v; + auto v = state.allocValue(); + manifest->listElems()[n++] = (Value *) v; state.mkAttrs(v, 16); mkString(*state.allocAttr(v, state.sType), "derivation"); @@ -70,7 +70,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, state.mkList(vOutputs, outputs.size()); unsigned int m = 0; for (auto & j : outputs) { - mkString(*(vOutputs.listElems()[m++] = state.allocValue()), j.first); + mkString(*(vOutputs.listElems()[m++] = (Value *) state.allocValue()), j.first); Value & vOutputs = *state.allocAttr(v, state.symbols.create(j.first)); state.mkAttrs(vOutputs, 2); mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second); @@ -93,7 +93,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, vMeta.attrs->push_back(Attr(state.symbols.create(j), v)); } vMeta.attrs->sort(); - v.attrs->sort(); + v->attrs->sort(); if (drvPath != "") references.insert(drvPath); } @@ -102,7 +102,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, the store; we need it for future modifications of the environment. */ Path manifestFile = state.store->addTextToStore("env-manifest.nix", - (format("%1%") % manifest).str(), references); + fmt("%1%", (Value &) manifest), references); /* Get the environment builder expression. */ Value envBuilder; @@ -114,7 +114,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, state.mkAttrs(args, 3); mkString(*state.allocAttr(args, state.symbols.create("manifest")), manifestFile, {manifestFile}); - args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest)); + args.attrs->push_back(Attr(state.symbols.create("derivations"), (Value *) manifest)); args.attrs->sort(); mkApp(topLevel, envBuilder, args); diff --git a/src/nix/edit.cc b/src/nix/edit.cc index c9671f76d..7a356236f 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -38,8 +38,8 @@ struct CmdEdit : InstallableCommand Value * v2; try { - auto dummyArgs = state->allocBindings(0); - v2 = findAlongAttrPath(*state, "meta.position", *dummyArgs, *v); + auto dummyArgs = Bindings::allocBindings(0); + v2 = findAlongAttrPath(*state, "meta.position", dummyArgs, *v); } catch (Error &) { throw Error("package '%s' has no source location information", installable->what()); } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 227affc60..ac6f11a33 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -44,7 +44,7 @@ struct NixRepl const static int envSize = 32768; StaticEnv staticEnv; - Env * env; + Ptr env; int displ; StringSet varNames; @@ -506,8 +506,8 @@ void NixRepl::loadFile(const Path & path) void NixRepl::initEnv() { - env = &state.allocEnv(envSize); - env->up = &state.baseEnv; + env = state.allocEnv(envSize); + env->up = state.baseEnv; displ = 0; staticEnv.vars.clear(); @@ -665,6 +665,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m break; } + case tList0: case tList1: case tList2: case tListN: diff --git a/src/nix/search.cc b/src/nix/search.cc index e086de226..e60c468b9 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -129,7 +129,7 @@ struct CmdSearch : SourceExprCommand, MixJSON if (v->type == tLambda && toplevel) { Value * v2 = state->allocValue(); - state->autoCallFunction(*state->allocBindings(1), *v, *v2); + state->autoCallFunction(*Bindings::allocBindings(1), *v, *v2); v = v2; state->forceValue(*v); } diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 35c44a70c..50e6d5eb7 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -150,7 +150,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand auto state = std::make_unique(Strings(), store); auto v = state->allocValue(); state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v); - Bindings & bindings(*state->allocBindings(0)); + Bindings & bindings(*Bindings::allocBindings(0)); auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v); return state->forceString(*v2);