Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b221486
Prepare separate main branch for performance improvements
sungshik Jul 29, 2026
6aafa1d
Add conversion function for field `definesMap` of `TModel` to make it…
sungshik Jul 29, 2026
1ce56e7
Add cache for `lookupWide`
sungshik Jul 29, 2026
32dfbf0
Update `lookupPathsWide` to avoid solve loop
sungshik Jul 29, 2026
cdf4f1e
Add function, including caching of its results, to get the target of …
sungshik Jul 29, 2026
325e17e
Add flag to configure if messages in `TModel`s should be sorted
sungshik Jul 29, 2026
01a6923
Add `filterUnused` as a faster alternative to `reportUnused`
sungshik Aug 4, 2026
26bf10f
Update `resolvePaths` by separating initialization and updating of `p…
sungshik Aug 5, 2026
f54b384
Update check for illegal overloading of unused definitions to avoid s…
sungshik Aug 7, 2026
97568cb
Add `newDefInfo` function to make creation of `defInfo` values faster…
sungshik Aug 10, 2026
06a5cd9
Update default for sorting messages to false
sungshik Aug 12, 2026
0f42f39
Improve comment
sungshik Aug 14, 2026
ea58ab5
Rename auxiliary functions
sungshik Aug 14, 2026
edbf802
Merge pull request #55 from usethesource/performance-improvements/fas…
sungshik Aug 14, 2026
3e4daa2
Merge pull request #56 from usethesource/performance-improvements/fas…
sungshik Aug 14, 2026
ae802be
Merge branch 'performance-improvements-main' into performance-improve…
sungshik Aug 14, 2026
9a1df20
Merge pull request #57 from usethesource/performance-improvements/fas…
sungshik Aug 14, 2026
7f83e1a
Merge pull request #59 from usethesource/performance-improvements/fas…
sungshik Aug 14, 2026
106cbd7
Merge pull request #62 from usethesource/performance-improvements/fas…
sungshik Aug 14, 2026
ef21b19
Merge pull request #60 from usethesource/performance-improvements/fas…
sungshik Aug 14, 2026
8803bb5
Merge pull request #63 from usethesource/performance-improvements/fas…
sungshik Aug 14, 2026
fc6a33c
Merge pull request #58 from usethesource/performance-improvements/con…
sungshik Aug 14, 2026
6cd4fa0
Remove performance improvements scaffolding
sungshik Aug 14, 2026
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
90 changes: 72 additions & 18 deletions src/analysis/typepal/ConfigurableScopeGraph.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ bool defaultReportUnused (loc _, TModel _) {
return false;
}

list[loc] defaultFilterUnused(list[loc] defs, TModel tm) {
bool(loc, TModel) reportUnused = tm.config.reportUnused;
return [d | loc d <- defs, reportUnused(d, tm)];
}

// https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#:~:text=A%20URI%20is%20composed%20from,)%2C%20and%20the%20character%20%25%20.
// gen-delims: : / ? # [ ] @
// sub-delims: ! $ & ' ( ) * + , ;
Expand Down Expand Up @@ -157,12 +162,16 @@ data TypePalConfig(

bool(loc def, TModel tm) reportUnused = defaultReportUnused,

list[loc](list[loc] defs, TModel tm) filterUnused = defaultFilterUnused,

loc (Define def, str modelName, PathConfig pcfg) createLogicalLoc = defaultLogicalLoc,

list[str] (Use u, TModel tm) similarNames = defaultSimilarNames,

bool enableErrorFixes = true,

bool enableSortedMessages = false,

int cutoffForNameSimilarity = 3
);

Expand Down Expand Up @@ -404,11 +413,31 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){
/* parents) and definitions that can be reached in a single step via semantic links */
/************************************************************************************/

// Convert `tm.definesMap` to a more efficient representation for the kind
// of lookups that are performed in `bindWide`. The idea is to convert only
// once, and enjoy a return on investment each time when a lookup is
// performed in it (instead of also needing a `domainR` call each time).
map[loc, map[str, map[IdRole, set[loc]]]] convertDefinesMap() {
// Conversion function for the outer map
map[loc, map[str, map[IdRole, set[loc]]]] convertOuter(map[loc, map[str, rel[IdRole, loc]]] scope2id2pairs) {
return (scope: convertInner(scope2id2pairs[scope]) | loc scope <- scope2id2pairs);
}
// Conversion function for the inner maps
map[str, map[IdRole, set[loc]]] convertInner(map[str, rel[IdRole, loc]] id2pairs) {
return (id: Relation::index(id2pairs[id]) | str id <- id2pairs);
}
return convertOuter(tm.definesMap);
}

// Convert only once. (Note: this variable is local to `newScopeGraph`, so
// always associated with the same TModel.)
map[loc, map[str, map[IdRole, set[loc]]]] scope2id2role2defs = convertDefinesMap();

//@memo
// Retrieve all bindings for use in given syntactic scope
private set[loc] bindWide(loc scope, str id, set[IdRole] idRoles){
idsInScope = (scope in tm.definesMap) ? tm.definesMap[scope] : ();
foundDefs = id in idsInScope ? domainR(idsInScope[id], idRoles)<1> : {};
map[IdRole, set[loc]] role2defs = (scope2id2role2defs[scope] ? ())[id] ? ();
foundDefs = {*(role2defs[role] ? {}) | IdRole role <- idRoles};
// dbg("bindWide: <scope>, <id> =\> <foundDefs>");
return foundDefs;
}
Expand All @@ -421,26 +450,37 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){
return res;
}

// Cache to store results of `getPathTargets`. The assumption is that
// semantic paths might change between calls, so the cache needs to be
// invalidated when `the_solver.getPathsByPathRole()` returns an updated
// value (relative to the previous call of `getPathTargets`).
map[PathRole, map[loc, set[loc]]] getPathTargetsCache = ();

// Gets the target of each path with the provided role and source
set[loc] getPathTargets(PathRole role, loc source) {
if (role notin getPathTargetsCache) {
getPathTargetsCache[role] = ();
}
if (source notin getPathTargetsCache[role]) {
getPathTargetsCache[role][source] = {target | <source, loc target> <- pathsByPathRole[role]};
}
return getPathTargetsCache[role][source];
}

//@memo
// Find all (semantics induced, one-level) bindings for use in given syntactic scope via PathRole
private set[loc] lookupPathsWide(loc scope, Use use, PathRole pathRole){
// dbgEnter("lookupPathsWide: <use.id> in scope <scope>, role <pathRole>");;
res = {};

seenParents = {};
solve(res, scope) {
next_path:
for(<scope, loc parent> <- pathsByPathRole[pathRole] ? {}, parent notin seenParents){
seenParents += parent;
for(loc def <- lookupScopeWide(parent, use)){
switch(isAcceptablePathFun(parent, def, use, pathRole, the_solver)){
case acceptBinding():
res += def;
case ignoreContinue():
continue;
case ignoreSkipPath():
continue next_path;
}
for (loc parent <- getPathTargets(pathRole, scope)) {
for (loc def <- lookupScopeWide(parent, use)) {
switch (isAcceptablePathFun(parent, def, use, pathRole, the_solver)) {
case acceptBinding():
res += def;
case ignoreContinue():
continue; // Continue inner loop
case ignoreSkipPath():
break; // Break inner loop (continue outer loop)
}
}
}
Expand Down Expand Up @@ -497,13 +537,25 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){
return res;
}

public set[loc] lookupWide(Use u){
// Cache to store results of `lookupWide`. The assumption is that syntactic
// scopes will not change between calls, but semantic paths might, so the
// cache needs to be invalidated when `the_solver.getPathsByPathRole()`
// returns an updated value (relative to the previous call of `lookupWide`).
map[Use, set[loc]] lookupWideCache = ();

public set[loc] lookupWide(Use u){
// Update current paths and pathRoles
current_pathsByPathRole = the_solver.getPathsByPathRole();
if(current_pathsByPathRole != pathsByPathRole){
pathsByPathRole = current_pathsByPathRole;
pathRoles = domain(pathsByPathRole);
getPathTargetsCache = ();
lookupWideCache = ();
}

if (u in lookupWideCache) {
set[loc] defs = lookupWideCache[u];
if (isEmpty(defs)) throw NoBinding(); else return defs;
}

scope = u.scope;
Expand All @@ -512,6 +564,7 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){
// dbgPaths();
if(!(u has qualifierRoles)){
defs = {def | loc def <- lookupNestWide(scope, u), isAcceptableSimpleFun(def, u, the_solver) == acceptBinding()};
lookupWideCache[u] = defs;
// dbg("lookupWide: <u> =\> <defs>");
if(isEmpty(defs)) throw NoBinding(); else return defs;
} else {
Expand All @@ -528,6 +581,7 @@ ScopeGraph newScopeGraph(TModel tm, TypePalConfig config){
scopeLookups = lookupNestWide(qscope, use(u.ids[-1], "<u.occ>", u.occ, qscope, u.idRoles));
defs += { def | def <- scopeLookups, isAcceptableQualifiedFun(def, u, the_solver) == acceptBinding()};
}
lookupWideCache[u] = defs;
if(!isEmpty(defs)){
// dbg("lookupWide: <u> returns:\n<for(d <- defs){>\t==\> <d><}>");
return defs;
Expand Down
Loading
Loading