Skip to content

Commit

Permalink
Distinguish between lattice refs that occur in rule bodies.
Browse files Browse the repository at this point in the history
If a lattice is referenced inside a rule body, we need to do extra
invalidation/rescan work. If a lattice is referenced at the top-level, the
existing code is sufficient.
  • Loading branch information
neilconway committed Aug 5, 2012
1 parent f527c6e commit fa54975
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 14 deletions.
2 changes: 1 addition & 1 deletion bin/budtimelines
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ include VizUtil

module Depends
state do
table :depends, [:rid, :lhs, :op, :rhs, :nm]
table :depends, [:rid, :lhs, :op, :rhs, :nm, :in_body]
end
end

Expand Down
17 changes: 8 additions & 9 deletions lib/bud.rb
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ def resolve_imports
qlname = "#{local_name}.#{imp_dep.lhs}"
qrname = "#{local_name}.#{imp_dep.body}"
self.t_depends << [imp_dep.bud_obj, imp_dep.rule_id, qlname,
imp_dep.op, qrname, imp_dep.nm]
imp_dep.op, qrname, imp_dep.nm, imp_dep.in_body]
end
mod_inst.t_provides.each do |imp_pro|
qintname = "#{local_name}.#{imp_pro.interface}"
Expand Down Expand Up @@ -516,15 +516,14 @@ def prepare_invalidation_scheme
# "t2 <= t1 {|t| [t.key, lat_foo]}", whenever there is a delta on lat_foo we
# should rescan t1 (to produce tuples with the updated lat_foo value).
# TODO:
# (1) support non-join ops to be rescanned (+ tests)
# (2) don't trigger rescan for rules that don't actually embed lattice
# values (top-level refs are okay) => tweak rewrite code?
# (3) if t1 is fed by rules r1 and r2 but only r1 references lattice x,
# (1) support non-join ops to be rescanned (+ tests) + lambdas
# (2) if t1 is fed by rules r1 and r2 but only r1 references lattice x,
# don't trigger rescan of r2 on deltas for x (hard)
t_depends.each do |dep|
if @lattices.has_key? dep.body.to_sym and @tables.has_key? dep.lhs.to_sym
src_lat = @lattices[dep.body.to_sym]
dst_tbl = @tables[dep.lhs.to_sym]
src, dst = dep.body.to_sym, dep.lhs.to_sym
if @lattices.has_key? src and @tables.has_key? dst and dep.in_body
src_lat = @lattices[src]
dst_tbl = @tables[dst]
dst_tbl.non_temporal_predecessors.each do |e|
src_lat.rescan_on_merge << e
end
Expand Down Expand Up @@ -1134,7 +1133,7 @@ def builtin_state

# for BUD reflection
table :t_rules, [:bud_obj, :rule_id] => [:lhs, :op, :src, :orig_src, :nm_funcs_called]
table :t_depends, [:bud_obj, :rule_id, :lhs, :op, :body] => [:nm]
table :t_depends, [:bud_obj, :rule_id, :lhs, :op, :body] => [:nm, :in_body]
table :t_provides, [:interface] => [:input]
table :t_underspecified, t_provides.schema
table :t_stratum, [:predicate] => [:stratum]
Expand Down
2 changes: 1 addition & 1 deletion lib/bud/bud_meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def stratify_preds
bud = @bud_instance.toplevel
nodes = {}
bud.t_depends.each do |d|
#t_depends [:bud_instance, :rule_id, :lhs, :op, :body] => [:nm]
#t_depends [:bud_instance, :rule_id, :lhs, :op, :body] => [:nm, :in_body]
lhs = (nodes[d.lhs] ||= Node.new(d.lhs, :init, 0, [], true, false, false, false))
lhs.in_lhs = true
body = (nodes[d.body] ||= Node.new(d.body, :init, 0, [], false, true, false, false))
Expand Down
2 changes: 1 addition & 1 deletion lib/bud/depanalysis.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class DepAnalysis #:nodoc: all

state do
# Data inserted by client, usually from t_depends and t_provides
scratch :depends, [:lhs, :op, :body, :neg]
scratch :depends, [:lhs, :op, :body, :neg, :in_body]
scratch :providing, [:pred, :input]

# Intermediate state
Expand Down
2 changes: 1 addition & 1 deletion lib/bud/graphs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def process(depends)
# its name is "CYC" + concat(sort(predicate names))
depends.each do |d|
# b/c bud_obj was pruned before serialization...
(bud_obj, rule_id, lhs, op, body, nm) = d.to_a
bud_obj, rule_id, lhs, op, body, nm, in_body = d.to_a
head = lhs
body = body

