diff --git a/lib/analysis/ecb.rb b/lib/analysis/ecb.rb
index 1933a2c2e4c5cc05cda7b26c01e96c9f9795e15e..453e12254867df84db3c1c08689d64bd67786c4f 100644
--- a/lib/analysis/ecb.rb
+++ b/lib/analysis/ecb.rb
@@ -38,6 +38,7 @@ class ECBAnalysis
   # Calculates set of accessed cache blocks locally for this stream of
   # instructions. Modifications of the cache by calls are ignored.
   def ecb(instructions, cache_config)
+    cache_config.analysis_strategy = :must # Only used for address calculation
     cache = Cache.new(cache_config) # Only used for address calculation
     ecb = Set.new
     instructions.each do |instr|
diff --git a/lib/analysis/meg.rb b/lib/analysis/meg.rb
index 6a73a415f7c570f2cfb132daba266f9d5ac2fc7a..dff8793e047d6a1d0960d770910dfa3666906065 100644
--- a/lib/analysis/meg.rb
+++ b/lib/analysis/meg.rb
@@ -331,6 +331,9 @@ class MAAnalysis
       cache = ilist.first.block.classification.cache_at_entry[:must]
     end
 
+    #quickfix for analysis: we just need to model the cache state durng execution
+    cache.strategy = :must
+
     graph = MEG.new(lst.dup, cache, @pipeline)
     graph.construct_uninterrupted_graph
     graph.minimize
diff --git a/lib/core/cache.rb b/lib/core/cache.rb
index 1ea462f3289e3d25d5b628b1e5c3fa367adddde1..778c025abefced5fb415e4ec82a7144bd3440b9e 100644
--- a/lib/core/cache.rb
+++ b/lib/core/cache.rb
@@ -6,6 +6,23 @@ class LRUMustAnalysisOperator
   # combination after H. Theiling, C. Ferdinand: Combining Abstract
   # Interpretation and ILP for Microarchitecture Modelling and Program Path
   # Analysis.
+
+  def update(set, elem)
+    cache_hit = set.include?(elem)
+    if cache_hit
+      # cache hit: move datum to way0, age the rest
+      set.ways[1].merge(set.ways[0])
+      set.ways[1].delete(elem)
+      set.ways[0] = Set[elem]
+    else
+      # cache miss: discard way1, move way0 to way1, move datum to way0
+      set.ways[1] = set.ways[0].dup
+      set.ways[0] = Set[elem]
+    end
+
+    return cache_hit
+  end
+
   def combine(a, b)
     elems_a = a.elements
     elems_b = b.elements
@@ -52,7 +69,30 @@ class LRUMustAnalysisOperator
 end
 
 class LRUMayAnalysisOperator
-    def combine(a, b)
+  def update(m, x)
+    m_dup = m.dup
+    way_x = m_dup.ways.index(x)
+
+    if !!way_x && way_x < m.ways.size - 1
+      m.ways[0] = Set[x]
+      for i in 1..way_x do
+        m.ways[i] = m_dup.ways[i-1].dup
+      end
+      m.ways[way_x+1] = m_dup.ways[way_x+1].dup
+      m.ways[way_x+1].merge(m_dup.ways[way_x])
+      m.ways[way_x+1].delete(x)
+      for i in (way_x + 2)...m.ways.size do
+        m.ways[i] = m_dup.ways[i].dup
+      end
+    else
+      m.ways[0] = Set[x]
+      for i in 1...m.ways.size do
+        m.ways[i] = m_dup.ways[i-1].dup
+      end
+    end
+  end
+
+  def combine(a, b)
     elems_a = a.elements
     elems_b = b.elements
     elems = Hash.new
@@ -60,7 +100,7 @@ class LRUMayAnalysisOperator
     elems_a.each { |e| elems[e[:elem]] = {way: e[:way], occs: 1}}
     elems_b.each { |e|
       if elems[e[:elem]].nil?
-                 
+
         elems[e[:elem]] = {way: e[:way], occs: 1}
       else
         old = elems[e[:elem]]
@@ -81,31 +121,101 @@ class LRUMayAnalysisOperator
     }
   end
 
-  # All ways of the callee up to the oldest non-empty way are taken over.
-  # The remaining ways are then filled up with the youngest ways of the caller.
   def interprocedural_combine(caller_state, callee_state)
     # no conflict, keep existing ways
     return if callee_state.ways.all? { |w| w.empty? }
 
     if callee_state.ways[-1].empty?
