Skip to content

Commit

Permalink
[FIR] Improve performance when tracking local variable assignment
Browse files Browse the repository at this point in the history
During data-flow analysis, mutable local variables need not have their
assignments tracked before they can be smart-cast. This is in case a
local variable is mutated within a loop or captured and mutated within a
closure. There was a significant performance problem with this analysis
which could cause exponential degredation based on the number of when
branches or catch blocks.

^KT-69723 Fixed
  • Loading branch information
bnorm authored and qodana-bot committed Jul 25, 2024
1 parent dd0ab96 commit 315d59a
Show file tree
Hide file tree
Showing 7 changed files with 480 additions and 11 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,8 @@ internal class FirLocalVariableAssignmentAnalyzer {
return property in assignments
}

fun add(property: FirProperty, assignment: Assignment) {
assignments.getOrPut(property) { mutableSetOf() }.add(assignment)
fun add(property: FirProperty, assignment: Assignment): Boolean {
return assignments.getOrPut(property) { mutableSetOf() }.add(assignment)
}

fun copy(): VariableAssignments {
Expand All @@ -348,19 +348,20 @@ internal class FirLocalVariableAssignmentAnalyzer {
return copy
}

fun merge(other: VariableAssignments?) {
if (other == null) return
fun merge(other: VariableAssignments?): Boolean {
if (other == null || other.assignments.isEmpty()) return false

var modified = false
for ((property, values) in other.assignments) {
assignments.getOrPut(property) { mutableSetOf() }.addAll(values)
modified = modified or assignments.getOrPut(property) { mutableSetOf() }.addAll(values)
}
return modified
}

fun retain(properties: Set<FirProperty>) {
assignments.keys.retainAll(properties)
}

fun isEmpty(): Boolean = assignments.isEmpty()

fun getAssignedProperties(): Set<FirPropertySymbol> {
return assignments.entries
// TODO(KT-57563): Operator assignments should be treated just like any other assignment.
Expand Down Expand Up @@ -518,14 +519,12 @@ internal class FirLocalVariableAssignmentAnalyzer {
}

private fun MiniFlow.recordAssignment(property: FirProperty, assignment: Assignment) {
assignedLater.add(property, assignment)
if (!assignedLater.add(property, assignment)) return // Parents do not need to be updated if this flow is not updated.
parents.forEach { it.recordAssignment(property, assignment) }
}

private fun MiniFlow.recordAssignments(properties: VariableAssignments) {
if (properties.isEmpty()) return

assignedLater.merge(properties)
if (!assignedLater.merge(properties)) return // Parents do not need to be updated if this flow is not updated.
parents.forEach { it.recordAssignments(properties) }
}

Expand Down
Loading

0 comments on commit 315d59a

Please sign in to comment.