wip
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/BitSubsets.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/BitSubsets.kt
index 4b884f9..628e531 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/BitSubsets.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/BitSubsets.kt
@@ -97,6 +97,9 @@
 class BitSubsetsUnionSemilattice<T>(subsets: BitSubsets<T>) : Semilattice<BitSubsets<T>.Subset> {
     override val top: BitSubsets<T>.Subset = subsets.Subset()
     override fun meet(x: BitSubsets<T>.Subset, y: BitSubsets<T>.Subset): BitSubsets<T>.Subset = x union y
+    override fun copy(x: BitSubsets<T>.Subset): BitSubsets<T>.Subset {
+        TODO("Not yet implemented")
+    }
 }
 
 fun <T> BitSubsets<T>.unionSemilattice() = BitSubsetsUnionSemilattice(this)
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowAnalysis.kt
index 9399e4f..9573ed7 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowAnalysis.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowAnalysis.kt
@@ -7,6 +7,8 @@
 
 import org.jetbrains.kotlin.backend.common.ir.isUnconditional
 import org.jetbrains.kotlin.ir.IrElement
+import org.jetbrains.kotlin.ir.declarations.IrClass
+import org.jetbrains.kotlin.ir.declarations.IrFunction
 import org.jetbrains.kotlin.ir.expressions.IrBranch
 import org.jetbrains.kotlin.ir.expressions.IrBreak
 import org.jetbrains.kotlin.ir.expressions.IrContinue
@@ -35,22 +37,41 @@
      * Must be idempotent, commutative and associative.
      */
     fun meet(x: T, y: T): T
+    fun meetInPlace(x: T, y: T): T = meet(x, y)
+
+    fun meetAllInPlace(values: List<T>): T = values.reduce { x, y -> meet(x, y) }
+
+    fun copy(x: T): T
 }
 
-fun <T> Semilattice<T>.meetAll(values: List<T>): T = values.reduce { x, y -> meet(x, y) }
 
 private fun <K, T> MutableMap<K, T>.meetOrInsert(key: K, lattice: Semilattice<T>, value: T) {
-    this[key] = lattice.meet(this[key] ?: lattice.top, value)
+    getOrPut(key) { lattice.top }.let {
+        lattice.meetInPlace(it, value)
+    }
+    //this[key] = lattice.meet(this[key] ?: lattice.top, value)
 }
 
 object BitsetUnionSemilattice : Semilattice<BitSet> {
     override val top: BitSet = BitSet()
     override fun meet(x: BitSet, y: BitSet): BitSet = x.copy().also { it.or(y) }
+    override fun copy(x: BitSet): BitSet = x.copy()
 }
 