-        if (caller_state.ways[0] & (callee_state.ways[0])).empty?
-            caller_state.ways[1] = caller_state.ways[0].dup
-            caller_state.ways[1].subtract(callee_state.ways[0])
-            caller_state.ways[0] = callee_state.ways[0].dup
-        else
-            caller_state.ways[1] = caller_state.ways[0].dup
-            caller_state.ways[1].merge(caller_state.ways[0]).subtract(callee_state.ways[0])
-            caller_state.ways[0] = callee_state.ways[0].dup
-        end
+      if (caller_state.ways[0] & (callee_state.ways[0])).empty?
+        caller_state.ways[1] = caller_state.ways[0].dup
+        caller_state.ways[1].subtract(callee_state.ways[0])
+        caller_state.ways[0] = callee_state.ways[0].dup
+      else
+        caller_state.ways[1] = caller_state.ways[0].dup
+        caller_state.ways[1].merge(caller_state.ways[0]).subtract(callee_state.ways[0])
+        caller_state.ways[0] = callee_state.ways[0].dup
+      end
     else
-        # callee overwrote both ways
-        caller_state.ways.each_with_index { |e, i|
-            caller_state.ways[i] = callee_state.ways[i].dup
-        }
+      # callee overwrote both ways
+      caller_state.ways.each_with_index { |e, i|
+        caller_state.ways[i] = callee_state.ways[i].dup
+      }
     end
   end
 end
 
+class LRUMayMaxAnalysisOperator
+  def update(m, m_pers, x)
+    m_pers_dup = m_pers.dup
+    mayevict = m.ways.inject(0) { |acc, elem| if (elem.include?(x)) then acc + elem.size - 1 else acc + elem.size end } >= m.ways.size
+    associativity = m_pers.ways.size
+
+    if mayevict
+      m_pers.ways[0] = Set[x]
+      for i in 1...associativity do
+        m_pers.ways[i] = m_pers_dup.ways[i-1].dup
+        m_pers.ways[i].delete(x)
+      end
+      m_pers.removed = m_pers_dup.removed.dup
+      m_pers.removed.merge(m_pers_dup.ways[-1])
+      m_pers.removed.delete(x)
+    else
+      m_pers.ways[0] = Set[x]
+      for i in 1...associativity - 1 do
+        m_pers.ways[i] = m_pers_dup.ways[i-1].dup
+        m_pers.ways[i].delete(x)
+      end
+      m_pers.ways[associativity-1] = m_pers_dup.ways[associativity-1].dup
+      m_pers.ways[associativity-1].merge(m_pers_dup.ways[associativity-2])
+      m_pers.ways[associativity-1].delete(x)
+
+      m_pers.removed = m_pers_dup.removed.dup
+      m_pers.removed.delete(x)
+    end
+  end
+
+  def combine(a, b)
+    elems_a = a.elements
+    elems_b = b.elements
+    elems = Hash.new
+
+    elems_a.each { |e| elems[e[:elem]] = {way: e[:way], occs: 1}}
+    elems_b.each { |e|
+      if elems[e[:elem]].nil?
+
+        elems[e[:elem]] = {way: e[:way], occs: 1}
+      else
+        old = elems[e[:elem]]
+        old_way = old[:way]
+        new_way = old_way > e[:way] ? old_way : e[:way]
+        elems[e[:elem]] = {way: new_way, occs: old[:occs] + 1 }
+      end
+    }
+
+    a.removed.merge(b.removed)
+
+    a.ways[0] = Set.new
+    a.ways[1] = Set.new
+    elems.each { |k,v|
+      next if a.removed.include?(k)
+      if v[:way] == 0
+        a.ways[0].add(k)
+      else
+        a.ways[1].add(k)
+      end
+    }
+
+  end
+
+  # All ways of the callee up to the oldest non-empty way are taken over.
+  # The remaining ways are then filled up with the youngest ways of the caller.
+  def interprocedural_combine(caller_state, callee_state)
+    combine(caller_state, callee_state)
+    caller_state.removed.merge(callee_state.removed)
+  end
+end
+
+# deprecated
 class LRUPersAnalysisOperator
   def combine(a, b)
     elems_a = a.elements
@@ -159,23 +269,20 @@ end
 
 class LRUCacheSet
    attr_accessor :ways
-   attr_accessor :removed #set of removed blocks (only for persistence analysis)
    attr_reader :associativity, :operator
-  def initialize(associativity, strategy=:must)
+  def initialize(associativity, strategy)
     die("Only 2-way cache supported currently.") if associativity != 2
     @associativity = associativity
     @ways = Array.new(associativity) { Set.new }
-    @removed = Set.new
     
     if strategy == :must
       @operator = LRUMustAnalysisOperator.new
     elsif strategy == :may
       @operator = LRUMayAnalysisOperator.new
     elsif strategy == :pers
-      @operator = LRUPersAnalysisOperator.new
+      @operator = LRUMayMaxAnalysisOperator.new
     else
