Class: OrigenTesters::ATP::Processors::FlagOptimizer

Inherits:
OrigenTesters::ATP::Processor show all
Defined in:
lib/origen_testers/atp/processors/flag_optimizer.rb

Overview

This processor eliminates the use of run flags between adjacent tests:

s(:flow,
  s(:name, "prb1"),
  s(:test,
    s(:name, "test1"),
    s(:id, "t1"),
    s(:on_fail,
      s(:set_flag, "t1_FAILED", "auto_generated"),
      s(:continue))),
  s(:if_flag, "t1_FAILED",
    s(:test,
      s(:name, "test2"))))

s(:flow,
  s(:name, "prb1"),
  s(:test,
    s(:name, "test1"),
    s(:id, "t1"),
    s(:on_fail,
      s(:test,
        s(:name, "test2")))))

Defined Under Namespace

Classes: ExtractRunFlagTable

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from OrigenTesters::ATP::Processor

#clean_flag, #extract_volatiles, #handler_missing, #process, #process_all, #volatile?, #volatile_flags

Instance Attribute Details

#optimize_when_continueObject (readonly)

Returns the value of attribute optimize_when_continue.


28
29
30
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 28

def optimize_when_continue
  @optimize_when_continue
end

#run_flag_tableObject (readonly)

Returns the value of attribute run_flag_table.


28
29
30
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 28

def run_flag_table
  @run_flag_table
end

Instance Method Details

#can_be_combined?(node1, node2) ⇒ Boolean

Returns:

  • (Boolean)

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 143

def can_be_combined?(node1, node2)
  if (node1.type == :test || node1.type == :sub_flow) && (node2.type == :if_flag || node2.type == :unless_flag) &&
     # Don't optimize tests which are marked as continue if told not to
     !(node1.find(:on_fail) && node1.find(:on_fail).find(:continue) && !optimize_when_continue)

    if node1.find_all(:on_fail, :on_pass).any? do |node|
      if n = node.find(:set_flag)
        # Inline instead of setting a flag if...
        gated_by_set?(n.to_a[0], node2) && # The flag set by node1 is gating node2
        n.to_a[1] == 'auto_generated' && # The flag has been generated and not specified by the user
        n.to_a[0] !~ /_RAN$/ && # And don't compress RAN flags because they can be set by both on_fail and on_pass
        !volatile?(n.to_a[0]) # And make sure the flag has not been marked as volatile
      end
    end
      return true
    end
  end
  false
end

#combine(node1, node2) ⇒ Object


163
164
165
166
167
168
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 163

def combine(node1, node2)
  nodes_to_inline_on_pass_or_fail << node2 # .updated(nil, process_all(node2.children))
  node1 = node1.updated(nil, process_all(node1.children))
  nodes_to_inline_on_pass_or_fail.pop
  node1
end

#gated_by_set?(flag, node) ⇒ Boolean

node will always be an if_flag or unless_flag type node, guaranteed by the caller

Returns true if flag matches the one supplied

