Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/dmd/cli.d
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ struct Usage
Option("c",
"do not link"
),
Option("cache=mmap",
"use persistent cache for CTFE evaluations"
),
Option("color",
"turn colored console output on"
),
Expand Down
254 changes: 253 additions & 1 deletion src/dmd/dinterpret.d
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,23 @@

module dmd.dinterpret;

import core.stdc.stdlib;
import core.stdc.stdio;
import core.stdc.string;
import core.stdc.time;
import dmd.apply;
import dmd.arraytypes;
import dmd.astcodegen;
import dmd.attrib;
import dmd.builtin;
import dmd.constfold;
import dmd.ctfeexpr;
import dmd.dcache;
import dmd.dclass;
import dmd.declaration;
import dmd.dimport;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dsymbolsem;
Expand All @@ -30,14 +37,18 @@ import dmd.errors;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
import dmd.hdrgen;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.init;
import dmd.initsem;
import dmd.mtype;
import dmd.parse;
import dmd.root.array;
import dmd.root.outbuffer;
import dmd.root.rootobject;
import dmd.root.sha;
import dmd.statement;
import dmd.tokens;
import dmd.utf;
Expand Down Expand Up @@ -720,6 +731,161 @@ extern (C++) Expression ctfeInterpretForPragmaMsg(Expression e)
return e;
}

ubyte[20] ctfeHashingSeed()
{
static ubyte[20] hash;
static bool inited;

if (inited) return hash;
else
{
SHA1 hasher;
hasher.start();
// predefined versions indicate target, bitness and asserts
if (global.versionids)
{
foreach (id; *global.versionids)
{
const(char)* s = id.toChars();
hasher.put(cast(ubyte[])s[0..strlen(s)]);
}
}
if (global.debugids)
{
foreach (id; *global.debugids)
{
const(char)* s = id.toChars();
hasher.put(cast(ubyte[])s[0 .. strlen(s)]);
}
}
hash = hasher.finish();
inited = true;
return hash;
}
}

ubyte[20] generateCtfeCacheKey(FuncDeclaration fd, ref Expressions eargs)
{
SHA1 hasher;
hasher.start();
auto signature = fd.toFullSignature();
// Add in a hash of compilation parameters
hasher.put(ctfeHashingSeed);
debug(DCACHE) printf("Func's parent %s \n", fd.parent.toChars());
// Include modules of type arguments
if (TemplateInstance ti = fd.parent.isTemplateInstance())
{
if (ti.tiargs)
{
foreach (arg; *ti.tiargs)
{
Dsymbol sym = getDsymbol(arg);
if (sym)
{
/*if (ScopeDsymbol sd = sym.isScopeDsymbol())
{
foreach(sc; *sd.importedScopes)
{
if (Module md = sc.isModule())
{
debug(DCACHE) auto str = toHexString(md.signature[]);
debug(DCACHE) printf("\tmod %s sig: %.*s\n",
md.toChars(), str.length, str.ptr);
hasher.put(md.signature[]);
}
}
}*/
Dsymbol last;
while (sym)
{
last = sym;
sym = sym.parent;
}
if (Module md = last.isModule())
{
foreach (imp; fd._scope._module.aimports)
{
hasher.put(imp.signature[]);
}
debug(DCACHE) auto str = toHexString(md.signature[]);
debug(DCACHE) printf("\tmod %s sig: %.*s\n",
md.toChars(), str.length, str.ptr);
hasher.put(md.signature[]);
}
}
}
}
}
debug(DCACHE) printf("Imports from function's module:\n");
foreach (imp; fd._scope._module.aimports)
{
hasher.put(imp.signature[]);
auto str = toHexString(imp.signature[]);
debug(DCACHE) printf("\tmod %s sig: %.*s\n", imp.toChars(), str.length, str.ptr);
}
hasher.put(fd._scope._module.signature);
hasher.put(0x0); // separator
hasher.put(cast(ubyte[])signature[0 .. strlen(signature)]);
foreach(i, earg; eargs)
{
hasher.put(0x0); // separator
auto argument = earg.toChars();
hasher.put(cast(ubyte[])argument[0 .. strlen(argument)]);
}
return hasher.finish();
}