-      #throw "no such strategy as #{strategy}"
-      @operator = LRUMustAnalysisOperator.new
+      throw "no such strategy as #{strategy}"
     end
   end
 
@@ -188,22 +295,7 @@ class LRUCacheSet
   end
 
   def access(tag)
-    cache_hit = include?(tag)
-    if cache_hit
-      # cache hit: move datum to way0, age the rest
-      @ways[1].merge(@ways[0])
-      @ways[1].delete(tag)
-      @ways[0] = Set[tag]
-    else
-      # cache miss: discard way1, move way0 to way1, move datum to way0
-      if @operator.is_a?(LRUPersAnalysisOperator)
-          @removed.merge(ways[1])
-      end
-      @ways[1] = @ways[0].dup
-      @ways[0] = Set[tag]
-    end
-
-    return cache_hit
+    @operator.update(self, tag)
   end
 
   def elements
@@ -228,7 +320,6 @@ class LRUCacheSet
     @associativity = source.associativity
     @ways = source.ways.map {|w| w.dup}
     @operator = source.operator
-    @removed = source.removed.dup
   end
 
   def ==(other)
@@ -238,6 +329,47 @@ class LRUCacheSet
   alias_method :eql?, :==
 end
 
+# Cullmann Cache Persistence Analysis
+# combined may and may_pers
+class LRUPersCacheSet < LRUCacheSet
+  attr_accessor :removed #set of removed blocks (only for persistence analysis)
+  attr_accessor :shadow_may
+  def initialize(associativity, strategy)
+    die("PERS without :pers.") if strategy != :pers
+    super(associativity, strategy)
+    @removed = Set.new
+    @shadow_may = LRUCacheSet.new(associativity, :may)
+  end
+
+  # i.e. is shadow may update after the first call?
+  def access(tag)
+    @operator.update(@shadow_may, self, tag)
+    @shadow_may.access(tag)
+  end
+
+  def combine(other)
+    @operator.combine(self,other)
+    @shadow_may.combine(other)
+  end
+
+  def interprocedural_combine(callee)
+    @operator.interprocedural_combine(self,callee)
+    @shadow_may.interprocedural_combine(callee)
+  end
+
+  def initialize_copy(source)
+    super(source)
+    @removed = source.removed.dup
+    @shadow_may = source.shadow_may.dup
+  end
+
+  def ==(other)
+    super(other) && @shadow_may == other.shadow_may
+  end
+
+  alias_method :eql?, :==
+end
+
 class Cache
   attr_accessor :sets, :number_of_sets, :set_bits, :addr_bits, :associativity, :hit_time, :miss_time, :strategy
   def initialize(cache_config)
@@ -252,11 +384,16 @@ class Cache
   def init_sets(replacement_policy)
     case replacement_policy
     when 'LRU', 'lru'
-      @settype = LRUCacheSet
-      @sets = Array.new(@number_of_sets) {|i| LRUCacheSet.new(@associativity, @strategy)}
+      case @strategy
+      when :pers
+        @settype = LRUPersCacheSet
+      else
+        @settype = LRUCacheSet
+      end
     else
       die("Only LRU caches are supported right now.")
     end
+    @sets = Array.new(@number_of_sets) {|i| @settype.new(@associativity, @strategy)}
   end
 
   def access(address)
@@ -282,17 +419,6 @@ class Cache
     self
   end
 
-  #def interprocedural_combine(callee)
-  #  @sets.each_index do |i|
-  #    if callee.nil?
-  #      reset(i)
-  #    else
-  #      @sets[i].interprocedural_combine(callee.sets[i])
-  #    end
-  #  end
-  #  self
-  #end
-
   def interprocedural_combine(call_instr)
     if @strategy == :must
       ecb = Set.new
@@ -302,8 +428,6 @@ class Cache
       @sets.each_index do |i|
         if ecb.include?(i)
           reset(i)
-        #else
-          #@sets[i].interprocedural_combine(callee.sets[i])
         end
       end
     else
@@ -319,7 +443,7 @@ class Cache
   end
 
   def reset(set)
-    @sets[set] = @settype.new(@associativity)
+    @sets[set] = @settype.new(@associativity, @strategy)
   end
 
   def reset_all
@@ -331,6 +455,7 @@ class Cache
     @number_of_sets = source.number_of_sets
     @set_bits = source.set_bits
     @addr_bits = source.addr_bits
+    @strategy = source.strategy
   end
 
   def ==(other)
@@ -364,7 +489,8 @@ class Cache
         end
         s += "\t#{way.to_s}"
       end
-      if set.operator.is_a?(LRUPersAnalysisOperator)
+      #if set.operator.is_a?(LRUPersAnalysisOperator)
+      if set.is_a?(LRUPersCacheSet)
         def (set.removed).to_s
           removed = "{ "
           for x in self