s(:if_flag, flag,
  s(:test, ...

Also returns true if flag matches the one supplied, but it is nested within other flag conditions:

s(:unless_flag, other_flag,
  s(:if_flag, other_flag2,
    s(:if_flag, flag,
      s(:test, ...

Returns:

  • (Boolean)

183
184
185
186
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 183

def gated_by_set?(flag, node)
  (flag == node.to_a[0] && node.type == :if_flag) ||
    (node.to_a.size == 2 && (node.to_a.last.type == :if_flag || node.to_a.last.type == :unless_flag) && gated_by_set?(flag, node.to_a.last))
end

#if_run_flag_to_removeObject


225
226
227
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 225

def if_run_flag_to_remove
  @if_run_flag_to_remove ||= []
end

#nodes_to_inline_on_pass_or_failObject


229
230
231
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 229

def nodes_to_inline_on_pass_or_fail
  @nodes_to_inline_on_pass_or_fail ||= []
end

#on_if_flag(node) ⇒ Object


90
91
92
93
94
95
96
97
98
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 90

def on_if_flag(node)
  name, *nodes = *node
  # Remove this node and return its children if required
  if if_run_flag_to_remove.last == node.to_a[0]
    node.updated(:inline, optimize(process_all(node.to_a[1..-1])))
  else
    node.updated(nil, [name] + optimize(process_all(nodes)))
  end
end

#on_named_collection(node) ⇒ Object Also known as: on_flow, on_group, on_unless_flag, on_sub_flow


69
70
71
72
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 69

def on_named_collection(node)
  name, *nodes = *node
  node.updated(nil, [name] + optimize(process_all(nodes)))
end

#on_on_fail(node) ⇒ Object Also known as: on_on_pass


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 100

def on_on_fail(node)
  if to_inline = nodes_to_inline_on_pass_or_fail.last
    # If this node sets the flag that gates the node to be inlined
    set_flag = node.find(:set_flag)
    if set_flag && gated_by_set?(set_flag.to_a[0], to_inline)
      # Remove the sub-node that sets the flag if there are no further references to it

      if @run_flag_table[set_flag.to_a[0]] == 1 || !@run_flag_table[set_flag.to_a[0]]
        node = node.updated(nil, node.children - [set_flag])
      end

      # And append the content of the node to be in_lined at the end of this on pass/fail node
      append = reorder_nested_run_flags(set_flag.to_a[0], to_inline).to_a[1..-1]

      # Belt and braces approach to make sure this node to be inlined does
      # not get picked up anywhere else
      nodes_to_inline_on_pass_or_fail.pop
      nodes_to_inline_on_pass_or_fail << nil
    end
  end
  node.updated(nil, optimize(process_all(node.children + Array(append))))
end

#on_unnamed_collection(node) ⇒ Object Also known as: on_else


78
79
80
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 78

def on_unnamed_collection(node)
  node.updated(nil, optimize(process_all(node.children)))
end

#on_whenever(node) ⇒ Object Also known as: on_whenever_all, on_whenever_any


83
84
85
86
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 83

def on_whenever(node)
  name, *nodes = *node
  node.updated(nil, [name] + optimize(process_all(nodes)))
end

#optimize(nodes) ⇒ Object


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 124

def optimize(nodes)
  results = []
  node1 = nil
  nodes.each do |node2|
    if node1
      if can_be_combined?(node1, node2)
        node1 = combine(node1, node2)
      else
        results << node1
        node1 = node2
      end
    else
      node1 = node2
    end
  end
  results << node1 if node1
  results
end

#reorder_nested_run_flags(flag, node) ⇒ Object

Returns the node with the run_flag clauses re-ordered to have the given flag of interest at the top.

The caller guarantees the run_flag clause containing the given flag is present.

For example, given this node:

s(:unless_flag, "flag1",
  s(:if_flag, "ot_BEA7F3B_FAILED",
    s(:test,
      s(:object, <TestSuite: inner_test1_BEA7F3B>),
      s(:name, "inner_test1_BEA7F3B"),
      s(:number, 0),
      s(:id, "it1_BEA7F3B"),
      s(:on_fail,
        s(:render, "multi_bin;")))))

Then this node would be returned when the flag of interest is ot_BEA7F3B_FAILED:

s(:if_flag, "ot_BEA7F3B_FAILED",
  s(:unless_flag, "flag1",
    s(:test,
      s(:object, <TestSuite: inner_test1_BEA7F3B>),
      s(:name, "inner_test1_BEA7F3B"),
      s(:number, 0),
      s(:id, "it1_BEA7F3B"),
      s(:on_fail,
        s(:render, "multi_bin;")))))

215
216
217
218
219
220
221
222
223
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 215

def reorder_nested_run_flags(flag, node)
  # If the run_flag we care about is already at the top, just return node
  unless node.to_a[0] == flag && node.type == :if_flag
    if_run_flag_to_remove << flag
    node = node.updated(:if_flag, [flag] + [process(node)])
    if_run_flag_to_remove.pop
  end
  node
end

#run(node, options = {}) ⇒ Object


56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/origen_testers/atp/processors/flag_optimizer.rb', line 56

def run(node, options = {})
  options = {
    optimize_when_continue: true
  }.merge(options)
  @optimize_when_continue = options[:optimize_when_continue]
  # Pre-process the AST for # of occurrences of each run-flag used
  t = ExtractRunFlagTable.new
  t.process(node)
  @run_flag_table = t.run_flag_table
  extract_volatiles(node)
  process(node)
end