1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-15 06:52:43 +01:00

Checkpoint

This commit is contained in:
Eelco Dolstra 2019-04-15 18:40:35 +02:00
parent bb6e6923f2
commit 4237414f4d
23 changed files with 844 additions and 295 deletions

View file

@ -7,26 +7,25 @@
namespace nix { namespace nix {
/* Allocate a new array of attributes for an attribute set with a specific /* Allocate a new array of attributes for an attribute set with a
capacity. The space is implicitly reserved after the Bindings specific capacity. The space is implicitly reserved after the
structure. */ Bindings structure. */
Bindings * EvalState::allocBindings(size_t capacity) Ptr<Bindings> Bindings::allocBindings(size_t capacity)
{ {
if (capacity > std::numeric_limits<Bindings::size_t>::max()) if (capacity >= 1UL << Object::miscBits)
throw Error("attribute set of size %d is too big", capacity); 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>(Bindings::wordsFor(capacity), capacity);
} }
void EvalState::mkAttrs(Value & v, size_t capacity) void EvalState::mkAttrs(Value & v, size_t capacity)
{ {
v.type = tAttrs;
if (capacity == 0) { if (capacity == 0) {
v = vEmptySet; v.attrs = emptyBindings;
return; return;
} }
clearValue(v); v.attrs = Bindings::allocBindings(capacity);
v.type = tAttrs;
v.attrs = allocBindings(capacity);
nrAttrsets++; nrAttrsets++;
nrAttrsInAttrsets += capacity; nrAttrsInAttrsets += capacity;
} }

View file

@ -2,6 +2,7 @@
#include "nixexpr.hh" #include "nixexpr.hh"
#include "symbol-table.hh" #include "symbol-table.hh"
#include "gc.hh"
#include <algorithm> #include <algorithm>
@ -30,19 +31,22 @@ struct Attr
by its size and its capacity, the capacity being the number of Attr by its size and its capacity, the capacity being the number of Attr
elements allocated after this structure, while the size corresponds to elements allocated after this structure, while the size corresponds to
the number of elements already inserted in this structure. */ the number of elements already inserted in this structure. */
class Bindings class Bindings : public Object
{ {
public: public:
typedef uint32_t size_t; typedef uint32_t size_t;
private: 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]; 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; Bindings(const Bindings & bindings) = delete;
public:
size_t size() const { return size_; } size_t size() const { return size_; }
bool empty() const { return !size_; } bool empty() const { return !size_; }
@ -51,7 +55,7 @@ public:
void push_back(const Attr & attr) void push_back(const Attr & attr)
{ {
assert(size_ < capacity_); assert(size_ < capacity());
attrs[size_++] = attr; attrs[size_++] = attr;
} }
@ -73,7 +77,7 @@ public:
void sort(); void sort();
size_t capacity() { return capacity_; } size_t capacity() const { return getMisc(); }
/* Returns the attributes in lexicographically sorted order. */ /* Returns the attributes in lexicographically sorted order. */
std::vector<const Attr *> lexicographicOrder() const std::vector<const Attr *> lexicographicOrder() const
@ -88,7 +92,19 @@ public:
return res; return res;
} }
friend class EvalState; Size words() const
{
return wordsFor(capacity());
}
static Size wordsFor(size_t capacity)
{
return 2 + 3 * capacity; // FIXME
}
static Ptr<Bindings> allocBindings(size_t capacity);
friend class GC;
}; };

View file

@ -30,7 +30,7 @@ MixEvalArgs::MixEvalArgs()
Bindings * MixEvalArgs::getAutoArgs(EvalState & state) Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
{ {
Bindings * res = state.allocBindings(autoArgs.size()); Bindings * res = Bindings::allocBindings(autoArgs.size());
for (auto & i : autoArgs) { for (auto & i : autoArgs) {
Value * v = state.allocValue(); Value * v = state.allocValue();
if (i.second[0] == 'E') if (i.second[0] == 'E')

View file

@ -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); 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;
}
} }

View file

