Skip to content

Commit 64507ab

Browse files
authored
Merge pull request github#6712 from hvitved/csharp/subsumption-perf-take2
C#: Speedup type subsumption calculation
2 parents 187b7e1 + b9c4abe commit 64507ab

File tree

2 files changed

+84
-152
lines changed

2 files changed

+84
-152
lines changed

csharp/ql/lib/semmle/code/csharp/Unification.qll

Lines changed: 59 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module Gvn {
1717
string getNameNested(Type t) {
1818
if not t instanceof NestedType or t.(NestedType).getDeclaringType() instanceof GenericType
1919
then result = t.getName()
20-
else result = getNameNested(t.(NestedType).getDeclaringType()) + "." + t.getName()
20+
else result = getNameNested(t.(NestedType).getDeclaringType()) + "+" + t.getName()
2121
}
2222

2323
/**
@@ -326,123 +326,78 @@ module Gvn {
326326
getTypeArgument(k, t, i) = TTypeParameterGvnType()
327327
}
328328

329-
/**
330-
* Hold if (non-type-parameters) `arg1` and `arg2` are unifiable, and both are
331-
* the `i`th type argument of a compound type of kind `k`.
332-
*/
333-
pragma[nomagic]
334-
private predicate unifiableNonTypeParameterTypeArguments(
335-
CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2, int i
336-
) {
337-
exists(int j |
338-
arg1 = getNonTypeParameterTypeArgument(k, _, i) and
339-
arg2 = getNonTypeParameterTypeArgument(k, _, j) and
340-
i <= j and
341-
j <= i
342-
|
343-
arg1 = arg2
344-
or
345-
unifiable(arg1, arg2)
346-
)
347-
}
348-
349329
/**
350330
* Hold if `arg1` and `arg2` are unifiable, and both are the `i`th type argument
351331
* of a compound type of kind `k`.
332+
*
333+
* `subsumes` indicates whether `arg1` in fact subsumes `arg2`.
352334
*/
353335
pragma[nomagic]
354336
private predicate unifiableTypeArguments(
355-
CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2, int i
337+
CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2, int i, boolean subsumes
356338
) {
357-
unifiableNonTypeParameterTypeArguments(k, arg1, arg2, i)
358-
or
359-
exists(int j |
360-
arg1 = TTypeParameterGvnType() and
361-
typeArgumentIsTypeParameter(k, _, i) and
362-
arg2 = getTypeArgument(k, _, j) and
363-
i <= j and
364-
j <= i
339+
arg1 = getNonTypeParameterTypeArgument(k, _, pragma[only_bind_into](i)) and
340+
arg2 = getNonTypeParameterTypeArgument(k, _, pragma[only_bind_into](i)) and
341+
(
342+
arg1 = arg2 and
343+
subsumes = true
344+
or
345+
unifiable(arg1, arg2, subsumes)
365346
)
366347
or
367-
exists(int j |
368-
arg1 = getTypeArgument(k, _, i) and
369-
typeArgumentIsTypeParameter(k, _, j) and
370-
arg2 = TTypeParameterGvnType() and
371-
i <= j and
372-
j <= i
373-
)
348+
arg1 = TTypeParameterGvnType() and
349+
typeArgumentIsTypeParameter(k, _, pragma[only_bind_into](i)) and
350+
arg2 = getTypeArgument(k, _, pragma[only_bind_into](i)) and
351+
subsumes = true
352+
or
353+
arg1 = getNonTypeParameterTypeArgument(k, _, pragma[only_bind_into](i)) and
354+
typeArgumentIsTypeParameter(k, _, pragma[only_bind_into](i)) and
355+
arg2 = TTypeParameterGvnType() and
356+
subsumes = false
374357
}
375358

376359
pragma[nomagic]
377360
private predicate unifiableSingle0(
378-
CompoundTypeKind k, ConstructedGvnType t2, GvnTypeArgument arg1, GvnTypeArgument arg2
361+
CompoundTypeKind k, ConstructedGvnType t2, GvnTypeArgument arg1, GvnTypeArgument arg2,
362+
boolean subsumes
379363
) {
380-
unifiableTypeArguments(k, arg1, arg2, 0) and
364+
unifiableTypeArguments(k, arg1, arg2, 0, subsumes) and
381365
arg2 = getTypeArgument(k, t2, 0) and
382366
k.getNumberOfTypeParameters() = 1
383367
}
384368

385-
/**
386-
* Holds if the type arguments of types `t1` and `t2` are unifiable, `t1`
387-
* and `t2` are of the same kind, and the number of type arguments is 1.
388-
*/
389-
private predicate unifiableSingle(ConstructedGvnType t1, ConstructedGvnType t2) {
390-
exists(CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2 |
391-
unifiableSingle0(k, t2, arg1, arg2) and
392-
arg1 = getTypeArgument(k, t1, 0)
393-
)
394-
}
395-
396369
pragma[nomagic]
397370
private predicate unifiableMultiple01Aux0(
398-
CompoundTypeKind k, ConstructedGvnType t2, GvnTypeArgument arg10, GvnTypeArgument arg21
371+
CompoundTypeKind k, ConstructedGvnType t2, GvnTypeArgument arg10, GvnTypeArgument arg21,
372+
boolean subsumes
399373
) {
400374
exists(GvnTypeArgument arg20 |
401-
unifiableTypeArguments(k, arg10, arg20, 0) and
375+
unifiableTypeArguments(k, arg10, arg20, 0, subsumes) and
402376
arg20 = getTypeArgument(k, t2, 0) and
403377
arg21 = getTypeArgument(k, t2, 1)
404378
)
405379
}
406380

407381
pragma[nomagic]
408382
private predicate unifiableMultiple01Aux1(
409-
CompoundTypeKind k, ConstructedGvnType t1, GvnTypeArgument arg10, GvnTypeArgument arg21
383+
CompoundTypeKind k, ConstructedGvnType t1, GvnTypeArgument arg10, GvnTypeArgument arg21,
384+
boolean subsumes
410385
) {
411386
exists(GvnTypeArgument arg11 |
412-
unifiableTypeArguments(k, arg11, arg21, 1) and
387+
unifiableTypeArguments(k, arg11, arg21, 1, subsumes) and
413388
arg10 = getTypeArgument(k, t1, 0) and
414389
arg11 = getTypeArgument(k, t1, 1)
415390
)
416391
}
417392

418-
/**
419-
* Holds if the first two type arguments of types `t1` and `t2` are unifiable,
420-
* and both `t1` and `t2` are of kind `k`.
421-
*/
422-
private predicate unifiableMultiple01(
423-
CompoundTypeKind k, ConstructedGvnType t1, ConstructedGvnType t2
424-
) {
425-
exists(GvnTypeArgument arg10, GvnTypeArgument arg21 |
426-
unifiableMultiple01Aux0(k, t2, arg10, arg21) and
427-
unifiableMultiple01Aux1(k, t1, arg10, arg21)
428-
)
429-
}
430-
431393
pragma[nomagic]
432394
private predicate unifiableMultiple2Aux(
433-
CompoundTypeKind k, ConstructedGvnType t2, int i, GvnTypeArgument arg1, GvnTypeArgument arg2
434-
) {
435-
unifiableTypeArguments(k, arg1, arg2, i) and
436-
arg2 = getTypeArgument(k, t2, i) and
437-
i >= 2
438-
}
439-
440-
private predicate unifiableMultiple2(
441-
CompoundTypeKind k, ConstructedGvnType t1, ConstructedGvnType t2, int i
395+
CompoundTypeKind k, ConstructedGvnType t2, int i, GvnTypeArgument arg1, boolean subsumes
442396
) {
443-
exists(GvnTypeArgument arg1, GvnTypeArgument arg2 |
444-
unifiableMultiple2Aux(k, t2, i, arg1, arg2) and
445-
arg1 = getTypeArgument(k, t1, i)
397+
exists(GvnTypeArgument arg2 |
398+
unifiableTypeArguments(k, arg1, arg2, i, subsumes) and
399+
arg2 = getTypeArgument(k, t2, i) and
400+
i >= 2
446401
)
447402
}
448403

@@ -452,45 +407,35 @@ module Gvn {
452407
*/
453408
pragma[nomagic]
454409
private predicate unifiableMultiple(
455-
CompoundTypeKind k, ConstructedGvnType t1, ConstructedGvnType t2, int i
410+
CompoundTypeKind k, ConstructedGvnType t1, ConstructedGvnType t2, int i, boolean subsumes
456411
) {
457-
unifiableMultiple01(k, t1, t2) and i = 1
412+
exists(GvnTypeArgument arg10, GvnTypeArgument arg21, boolean subsumes1, boolean subsumes2 |
413+
unifiableMultiple01Aux0(k, t2, arg10, arg21, subsumes1) and
414+
unifiableMultiple01Aux1(k, t1, arg10, arg21, subsumes2) and
415+
subsumes = subsumes1.booleanAnd(subsumes2)
416+
) and
417+
i = 1
458418
or
459-
unifiableMultiple(k, t1, t2, i - 1) and
460-
unifiableMultiple2(k, t1, t2, i)
419+
exists(GvnTypeArgument arg1, boolean subsumes1, boolean subsumes2 |
420+
unifiableMultiple(k, t1, t2, i - 1, subsumes1) and
421+
unifiableMultiple2Aux(k, t2, i, arg1, subsumes2) and
422+
arg1 = getTypeArgument(k, t1, i) and
423+
subsumes = subsumes1.booleanAnd(subsumes2)
424+
)
461425
}
462426

463-
private newtype TTypePath =
464-
TTypePathNil() or
465-
TTypePathCons(int head, TTypePath tail) { exists(getTypeAtCons(_, head, tail)) }
466-
467-
/**
468-
* Gets the GVN inside GVN `t`, by following the path `path`, if any.
469-
*/
470-
private GvnType getTypeAt(GvnType t, TTypePath path) {
471-
path = TTypePathNil() and
472-
result = t
427+
pragma[nomagic]
428+
private predicate unifiable(ConstructedGvnType t1, ConstructedGvnType t2, boolean subsumes) {
429+
exists(CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2 |
430+
unifiableSingle0(k, t2, arg1, arg2, subsumes) and
431+
arg1 = getTypeArgument(k, t1, 0)
432+
)
473433
or
474-
exists(ConstructedGvnTypeList l, int head, TTypePath tail |
475-
t = TConstructedGvnType(l) and
476-
path = TTypePathCons(head, tail) and
477-
result = getTypeAtCons(l, head, tail)
434+
exists(CompoundTypeKind k |
435+
unifiableMultiple(k, t1, t2, k.getNumberOfTypeParameters() - 1, subsumes)
478436
)
479437
}
480438

481-
private GvnType getTypeAtCons(ConstructedGvnTypeList l, int head, TTypePath tail) {
482-
result = getTypeAt(l.getArg(head), tail)
483-
}
484-
485-
/**
486-
* Gets the leaf GVN inside GVN `t`, by following the path `path`, if any.
487-
*/
488-
pragma[noinline]
489-
private GvnType getLeafTypeAt(GvnType t, TTypePath path) {
490-
result = getTypeAt(t, path) and
491-
not result instanceof ConstructedGvnType
492-
}
493-
494439
cached
495440
private module Cached {
496441
cached
@@ -540,33 +485,20 @@ module Gvn {
540485
}
541486

542487
/**
543-
* Holds if GVNs `t1` and `t2` can be unified. That is, is it possible to
488+
* Holds if GVNs `t1` and `t2` can be unified. That is, it is possible to
544489
* replace all type parameters in `t1` and `t2` with some GVNs (possibly
545490
* type parameters themselves) to make the two substituted terms equal.
546491
*/
547492
cached
548-
predicate unifiable(ConstructedGvnType t1, ConstructedGvnType t2) {
549-
unifiableSingle(t1, t2)
550-
or
551-
exists(CompoundTypeKind k | unifiableMultiple(k, t1, t2, k.getNumberOfTypeParameters() - 1))
552-
}
493+
predicate unifiable(ConstructedGvnType t1, ConstructedGvnType t2) { unifiable(t1, t2, _) }
553494

554495
/**
555-
* Holds if GVN `t1` subsumes GVN `t2`. That is, is it possible to replace all
496+
* Holds if GVN `t1` subsumes GVN `t2`. That is, it is possible to replace all
556497
* type parameters in `t1` with some GVNs (possibly type parameters themselves)
557498
* to make the two substituted terms equal.
558499
*/
559500
cached
560-
predicate subsumes(ConstructedGvnType t1, ConstructedGvnType t2) {
561-
unifiable(t1, t2) and // subsumption implies unification
562-
forall(TTypePath path, GvnType leaf1 | leaf1 = getLeafTypeAt(t1, path) |
563-
exists(GvnType child2 | child2 = getTypeAt(t2, path) |
564-
leaf1 = TTypeParameterGvnType()
565-
or
566-
leaf1 = child2
567-
)
568-
)
569-
}
501+
predicate subsumes(ConstructedGvnType t1, ConstructedGvnType t2) { unifiable(t1, t2, true) }
570502
}
571503

572504
import Cached

0 commit comments

Comments
 (0)