-class BitsetIntersectSemilattice(completeSet: BitSet) : Semilattice<BitSet> {
+class BitsetIntersectLattice(completeSet: BitSet) : Semilattice<BitSet> {
     override val top: BitSet = completeSet
+    val bottom: BitSet = BitSet()
     override fun meet(x: BitSet, y: BitSet): BitSet = x.copy().also { it.and(y) }
+    override fun meetInPlace(x: BitSet, y: BitSet): BitSet = x.also { it.and(y) }
+    override fun meetAllInPlace(values: List<BitSet>): BitSet {
+        val result = values.first()
+        for (v in values.drop(1)) {
+            meetInPlace(result, v)
+        }
+        return result
+    }
+    fun join(x: BitSet, y: BitSet): BitSet = x.copy().also { it.or(y) }
+    override fun copy(x: BitSet): BitSet = x.copy()
 }
 
 abstract class ForwardDataFlowAnalysis<T>(val lattice: Semilattice<T>) : IrVisitor<T, T>() {
@@ -60,6 +81,7 @@
     val returnedStates = mutableMapOf<IrReturnTargetSymbol, T>()
 
     infix fun T.meet(other: T): T = lattice.meet(this, other)
+    infix fun T.meetInPlace(other: T): T = lattice.meetInPlace(this, other)
 
     override fun visitElement(element: IrElement, data: T): T {
         var resultData = data
@@ -71,19 +93,27 @@
         return resultData
     }
 
+    override fun visitClass(declaration: IrClass, data: T): T {
+        return data;
+    }
+
+    override fun visitFunction(declaration: IrFunction, data: T): T {
+        return data;
+    }
+
     final override fun visitBranch(branch: IrBranch, data: T): T = error("Should not call this")
 
     override fun visitWhen(expression: IrWhen, data: T): T {
         val conditions = expression.branches.map { it.condition }
         val conditionResults = conditions.fold(listOf<T>()) { acc, next ->
             val prev = if (acc.isEmpty()) data else acc.last()
-            acc + listOf(next.accept(this, prev))
+            acc + listOf(lattice.copy(next.accept(this, prev)))
         }
         val branchResults = conditionResults.zip(expression.branches) { conditionResult, branch ->
             branch.result.accept(this, conditionResult)
         }
         val elseResult = if (!expression.branches.last().isUnconditional()) conditionResults.last() else lattice.top
-        return lattice.meetAll(branchResults + elseResult)
+        return lattice.meetAllInPlace(branchResults + elseResult)
     }
 
     final override fun visitWhileLoop(loop: IrWhileLoop, data: T): T = super.visitWhileLoop(loop, data)
@@ -96,24 +126,24 @@
         var lastIterData = lattice.top
         // TODO consider short-circuiting in special cases
         while (true) {
-            var thisIterData = lastIterData meet data
+            var thisIterData = data meetInPlace lastIterData
 
-            thisIterData = thisIterData meet continuedStates[loop]!!
+            thisIterData = thisIterData meetInPlace continuedStates[loop]!!
 
             if (loop is IrWhileLoop) {
-                thisIterData = thisIterData meet loop.condition.accept(this, thisIterData)
+                thisIterData = thisIterData meetInPlace loop.condition.accept(this, thisIterData)
             }
 
             thisIterData = loop.body?.accept(this, thisIterData) ?: thisIterData
 
             if (loop is IrDoWhileLoop) {
-                thisIterData = thisIterData meet loop.condition.accept(this, thisIterData)
+                thisIterData = thisIterData meetInPlace loop.condition.accept(this, thisIterData)
             }
 
-            thisIterData = thisIterData meet breakedStates[loop]!!
+            thisIterData = thisIterData meetInPlace breakedStates[loop]!!
 
             if (thisIterData == lastIterData) break
-            require(thisIterData meet lastIterData == thisIterData) { "Data flow analysis diverges" }
+            //require(thisIterData meet lastIterData == thisIterData) { "Data flow analysis diverges" }
             lastIterData = thisIterData
         }
 
@@ -132,7 +162,7 @@
 
     override fun visitReturnableBlock(expression: IrReturnableBlock, data: T): T {
         val blockResult = super.visitReturnableBlock(expression, data)
-        return lattice.meet(blockResult, returnedStates[expression.symbol]!!)
+        return lattice.meetInPlace(blockResult, returnedStates[expression.symbol] ?: lattice.top)
     }
 
     override fun visitReturn(expression: IrReturn, data: T): T {
@@ -146,12 +176,12 @@
 
         // The try block may be executed only partially,
         // the approximation of "either entirely or not at all" is safe.
-        val afterTry = aTry.tryResult.accept(this, data) meet data
+        val afterTry = aTry.tryResult.accept(this, data) meetInPlace data
         val afterCatches = aTry.catches.map { it.result.accept(this, afterTry) }
 
         require(aTry.finallyExpression == null)
 
-        return lattice.meetAll(listOf(afterTry) + afterCatches)
+        return lattice.meetAllInPlace(listOf(afterTry) + afterCatches)
     }
 
     override fun visitThrow(expression: IrThrow, data: T): T {
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt
index a74310a..d335abc 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt
@@ -288,21 +288,23 @@
                     previous.and(set)
             }
 
-            val containersSemilattice = BitsetIntersectSemilattice(BitSet().also {
+            val containersSemilattice = BitsetIntersectLattice(BitSet().also {
                 initializedContainers.containerIds.forEach { _, id -> it.set(id) }
             })
 
             val intraproceduralAnalysis = object : ForwardDataFlowAnalysis<BitSet>(containersSemilattice) {
-                private fun BitSet.withSetBit(bit: Int): BitSet =
-                        if (this.get(bit)) this else copy().also { it.set(bit) }
+                // TODO careful in place!!!
+                private fun BitSet.withSetBit(bit: Int): BitSet = this.also { it.set(bit) }
 
-                private fun getResultAfterCall(function: IrSimpleFunction, set: BitSet): BitSet {
+                // FIXME careful with the returned value!!!!
+                private fun getResultAfterCall(function: IrSimpleFunction, set: BitSet?): BitSet? {
                     val result = initializedContainers.afterCall[function]
                     if (result == null) {
                         val file = function.calledInitializer ?: return set
-                        return set.withSetBit(initializedContainers.containerIds[file]!!)
+                        return (set ?: BitSet()).also { it.set(initializedContainers.containerIds[file]!!) }
                     }
-                    return result.copy().also { it.or(set) }
+                    if (result.isEmpty) return set
+                    return (set ?: BitSet()).also { it.or(result) }
                 }
 
                 private fun updateResultForFunction(function: IrSimpleFunction, globalSet: BitSet, threadLocalSet: BitSet) {
@@ -313,8 +315,8 @@
 
                 private fun updateResultForFunction(function: IrSimpleFunction, set: BitSet) {
                     if (analysisGoal != AnalysisGoal.ComputeInitializedBeforeCall) return
-                    intersectInitializedFiles(initializedContainers.beforeCallGlobal, function, set.copy().also { it.or(containersWithInitializedGlobals) })
-                    intersectInitializedFiles(initializedContainers.beforeCallThreadLocal, function, set.copy().also { it.or(containersWithInitializedThreadLocals) })
+                    intersectInitializedFiles(initializedContainers.beforeCallGlobal, function, set.also { it.or(containersWithInitializedGlobals) })
+                    intersectInitializedFiles(initializedContainers.beforeCallThreadLocal, function, set.also { it.or(containersWithInitializedThreadLocals) })
                 }
 
                 override fun visitGetObjectValue(expression: IrGetObjectValue, data: BitSet): BitSet {
@@ -346,7 +348,7 @@
                                 callSitesRequiringThreadLocalInitializerCall += expression
                         }
                     }
-                    return getResultAfterCall(actualCallee, argumentsResult)
+                    return getResultAfterCall(actualCallee, argumentsResult) ?: BitSet()
                 }
 
                 private fun processExecuteImpl(expression: IrCall, data: BitSet): BitSet {
@@ -359,7 +361,7 @@
                     if (analysisGoal != AnalysisGoal.CollectCallSites) {
                         require(!jobInvocation.isVirtualCall) { "Expected a static call but was: ${jobInvocation.render()}" }
                         updateResultForFunction(jobInvocation.actualCallee,
-                                curData.copy().also { it.or(containersWithInitializedGlobals) }, // Globals (= shared) visible to other threads as well.
+                                curData.also { it.or(containersWithInitializedGlobals) }, // Globals (= shared) visible to other threads as well.
                                 BitSet() // A new thread is about to be created - no thread locals initialized yet.
                         )
                     }
@@ -379,20 +381,24 @@
                     val devirtualizedCallSite = virtualCallSites[expression] ?: return data
                     val arguments = expression.getArgumentsWithIr()
                     val argumentsResult = arguments.fold(data) { set, arg -> arg.second.accept(this, set) }
-                    var callResult = BitSet()
+                    var callResult: BitSet? = null
                     var first = true
                     for (callSite in devirtualizedCallSite) {
                         val callee = callSite.actualCallee.irFunction ?: error("No IR for: ${callSite.actualCallee}")
                         updateResultForFunction(callee, argumentsResult)
                         if (first) {
-                            callResult = getResultAfterCall(callee, BitSet())
+                            callResult = getResultAfterCall(callee, null)
                             first = false
                         } else {
-                            val otherSet = getResultAfterCall(callee, BitSet())
-                            callResult.and(otherSet)
+                            val otherSet = getResultAfterCall(callee, null)
+                            if (otherSet == null) {
+                                callResult = null
+                            } else {
+                                callResult?.and(otherSet)
+                            }
                         }
                     }
-                    return argumentsResult.copy().also { it.or(callResult) }
+                    return argumentsResult.also { if (callResult != null) { it.or(callResult) } }
                 }
             }