@ -19,24 +19,14 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/resource.h> #include <sys/resource.h>
#if HAVE_BOEHMGC
#include <gc/gc.h>
#include <gc/gc_cpp.h>
#endif
namespace nix { namespace nix {
// FIXME
static char * dupString(const char * s) static char * dupString(const char * s)
{ {
char * t; char * t;
#if HAVE_BOEHMGC
t = GC_STRDUP(s);
#else
t = strdup(s); t = strdup(s);
#endif
if (!t) throw std::bad_alloc(); if (!t) throw std::bad_alloc();
return t; return t;
} }
@ -85,6 +75,7 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
str << "}"; str << "}";
break; break;
} }
case tList0:
case tList1: case tList1:
case tList2: case tList2:
case tListN: case tListN:
@ -108,9 +99,11 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
case tPrimOpApp: case tPrimOpApp:
str << "<PRIMOP-APP>"; str << "<PRIMOP-APP>";
break; break;
#if 0
case tExternal: case tExternal:
str << *v.external; str << *v.external;
break; break;
#endif
case tFloat: case tFloat:
str << v.fpoint; str << v.fpoint;
break; 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; const Value * primOp = &v;
while (primOp->type == tPrimOpApp) { while (primOp->type == tPrimOpApp) {
primOp = primOp->primOpApp.left; primOp = primOp->app.left;
} }
assert(primOp->type == tPrimOp); assert(primOp->type == tPrimOp);
return primOp; return primOp;
@ -149,7 +142,7 @@ string showType(const Value & v)
case tPath: return "a path"; case tPath: return "a path";
case tNull: return "null"; case tNull: return "null";
case tAttrs: return "a set"; 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 tThunk: return "a thunk";
case tApp: return "a function application"; case tApp: return "a function application";
case tLambda: return "a function"; 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)); return fmt("the built-in function '%s'", string(v.primOp->name));
case tPrimOpApp: case tPrimOpApp:
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name)); return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
#if 0
case tExternal: return v.external->showType(); 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 #endif
case tFloat: return "a float";
default:
abort();
}
}
static Symbol getName(const AttrName & name, EvalState & state, Env & env) static Symbol getName(const AttrName & name, EvalState & state, Env & env)
@ -194,46 +180,6 @@ void initGC()
{ {
if (gcInitialised) return; 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; gcInitialised = true;
} }
@ -311,7 +257,9 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
assert(gcInitialised); assert(gcInitialised);
#if 0
static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes"); static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes");
#endif
/* Initialise the Nix expression search path. */ /* Initialise the Nix expression search path. */
if (!evalSettings.pureEval) { if (!evalSettings.pureEval) {
@ -340,9 +288,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
} }
} }
clearValue(vEmptySet); emptyBindings = Bindings::allocBindings(0);
vEmptySet.type = tAttrs;
vEmptySet.attrs = allocBindings(0);
createBaseEnv(); createBaseEnv();
} }
@ -441,9 +387,9 @@ Value * EvalState::addConstant(const string & name, Value & v)
Value * v2 = allocValue(); Value * v2 = allocValue();
*v2 = v; *v2 = v;
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v2; baseEnv->values[baseEnvDispl++] = v2;
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; 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; return v2;
} }
@ -462,15 +408,15 @@ Value * EvalState::addPrimOp(const string & name,
v->type = tPrimOp; v->type = tPrimOp;
v->primOp = new PrimOp(primOp, arity, sym); v->primOp = new PrimOp(primOp, arity, sym);
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
baseEnv.values[baseEnvDispl++] = v; baseEnv->values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(sym, v)); baseEnv->values[0]->attrs->push_back(Attr(sym, v));
return v; return v;
} }
Value & EvalState::getBuiltin(const string & name) 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()); mkString(v, s.c_str());
if (!context.empty()) { if (!context.empty()) {
size_t n = 0; size_t n = 0;
// FIXME: store as Symbols?
v.string.context = (const char * *) v.string.context = (const char * *)
allocBytes((context.size() + 1) * sizeof(char *)); malloc((context.size() + 1) * sizeof(char *));
for (auto & i : context) for (auto & i : context)
v.string.context[n++] = dupString(i.c_str()); v.string.context[n++] = dupString(i.c_str());
v.string.context[n] = 0; v.string.context[n] = 0;
@ -598,50 +545,41 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
} }
std::atomic<uint64_t> nrValuesFreed{0}; Ptr<Value> EvalState::allocValue()
void finalizeValue(void * obj, void * data)
{
nrValuesFreed++;
}
Value * EvalState::allocValue()
{ {
nrValues++; nrValues++;
auto v = (Value *) allocBytes(sizeof(Value)); return gc.alloc<Value>(Value::words());
//GC_register_finalizer_no_order(v, finalizeValue, nullptr, nullptr, nullptr);
return v;
} }
Env & EvalState::allocEnv(size_t size) Ptr<Env> EvalState::allocEnv(size_t size)
{ {
if (size > std::numeric_limits<decltype(Env::size)>::max()) if (size > std::numeric_limits<decltype(Env::size)>::max()) // FIXME
throw Error("environment size %d is too big", size); throw Error("environment size %d is too big", size);
nrEnvs++; nrEnvs++;
nrValuesInEnvs += size; nrValuesInEnvs += size;
Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *)); auto env = gc.alloc<Env>(Env::wordsFor(size), size);
env->size = (decltype(Env::size)) size;
env->type = Env::Plain;
// FIXME
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */ /* 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) void EvalState::mkList(Value & v, size_t size)
{ {
clearValue(v); if (size == 0)
if (size == 1) v.type = tList0;
else if (size == 1)
v.type = tList1; v.type = tList1;
else if (size == 2) else if (size == 2)
v.type = tList2; v.type = tList2;
else { else {
v.type = tListN; v.type = tListN;
v.bigList.size = size; v.bigList = gc.alloc<PtrList<Value>>(
v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0; PtrList<Value>::wordsFor(size), tValueList, size);
} }
nrListElems += size; nrListElems += size;
} }
@ -704,25 +642,25 @@ Value * ExprVar::maybeThunk(EvalState & state, Env & env)
Value * ExprString::maybeThunk(EvalState & state, Env & env) Value * ExprString::maybeThunk(EvalState & state, Env & env)
{ {
nrAvoided++; nrAvoided++;
return &v; return &*v;
} }
Value * ExprInt::maybeThunk(EvalState & state, Env & env) Value * ExprInt::maybeThunk(EvalState & state, Env & env)
{ {
nrAvoided++; nrAvoided++;
return &v; return &*v;
} }
Value * ExprFloat::maybeThunk(EvalState & state, Env & env) Value * ExprFloat::maybeThunk(EvalState & state, Env & env)
{ {
nrAvoided++; nrAvoided++;
return &v; return &*v;
} }
Value * ExprPath::maybeThunk(EvalState & state, Env & env) Value * ExprPath::maybeThunk(EvalState & state, Env & env)
{ {
nrAvoided++; nrAvoided++;
return &v; return &*v;
} }
@ -732,13 +670,13 @@ void EvalState::evalFile(const Path & path_, Value & v)
FileEvalCache::iterator i; FileEvalCache::iterator i;
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) { if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
v = i->second; v = *i->second;
return; return;
} }
Path path2 = resolveExprPath(path); Path path2 = resolveExprPath(path);
if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) { if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) {
v = i->second; v = *i->second;
return; return;
} }
@ -755,14 +693,18 @@ void EvalState::evalFile(const Path & path_, Value & v)
fileParseCache[path2] = e; fileParseCache[path2] = e;
try { 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) { } catch (Error & e) {
addErrorPrefix(e, "while evaluating the file '%1%':\n", path2); addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);
throw; 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) void ExprInt::eval(EvalState & state, Env & env, Value & v)
{ {
v = this->v; v = *this->v;
} }
void ExprFloat::eval(EvalState & state, Env & env, Value & 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) void ExprString::eval(EvalState & state, Env & env, Value & v)
{ {
v = this->v; v = *this->v;
} }
void ExprPath::eval(EvalState & state, Env & env, Value & 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) { if (hasOverrides) {
Value * vOverrides = (*v.attrs)[overrides->second.displ].value; Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
state.forceAttrs(*vOverrides); 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) for (auto & i : *v.attrs)
newBnds->push_back(i); newBnds->push_back(i);
for (auto & i : *vOverrides->attrs) { for (auto & i : *vOverrides->attrs) {
@ -1065,7 +1007,7 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
Value * primOp = &fun; Value * primOp = &fun;
while (primOp->type == tPrimOpApp) { while (primOp->type == tPrimOpApp) {
argsDone++; argsDone++;
primOp = primOp->primOpApp.left; primOp = primOp->app.left;
} }
assert(primOp->type == tPrimOp); assert(primOp->type == tPrimOp);
auto arity = primOp->primOp->arity; auto arity = primOp->primOp->arity;
@ -1078,8 +1020,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
Value * vArgs[arity]; Value * vArgs[arity];
auto n = arity - 1; auto n = arity - 1;
vArgs[n--] = &arg; vArgs[n--] = &arg;
for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left) for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->app.left)
vArgs[n--] = arg->primOpApp.right; vArgs[n--] = arg->app.right;
/* And call the primop. */ /* And call the primop. */
nrPrimOpCalls++; nrPrimOpCalls++;
@ -1089,8 +1031,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
Value * fun2 = allocValue(); Value * fun2 = allocValue();
*fun2 = fun; *fun2 = fun;
v.type = tPrimOpApp; v.type = tPrimOpApp;
v.primOpApp.left = fun2; v.app.left = fun2;
v.primOpApp.right = &arg; v.app.right = &arg;
} }
} }
@ -1379,7 +1321,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
NixFloat nf = 0; NixFloat nf = 0;
bool first = !forceString; bool first = !forceString;
ValueType firstType = tString; Tag firstType = tString;
for (auto & i : *es) { for (auto & i : *es) {
Value vTmp; Value vTmp;
@ -1594,8 +1536,10 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
return coerceToString(pos, *i->value, context, coerceMore, copyToStore); return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
} }
#if 0
if (v.type == tExternal) if (v.type == tExternal)
return v.external->coerceToString(pos, context, coerceMore, copyToStore); return v.external->coerceToString(pos, context, coerceMore, copyToStore);
#endif
if (coerceMore) { if (coerceMore) {
@ -1692,6 +1636,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tNull: case tNull:
return true; return true;
case tList0:
case tList1: case tList1:
case tList2: case tList2:
case tListN: case tListN:
@ -1727,8 +1672,10 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tPrimOpApp: case tPrimOpApp:
return false; return false;
#if 0
case tExternal: case tExternal:
return *v1.external == *v2.external; return *v1.external == *v2.external;
#endif
case tFloat: case tFloat:
return v1.fpoint == v2.fpoint; return v1.fpoint == v2.fpoint;
@ -1751,10 +1698,6 @@ void EvalState::printStats()
uint64_t bValues = nrValues * sizeof(Value); uint64_t bValues = nrValues * sizeof(Value);
uint64_t bAttrsets = nrAttrsets * sizeof(Bindings) + nrAttrsInAttrsets * sizeof(Attr); 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) { if (showStats) {
auto outPath = getEnv("NIX_SHOW_STATS_PATH","-"); auto outPath = getEnv("NIX_SHOW_STATS_PATH","-");
std::fstream fs; std::fstream fs;
@ -1804,13 +1747,6 @@ void EvalState::printStats()
topObj.attr("nrLookups", nrLookups); topObj.attr("nrLookups", nrLookups);
topObj.attr("nrPrimOpCalls", nrPrimOpCalls); topObj.attr("nrPrimOpCalls", nrPrimOpCalls);
topObj.attr("nrFunctionCalls", nrFunctionCalls); topObj.attr("nrFunctionCalls", nrFunctionCalls);
#if HAVE_BOEHMGC
{
auto gc = topObj.object("gc");
gc.attr("heapSize", heapSize);
gc.attr("totalBytes", totalBytes);
}
#endif
if (countCalls) { if (countCalls) {
{ {
@ -1893,6 +1829,7 @@ size_t valueSize(Value & v)
sz += doValue(*i.value); sz += doValue(*i.value);
} }
break; break;
case tList0:
case tList1: case tList1:
case tList2: case tList2:
case tListN: case tListN:
@ -1914,14 +1851,16 @@ size_t valueSize(Value & v)
sz += doEnv(*v.lambda.env); sz += doEnv(*v.lambda.env);
break; break;
case tPrimOpApp: case tPrimOpApp:
sz += doValue(*v.primOpApp.left); sz += doValue(*v.app.left);
sz += doValue(*v.primOpApp.right); sz += doValue(*v.app.right);
break; break;
#if 0
case tExternal: case tExternal:
if (seen.find(v.external) != seen.end()) break; if (seen.find(v.external) != seen.end()) break;
seen.insert(v.external); seen.insert(v.external);
sz += v.external->valueSize(seen); sz += v.external->valueSize(seen);
break; break;
#endif
default: 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 string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
{ {
throw TypeError(format("cannot coerce %1% to a string, at %2%") % 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) { std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) {
return v.print(str); return v.print(str);
} }
#endif
EvalSettings evalSettings; EvalSettings evalSettings;

View file

@ -6,6 +6,7 @@
#include "symbol-table.hh" #include "symbol-table.hh"
#include "hash.hh" #include "hash.hh"
#include "config.hh" #include "config.hh"
#include "gc.hh"
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
@ -32,13 +33,26 @@ struct PrimOp
}; };
struct Env struct Env : Object
{ {
Env * up; Env * up;
// FIXME: use misc field
unsigned short size; // used by valueSize unsigned short size; // used by valueSize
unsigned short prevWith:14; // nr of levels up to next `with' environment 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]; 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. */ mode. */
std::optional<PathSet> allowedPaths; std::optional<PathSet> allowedPaths;
Value vEmptySet; Ptr<Bindings> emptyBindings;
const ref<Store> store; const ref<Store> store;
@ -91,19 +105,11 @@ private:
SrcToStore srcToStore; SrcToStore srcToStore;
/* A cache from path names to parse trees. */ /* A cache from path names to parse trees. */
#if HAVE_BOEHMGC
typedef std::map<Path, Expr *, std::less<Path>, traceable_allocator<std::pair<const Path, Expr *> > > FileParseCache;
#else
typedef std::map<Path, Expr *> FileParseCache; typedef std::map<Path, Expr *> FileParseCache;
#endif
FileParseCache fileParseCache; FileParseCache fileParseCache;
/* A cache from path names to values. */ /* A cache from path names to values. */
#if HAVE_BOEHMGC typedef std::map<Path, Ptr<Value>> FileEvalCache;
typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value> > > FileEvalCache;
#else
typedef std::map<Path, Value> FileEvalCache;
#endif
FileEvalCache fileEvalCache; FileEvalCache fileEvalCache;
SearchPath searchPath; SearchPath searchPath;
@ -213,7 +219,7 @@ public:
/* The base environment, containing the builtin functions and /* The base environment, containing the builtin functions and
values. */ values. */
Env & baseEnv; Ptr<Env> baseEnv;
/* The same, but used during parsing to resolve variables. */ /* The same, but used during parsing to resolve variables. */
StaticEnv staticBaseEnv; // !!! should be private StaticEnv staticBaseEnv; // !!! should be private
@ -260,13 +266,13 @@ public:
void autoCallFunction(Bindings & args, Value & fun, Value & res); void autoCallFunction(Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */ /* Allocation primitives. */
Value * allocValue(); Ptr<Value> allocValue();
Env & allocEnv(size_t size); Ptr<Env> allocEnv(size_t size);
// Note: the resulting Value is only reachable as long as vAttrs
// is reachable.
Value * allocAttr(Value & vAttrs, const Symbol & name); Value * allocAttr(Value & vAttrs, const Symbol & name);
Bindings * allocBindings(size_t capacity);
void mkList(Value & v, size_t length); void mkList(Value & v, size_t length);
void mkAttrs(Value & v, size_t capacity); void mkAttrs(Value & v, size_t capacity);
void mkThunk_(Value & v, Expr * expr); void mkThunk_(Value & v, Expr * expr);

287
src/libexpr/gc.cc Normal file
View file

@ -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<Object> *) malloc(sizeof(Ptr<Object>));
backSentinel = (Ptr<Object> *) malloc(sizeof(Ptr<Object>));
frontSentinel->prev = nullptr;
frontSentinel->next = backSentinel;
backSentinel->prev = frontSentinel;
backSentinel->next = nullptr;
}
GC::~GC()
{
size_t n = 0;
for (Ptr<Object> * 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<Object *> stack;
for (Ptr<Object> * 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<Object> *) 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<Size, Size> 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<Value> *) 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)
{
}
}

346
src/libexpr/gc.hh Normal file
View file

@ -0,0 +1,346 @@
#pragma once
#include "logging.hh"
#include <stack>
#include <limits>
#include <cassert>
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<class T>
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<class T>
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<Ptr &>(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<Object> * frontSentinel;
Ptr<Object> * 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<Size, Size> freeUnmarked();
};
// Note: arenas are sorted by ascending amount of free space.
struct ArenaList
{
Size nextSize;
std::vector<Arena> arenas;
ArenaList();
};
std::array<ArenaList, 2> arenaLists;
public:
GC();
~GC();
template<typename T, typename... Args>
Ptr<T> 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<T>((Ptr<T> *) 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;
}

View file

@ -248,7 +248,7 @@ void DrvInfo::setMeta(const string & name, Value * v)
{ {
getMeta(); getMeta();
Bindings * old = meta; 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); Symbol sym = state->symbols.create(name);
if (old) if (old)
for (auto i : *old) for (auto i : *old)

View file

@ -69,11 +69,7 @@ public:
}; };
#if HAVE_BOEHMGC typedef std::list<DrvInfo> DrvInfos;
typedef list<DrvInfo, traceable_allocator<DrvInfo> > DrvInfos;
#else
typedef list<DrvInfo> DrvInfos;
#endif
/* If value `v' denotes a derivation, return a DrvInfo object /* If value `v' denotes a derivation, return a DrvInfo object

View file

@ -52,9 +52,9 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
skipWhitespace(s); skipWhitespace(s);
while (1) { while (1) {
if (values.empty() && *s == ']') break; if (values.empty() && *s == ']') break;
Value * v2 = state.allocValue(); Ptr<Value> v2 = state.allocValue();
parseJSON(state, s, *v2); parseJSON(state, s, *v2);
values.push_back(v2); values.push_back(std::move(v2));
skipWhitespace(s); skipWhitespace(s);
if (*s == ']') break; if (*s == ']') break;
if (*s != ',') throw JSONParseError("expected ',' or ']' after JSON array element"); 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); skipWhitespace(s);
if (*s != ':') throw JSONParseError("expected ':' in JSON object"); if (*s != ':') throw JSONParseError("expected ':' in JSON object");
s++; s++;
Value * v2 = state.allocValue(); Ptr<Value> v2 = state.allocValue();
parseJSON(state, s, *v2); parseJSON(state, s, *v2);
attrs[state.symbols.create(name)] = v2; attrs.emplace(state.symbols.create(name), std::move(v2));
skipWhitespace(s); skipWhitespace(s);
if (*s == '}') break; if (*s == '}') break;
if (*s != ',') throw JSONParseError("expected ',' or '}' after JSON member"); 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 == '.' ) { else if (isdigit(*s) || *s == '-' || *s == '.' ) {
// Buffer into a string first, then use built-in C++ conversions // Buffer into a string first, then use built-in C++ conversions
std::string tmp_number; std::string tmp_number;
ValueType number_type = tInt; Tag number_type = tInt;
while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') { while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') {
if (*s == '.' || *s == 'e' || *s == 'E') if (*s == '.' || *s == 'e' || *s == 'E')

View file

@ -13,11 +13,6 @@ ifneq ($(OS), FreeBSD)
libexpr_LDFLAGS += -ldl libexpr_LDFLAGS += -ldl
endif 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 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 $(d)/parser-tab.cc $(d)/parser-tab.hh: $(d)/parser.y

View file

@ -92,8 +92,11 @@ std::ostream & operator << (std::ostream & str, const Expr & e);
struct ExprInt : Expr struct ExprInt : Expr
{ {
NixInt n; NixInt n;
Value v; Ptr<Value> v;
ExprInt(NixInt n) : n(n) { mkInt(v, n); }; ExprInt(NixInt n) : n(n) {
v = gc.alloc<Value>(Value::words());
mkInt(v, n);
};
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
@ -101,8 +104,11 @@ struct ExprInt : Expr
struct ExprFloat : Expr struct ExprFloat : Expr
{ {
NixFloat nf; NixFloat nf;
Value v; Ptr<Value> v;
ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); }; ExprFloat(NixFloat nf) : nf(nf) {
v = gc.alloc<Value>(Value::words());
mkFloat(v, nf);
};
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
@ -110,8 +116,11 @@ struct ExprFloat : Expr
struct ExprString : Expr struct ExprString : Expr
{ {
Symbol s; Symbol s;
Value v; Ptr<Value> v;
ExprString(const Symbol & s) : s(s) { mkString(v, s); }; ExprString(const Symbol & s) : s(s) {
v = gc.alloc<Value>(Value::words());
mkString(v, s);
};
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
@ -126,8 +135,11 @@ struct ExprIndStr : Expr
struct ExprPath : Expr struct ExprPath : Expr
{ {
string s; string s;
Value v; Ptr<Value> v;
ExprPath(const string & s) : s(s) { mkPathNoCopy(v, this->s.c_str()); }; ExprPath(const string & s) : s(s) {
v = gc.alloc<Value>(Value::words());
mkPathNoCopy(v, this->s.c_str());
};
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };

View file

@ -130,8 +130,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
if (args[0]->attrs->empty()) if (args[0]->attrs->empty())
state.evalFile(realPath, v); state.evalFile(realPath, v);
else { else {
Env * env = &state.allocEnv(args[0]->attrs->size()); auto env = state.allocEnv(args[0]->attrs->size());
env->up = &state.baseEnv; env->up = state.baseEnv;
StaticEnv staticEnv(false, &state.staticBaseEnv); 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 tPath: t = "path"; break;
case tNull: t = "null"; break; case tNull: t = "null"; break;
case tAttrs: t = "set"; 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 tLambda:
case tPrimOp: case tPrimOp:
case tPrimOpApp: case tPrimOpApp:
t = "lambda"; t = "lambda";
break; break;
#if 0
case tExternal: case tExternal:
t = args[0]->external->typeOf(); t = args[0]->external->typeOf();
break; break;
#endif
case tFloat: t = "float"; break; case tFloat: t = "float"; break;
default: abort(); default: abort();
} }
@ -348,15 +350,12 @@ struct CompareValues
}; };
#if HAVE_BOEHMGC typedef list<Ptr<Value>> ValueList;
typedef list<Value *, gc_allocator<Value *> > ValueList;
#else
typedef list<Value *> ValueList;
#endif
static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
#if 0
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
/* Get the start set. */ /* Get the start set. */
@ -417,6 +416,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
unsigned int n = 0; unsigned int n = 0;
for (auto & i : res) for (auto & i : res)
v.listElems()[n++] = i; 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(); auto len = args[1]->listSize();
ValueVector right, wrong; // Note: these Values are reachable via args[0].
std::vector<Value *> right, wrong;
for (unsigned int n = 0; n < len; ++n) { for (unsigned int n = 0; n < len; ++n) {
auto vElem = args[1]->listElems()[n]; auto vElem = args[1]->listElems()[n];
@ -2107,10 +2109,10 @@ RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun)
void EvalState::createBaseEnv() void EvalState::createBaseEnv()
{ {
baseEnv.up = 0; baseEnv->up = 0;
/* Add global constants such as `true' to the base environment. */ /* Add global constants such as `true' to the base environment. */
Value v; auto v = allocValue();
/* `builtins' must be first! */ /* `builtins' must be first! */
mkAttrs(v, 128); mkAttrs(v, 128);
@ -2287,7 +2289,7 @@ void EvalState::createBaseEnv()
mkList(v, searchPath.size()); mkList(v, searchPath.size());
int n = 0; int n = 0;
for (auto & i : searchPath) { for (auto & i : searchPath) {
v2 = v.listElems()[n++] = allocValue(); v2 = v->listElems()[n++] = allocValue();
mkAttrs(*v2, 2); mkAttrs(*v2, 2);
mkString(*allocAttr(*v2, symbols.create("path")), i.second); mkString(*allocAttr(*v2, symbols.create("path")), i.second);
mkString(*allocAttr(*v2, symbols.create("prefix")), i.first); 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, /* Now that we've added all primops, sort the `builtins' set,
because attribute lookups expect it to be sorted. */ because attribute lookups expect it to be sorted. */
baseEnv.values[0]->attrs->sort(); baseEnv->values[0]->attrs->sort();
} }

View file

@ -56,7 +56,7 @@ void printValueAsJSON(EvalState & state, bool strict,
break; break;
} }
case tList1: case tList2: case tListN: { case tList0: case tList1: case tList2: case tListN: {
auto list(out.list()); auto list(out.list());
for (unsigned int n = 0; n < v.listSize(); ++n) { for (unsigned int n = 0; n < v.listSize(); ++n) {
auto placeholder(list.placeholder()); auto placeholder(list.placeholder());
@ -65,9 +65,11 @@ void printValueAsJSON(EvalState & state, bool strict,
break; break;
} }
#if 0
case tExternal: case tExternal:
v.external->printValueAsJSON(state, strict, out, context); v.external->printValueAsJSON(state, strict, out, context);
break; break;
#endif
case tFloat: case tFloat:
out.write(v.fpoint); out.write(v.fpoint);
@ -85,11 +87,13 @@ void printValueAsJSON(EvalState & state, bool strict,
printValueAsJSON(state, strict, v, out, context); printValueAsJSON(state, strict, v, out, context);
} }
#if 0
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
JSONPlaceholder & out, PathSet & context) const JSONPlaceholder & out, PathSet & context) const
{ {
throw TypeError(format("cannot convert %1% to JSON") % showType()); throw TypeError(format("cannot convert %1% to JSON") % showType());
} }
#endif
} }

View file

@ -119,7 +119,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
case tList1: case tList2: case tListN: { case tList0: case tList1: case tList2: case tListN: {
XMLOpenElement _(doc, "list"); XMLOpenElement _(doc, "list");
for (unsigned int n = 0; n < v.listSize(); ++n) for (unsigned int n = 0; n < v.listSize(); ++n)
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen); printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
@ -144,9 +144,11 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
} }
#if 0
case tExternal: case tExternal:
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen); v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
break; break;
#endif
case tFloat: case tFloat:
doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str())); 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, void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const
{ {
doc.writeEmptyElement("unevaluated"); doc.writeEmptyElement("unevaluated");
} }
#endif
void printValueAsXML(EvalState & state, bool strict, bool location, void printValueAsXML(EvalState & state, bool strict, bool location,

View file

@ -1,35 +1,10 @@
#pragma once #pragma once
#include "symbol-table.hh" #include "symbol-table.hh"
#include "gc.hh"
#if HAVE_BOEHMGC
#include <gc/gc_allocator.h>
#endif
namespace nix { 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; class Bindings;
struct Env; struct Env;
struct Expr; struct Expr;
@ -46,6 +21,7 @@ class JSONPlaceholder;
typedef int64_t NixInt; typedef int64_t NixInt;
typedef double NixFloat; typedef double NixFloat;
#if 0
/* External values must descend from ExternalValueBase, so that /* External values must descend from ExternalValueBase, so that
* type-agnostic nix functions (e.g. showType) can be implemented * 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); std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
#endif
struct Value struct Value : Object
{ {
ValueType type;
union union
{ {
NixInt integer; NixInt integer;
@ -127,10 +103,7 @@ struct Value
const char * path; const char * path;
Bindings * attrs; Bindings * attrs;
struct { PtrList<Value> * bigList;
size_t size;
Value * * elems;
} bigList;
Value * smallList[2]; Value * smallList[2];
struct { struct {
Env * env; Env * env;
@ -144,46 +117,38 @@ struct Value
ExprLambda * fun; ExprLambda * fun;
} lambda; } lambda;
PrimOp * primOp; PrimOp * primOp;
struct { //ExternalValueBase * external;
Value * left, * right;
} primOpApp;
ExternalValueBase * external;
NixFloat fpoint; NixFloat fpoint;
}; };
Value() : Object(tBlackhole, 0) { }
bool isList() const bool isList() const
{ {
return type == tList1 || type == tList2 || type == tListN; return type >= tList0 && type <= tListN;
} }
Value * * listElems() 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 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 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) static inline void mkInt(Value & v, NixInt n)
{ {
clearValue(v);
v.type = tInt; v.type = tInt;
v.integer = n; v.integer = n;
} }
@ -191,7 +156,6 @@ static inline void mkInt(Value & v, NixInt n)
static inline void mkFloat(Value & v, NixFloat n) static inline void mkFloat(Value & v, NixFloat n)
{ {
clearValue(v);
v.type = tFloat; v.type = tFloat;
v.fpoint = n; v.fpoint = n;
} }
@ -199,7 +163,6 @@ static inline void mkFloat(Value & v, NixFloat n)
static inline void mkBool(Value & v, bool b) static inline void mkBool(Value & v, bool b)
{ {
clearValue(v);
v.type = tBool; v.type = tBool;
v.boolean = b; v.boolean = b;
} }
@ -207,7 +170,6 @@ static inline void mkBool(Value & v, bool b)
static inline void mkNull(Value & v) static inline void mkNull(Value & v)
{ {
clearValue(v);
v.type = tNull; v.type = tNull;
} }
@ -247,7 +209,6 @@ void mkString(Value & v, const char * s);
static inline void mkPathNoCopy(Value & v, const char * s) static inline void mkPathNoCopy(Value & v, const char * s)
{ {
clearValue(v);
v.type = tPath; v.type = tPath;
v.path = s; v.path = s;
} }
@ -262,13 +223,8 @@ void mkPath(Value & v, const char * s);
size_t valueSize(Value & v); size_t valueSize(Value & v);
#if HAVE_BOEHMGC typedef std::vector<Ptr<Value>> ValueVector; // FIXME: make more efficient
typedef std::vector<Value *, gc_allocator<Value *> > ValueVector; typedef std::map<Symbol, Ptr<Value>> ValueMap; // FIXME: use Bindings?
typedef std::map<Symbol, Value *, std::less<Symbol>, gc_allocator<std::pair<const Symbol, Value *> > > ValueMap;
#else
typedef std::vector<Value *> ValueVector;
typedef std::map<Symbol, Value *> ValueMap;
#endif
} }

View file

@ -244,9 +244,6 @@ void printVersion(const string & programName)
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl; std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
if (verbosity > lvlInfo) { if (verbosity > lvlInfo) {
Strings cfg; Strings cfg;
#if HAVE_BOEHMGC
cfg.push_back("gc");
#endif
#if HAVE_SODIUM #if HAVE_SODIUM
cfg.push_back("signed-caches"); cfg.push_back("signed-caches");
#endif #endif

View file

@ -19,7 +19,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
if (pathExists(manifestFile)) { if (pathExists(manifestFile)) {
Value v; Value v;
state.evalFile(manifestFile, v); state.evalFile(manifestFile, v);
Bindings & bindings(*state.allocBindings(0)); auto bindings = Bindings::allocBindings(0);
getDerivations(state, v, "", bindings, elems, false); getDerivations(state, v, "", bindings, elems, false);
} }
return elems; return elems;
@ -42,7 +42,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
/* Construct the whole top level derivation. */ /* Construct the whole top level derivation. */
PathSet references; PathSet references;
Value manifest; auto manifest = state.allocValue();
state.mkList(manifest, elems.size()); state.mkList(manifest, elems.size());
unsigned int n = 0; unsigned int n = 0;
for (auto & i : elems) { for (auto & i : elems) {
@ -51,8 +51,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
as the meta attributes. */ as the meta attributes. */
Path drvPath = keepDerivations ? i.queryDrvPath() : ""; Path drvPath = keepDerivations ? i.queryDrvPath() : "";
Value & v(*state.allocValue()); auto v = state.allocValue();
manifest.listElems()[n++] = &v; manifest->listElems()[n++] = (Value *) v;
state.mkAttrs(v, 16); state.mkAttrs(v, 16);
mkString(*state.allocAttr(v, state.sType), "derivation"); mkString(*state.allocAttr(v, state.sType), "derivation");
@ -70,7 +70,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.mkList(vOutputs, outputs.size()); state.mkList(vOutputs, outputs.size());
unsigned int m = 0; unsigned int m = 0;
for (auto & j : outputs) { 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)); Value & vOutputs = *state.allocAttr(v, state.symbols.create(j.first));
state.mkAttrs(vOutputs, 2); state.mkAttrs(vOutputs, 2);
mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second); 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->push_back(Attr(state.symbols.create(j), v));
} }
vMeta.attrs->sort(); vMeta.attrs->sort();
v.attrs->sort(); v->attrs->sort();
if (drvPath != "") references.insert(drvPath); 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 the store; we need it for future modifications of the
environment. */ environment. */
Path manifestFile = state.store->addTextToStore("env-manifest.nix", Path manifestFile = state.store->addTextToStore("env-manifest.nix",
(format("%1%") % manifest).str(), references); fmt("%1%", (Value &) manifest), references);
/* Get the environment builder expression. */ /* Get the environment builder expression. */
Value envBuilder; Value envBuilder;
@ -114,7 +114,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.mkAttrs(args, 3); state.mkAttrs(args, 3);
mkString(*state.allocAttr(args, state.symbols.create("manifest")), mkString(*state.allocAttr(args, state.symbols.create("manifest")),
manifestFile, {manifestFile}); 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(); args.attrs->sort();
mkApp(topLevel, envBuilder, args); mkApp(topLevel, envBuilder, args);

View file

@ -38,8 +38,8 @@ struct CmdEdit : InstallableCommand
Value * v2; Value * v2;
try { try {
auto dummyArgs = state->allocBindings(0); auto dummyArgs = Bindings::allocBindings(0);
v2 = findAlongAttrPath(*state, "meta.position", *dummyArgs, *v); v2 = findAlongAttrPath(*state, "meta.position", dummyArgs, *v);
} catch (Error &) { } catch (Error &) {
throw Error("package '%s' has no source location information", installable->what()); throw Error("package '%s' has no source location information", installable->what());
} }

View file

@ -44,7 +44,7 @@ struct NixRepl
const static int envSize = 32768; const static int envSize = 32768;
StaticEnv staticEnv; StaticEnv staticEnv;
Env * env; Ptr<Env> env;
int displ; int displ;
StringSet varNames; StringSet varNames;
@ -506,8 +506,8 @@ void NixRepl::loadFile(const Path & path)
void NixRepl::initEnv() void NixRepl::initEnv()
{ {
env = &state.allocEnv(envSize); env = state.allocEnv(envSize);
env->up = &state.baseEnv; env->up = state.baseEnv;
displ = 0; displ = 0;
staticEnv.vars.clear(); staticEnv.vars.clear();
@ -665,6 +665,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
break; break;
} }
case tList0:
case tList1: case tList1:
case tList2: case tList2:
case tListN: case tListN:

View file

@ -129,7 +129,7 @@ struct CmdSearch : SourceExprCommand, MixJSON
if (v->type == tLambda && toplevel) { if (v->type == tLambda && toplevel) {
Value * v2 = state->allocValue(); Value * v2 = state->allocValue();
state->autoCallFunction(*state->allocBindings(1), *v, *v2); state->autoCallFunction(*Bindings::allocBindings(1), *v, *v2);
v = v2; v = v2;
state->forceValue(*v); state->forceValue(*v);
} }

View file

@ -150,7 +150,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
auto state = std::make_unique<EvalState>(Strings(), store); auto state = std::make_unique<EvalState>(Strings(), store);
auto v = state->allocValue(); auto v = state->allocValue();
state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v); 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); auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v);
return state->forceString(*v2); return state->forceString(*v2);