extern(C++) class ExtractSymbols : StoppableVisitor {
Scope* _sc;
Dsymbol[] symbols;

this(Scope* sc)
{
_sc = sc;
}

override void visit(Expression e)
{
if (!e.type.isscalar())
{
Dsymbol sym = e.type.toDsymbol(_sc);
if (sym)
symbols ~= sym;
}
}
alias visit = Visitor.visit;
}

void writeModuleName(Dsymbol sym, ref OutBuffer dest)
{
const(char)*[] parts;
sym = sym.getModule();
while (sym)
{
parts ~= sym.toChars();
sym = sym.parent;
}
for (size_t i = 0; i < parts.length; i++)
{
if (i != 0)
dest.writestring(cast(char*)".");
dest.writestring(parts[$ - i - 1]);
}
}

void uniqueInPlace(T)(ref T[] args)
{
size_t i = 0;
for (size_t j = 0; j < args.length; j++)
{
if (args[i] != args[j])
{
args[i++] = args[j];
}
}
args.length = i;
}

/*************************************
* Attempt to interpret a function given the arguments.
* Input:
Expand Down Expand Up @@ -836,6 +1002,51 @@ private Expression interpret(FuncDeclaration fd, InterState* istate, Expressions
}
eargs[i] = earg;
}

// Skip generated symbols and only permit caching of top-level CTFE calls
bool cacheable = (istate == null || istate.caller == null)
&& strncmp(fd.ident.toChars(), "__", 2) != 0;
clock_t ctfeStart = clock();
ubyte[20] cacheKey = void;
if (global.params.cache && cacheable)
{
cacheKey = generateCtfeCacheKey(fd, eargs);
auto value = dcache.get(cacheKey[]);
if (value != null)
{
clock_t startMixin = clock();
debug (DCACHE)
{
printf("interpreting global %s: %s\n", fd.loc.toChars(), fd.toFullSignature());
foreach (i, earg; eargs[])
{
printf("\targ %d : %s\n", i, earg.toChars());
}
printf("\tFound in cache\n");
printf("BODY %*s\n", value.length, value.ptr);
}
scope p = new Parser!ASTCodegen(fd.loc, fd._scope._module, cast(string)value ~ "\0", false);
p.nextToken();
Expression e = p.parseExpression();
if (p.errors)
{
return new ErrorExp();
}
if (p.token.value != TOK.endOfFile)
{
e.error("incomplete mixin expression (%s)", e.toChars());
return new ErrorExp();
}
cachedSemantics = true;
e = expressionSemantic(e, fd._scope);
cachedSemantics = false;
clock_t endMixin = clock();
double delta = 1.0*(endMixin - startMixin) / CLOCKS_PER_SEC;
debug(DCACHE) printf("\tSuccessfully mixed in!\n\n");
version(DCACHE_STATS) printf("Mixed in %s from cache in %lf\n", fd.toFullSignature(), delta);
return e;
}
}

// Now that we've evaluated all the arguments, we can start the frame
// (this is the moment when the 'call' actually takes place).
Expand Down Expand Up @@ -978,7 +1189,48 @@ private Expression interpret(FuncDeclaration fd, InterState* istate, Expressions
(cast(ThrownExceptionExp)e).generateUncaughtError();
e = CTFEExp.cantexp;
}

if (global.params.cache && cacheable)
{
clock_t ctfeEnd = clock();
double delta = 1.0*(ctfeEnd - ctfeStart) / CLOCKS_PER_SEC;
if (delta > 0.0)
{
debug(DCACHE)
{
printf("interpreting global %s: %s\n", fd.loc.toChars(), fd.toFullSignature());
foreach (i, earg; eargs[])
{
printf("\targ %d : %s\n", i, earg.toChars());
}
}
OutBuffer buf;
scope extractor = new ExtractSymbols(fd._scope);
walkPostorder(e, extractor);
extern(C) static int comp(scope const(void*) *a , scope const(void*)* b)
{
return a > b ? 1 : (a == b ? 0 : -1);
}
qsort(extractor.symbols.ptr, extractor.symbols.length, Dsymbol.sizeof, cast(_compare_fp_t)&comp);
uniqueInPlace(extractor.symbols);
buf.writestring("(){\n");
foreach (t; extractor.symbols)
{
buf.writestring("import ");
writeModuleName(t, buf);
buf.writestring(" : ");
buf.writestring(t.toChars());
buf.writestring(";\n");
}
buf.writestring("return ");
buf.writestring(e.toChars());
buf.writestring(";\n");
buf.writestring("}()\n");
auto value = buf.extractString();
dcache.put(cacheKey[], cast(ubyte[])value[0 .. strlen(value)]);
debug(DCACHE) printf("\treturns (%lf) CACHED %s\n", delta, value);
version(DCACHE_STATS) printf("Recomputed %s in %lf\n", fd.toFullSignature(), delta);
}
}
return e;
}

Expand Down
2 changes: 2 additions & 0 deletions src/dmd/dmodule.d
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,8 @@ extern (C++) final class Module : Package

size_t nameoffset; // offset of module name from start of ModuleInfo
size_t namelen; // length of module name in characters

ubyte[20] signature; // A signature of contents, currently SHA1

extern (D) this(const(char)* filename, Identifier ident, int doDocComment, int doHdrGen)
{
Expand Down
4 changes: 3 additions & 1 deletion src/dmd/dscope.d
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import core.stdc.stdio;
import core.stdc.string;
import dmd.aggregate;
import dmd.attrib;
import dmd.dcache;
import dmd.dclass;
import dmd.declaration;
import dmd.dmodule;
import dmd.doc;
import dmd.dimport;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
Expand Down Expand Up @@ -554,7 +556,7 @@ struct Scope
* checked by the compiler remain usable. Once the deprecation is over,
* this should be moved to search_correct instead.
*/
if (!s && !(flags & IgnoreSymbolVisibility))
if (!cachedSemantics && !s && !(flags & IgnoreSymbolVisibility))
{
s = searchScopes(flags | SearchLocalsOnly | IgnoreSymbolVisibility);
if (!s)
Expand Down
3 changes: 2 additions & 1 deletion src/dmd/dsymbol.d
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import dmd.aliasthis;
import dmd.arraytypes;
import dmd.attrib;
import dmd.gluelayer;
import dmd.dcache;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
Expand Down Expand Up @@ -1380,7 +1381,7 @@ public:
AliasDeclaration ad = void;
// accessing private selective and renamed imports is
// deprecated by restricting the symbol visibility
if (s.isImport() || (ad = s.isAliasDeclaration()) !is null && ad._import !is null)
if (cachedSemantics || s.isImport() || (ad = s.isAliasDeclaration()) !is null && ad._import !is null)
{}
else
error(loc, "%s `%s` is `private`", s.kind(), s.toPrettyChars());
Expand Down
3 changes: 3 additions & 0 deletions src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import dmd.arraytypes;
import dmd.attrib;
import dmd.astcodegen;
import dmd.canthrow;
import dmd.dcache;
import dmd.dscope;
import dmd.dsymbol;
import dmd.declaration;
Expand Down Expand Up @@ -2962,6 +2963,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (!sd.ctor)
sd.ctor = sd.searchCtor();

// A cached value is a literal
if (cachedSemantics) goto Lx;
// First look for constructor
if (exp.e1.op == TOK.type && sd.ctor)
{
Expand Down
1 change: 1 addition & 0 deletions src/dmd/globals.d
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ struct Param
* used to hide a feature that will have to go through deprecate-then-error
* before becoming default.
*/
bool cache; // use persistent caching of CTFE (DCache)

bool showGaggedErrors; // print gagged errors anyway
bool manual; // open browser on compiler manual
Expand Down
Loading