Expand Down
62 changes: 61 additions & 1 deletion lib/bud/rewrite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def initialize(seed, bud_instance)
@rules = []
@depends = []
@nm_funcs_called = false
@iter_stack = []
@refs_in_body = Set.new
super()
end

Expand Down Expand Up @@ -67,6 +69,61 @@ def call_to_id(exp)
return recv.nil? ? op.to_s : call_to_id(recv) + "." + op.to_s
end

# We want to distinguish between collection dependencies that occur in
# top-level expressions versus collections that are referenced inside rule
# bodies. We just want to set a flag when processing the :iter body, but
# annoyingly it seems that is hard to do without duplicating the
# implementation of process_iter().
#
# XXX: the whole RuleRewriter approach is wrong because it conflates
# converting ASTs to strings with doing analysis on ASTs. Those should be
# split into two separate passes.
def process_iter(exp)
iter = process exp.shift
args = exp.shift
args = (args == 0) ? '' : process(args)

@iter_stack.push(true)
body = exp.empty? ? nil : process(exp.shift)
@iter_stack.pop

do_process_iter(iter, args, body)
end

def do_process_iter(iter, args, body)
b, e = if iter == "END" then
[ "{", "}" ]
else
[ "do", "end" ]
end

iter.sub!(/\(\)$/, '')

# REFACTOR: ugh
result = []
result << "#{iter} {"
result << " |#{args}|" if args
if body then
result << " #{body.strip} "
else
result << ' '
end
result << "}"
result = result.join
return result if result !~ /\n/ and result.size < LINE_LENGTH

result = []
result << "#{iter} #{b}"
result << " |#{args}|" if args
result << "\n"
if body then
result << indent(body.strip)
result << "\n"
end
result << e
result.join
end

def process_call(exp)
recv, op, args = exp
if OP_LIST.include?(op) and @context[1] == :block and @context.length == 4
Expand All @@ -92,6 +149,7 @@ def process_call(exp)
ty, qn, _ = exp_id_type(recv, op, args) # qn = qualified name
if ty == :collection or ty == :lattice
(@tables[qn] = @nm if @collect) unless @tables[qn]
@refs_in_body << qn unless @iter_stack.empty?
#elsif ty == :import .. do nothing
elsif ty == :not_coll_id
# Check if receiver is a collection, and further if the current exp
Expand Down Expand Up @@ -167,6 +225,7 @@ def collect_rhs(exp)
end

def reset_instance_vars
@refs_in_body = Set.new
@tables = {}
@nm = false
@nm_funcs_called = false
Expand All @@ -184,7 +243,8 @@ def record_rule(lhs, op, rhs_pos, rhs)

@rules << [@bud_instance, @rule_indx, lhs, op, rule_txt, rule_txt_orig, @nm_funcs_called]
@tables.each_pair do |t, nm|
@depends << [@bud_instance, @rule_indx, lhs, op, t, nm]
in_rule_body = @refs_in_body.include? t
@depends << [@bud_instance, @rule_indx, lhs, op, t, nm, in_rule_body]
end

reset_instance_vars
Expand Down
4 changes: 4 additions & 0 deletions test/tc_lattice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,10 @@ class TestLatticeEmbedDeltas < MiniTest::Unit::TestCase
def test_join_deltas
i = LatticeEmbedJoin.new
%w[t1 t2 t3 m1 m2].each {|r| assert_equal(0, i.collection_stratum(r))}
depends = i.t_depends.map {|t| [t.lhs, t.body, t.in_body, t.nm]}.to_set
assert_equal([["t1", "t2", false, false], ["t1", "t3", false, false],
["t1", "m1", true, false], ["m1", "m2", false, false]].to_set,
depends)
i.t2 <+ [[5, 10]]
i.t3 <+ [[10, 20]]
i.m1 <+ Bud::MaxLattice.new(5)
Expand Down
2 changes: 2 additions & 0 deletions test/tc_meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ def test_paths
tally = 0
program.t_depends.each do |dep|
next if VizUtil.ma_tables.keys.include? dep.lhs.to_sym
assert_equal(false, dep.in_body) unless dep.lhs == "link2" and dep.body == "empty"
if dep.lhs == "shortest" and dep.body == "path"
assert(dep.nm, "NM rule")
tally += 1
Expand All @@ -156,6 +157,7 @@ def test_paths
tally += 1
elsif dep.lhs == "link2" and dep.body == "empty"
assert(dep.nm, "NM rule")
assert(dep.in_body)
tally += 1
elsif dep.lhs == "link3" and dep.body == "shortest"
assert(dep.nm, "NM rule")
Expand Down

0 comments on commit fa54975

Please sign in to comment.