# Generate hir_effect.inc.rs. To do this, we build up a DAG that # represents the ZJIT effect hierarchy. require 'set' # Effect represents not just a Ruby class but a named union of other effects. class Effect attr_accessor :name, :subeffects def initialize name, subeffects=nil @name = name @subeffects = subeffects || [] end def all_subeffects subeffects.flat_map { |subeffect| subeffect.all_subeffects } + subeffects end def subeffect name result = Effect.new name @subeffects << result result end end # Helper to generate graphviz. def to_graphviz_rec effect effect.subeffects.each {|subeffect| puts effect.name + "->" + subeffect.name + ";" } effect.subeffect.each {|subeffect| to_graphviz_rec subeffect } end # Generate graphviz. def to_graphviz effect puts "digraph G {" to_graphviz_rec effect puts "}" end # ===== Start generating the effect DAG ===== # Start at Any. All effects are subeffects of Any. any = Effect.new 'Any' # Build the effect universe. allocator = any.subeffect 'Allocator' control = any.subeffect 'Control' memory = any.subeffect 'Memory' other = memory.subeffect 'Other' frame = memory.subeffect 'Frame' pc = frame.subeffect 'PC' locals = frame.subeffect 'Locals' stack = frame.subeffect 'Stack' # Use the smallest unsigned value needed to describe all effect bits # If it becomes an issue, this can be generated but for now we do it manually $int_label = 'u8' # Assign individual bits to effect leaves and union bit patterns to nodes with subeffects num_bits = 0 $bits = {"Empty" => ["0#{$int_label}"]} $numeric_bits = {"Empty" => 0} Set[any, *any.all_subeffects].sort_by(&:name).each {|effect| subeffects = effect.subeffects if subeffects.empty? # Assign bits for leaves $bits[effect.name] = ["1#{$int_label} << #{num_bits}"] $numeric_bits[effect.name] = 1 << num_bits num_bits += 1 else # Assign bits for unions $bits[effect.name] = subeffects.map(&:name).sort end } [*any.all_subeffects, any].each {|effect| subeffects = effect.subeffects unless subeffects.empty? $numeric_bits[effect.name] = subeffects.map {|ty| $numeric_bits[ty.name]}.reduce(&:|) end } # ===== Finished generating the DAG; write Rust code ===== puts "// This file is @generated by src/hir/gen_hir_effect.rb." puts "mod bits {" $bits.keys.sort.map {|effect_name| subeffects = $bits[effect_name].join(" | ") puts " pub const #{effect_name}: #{$int_label} = #{subeffects};" } puts " pub const AllBitPatterns: [(&str, #{$int_label}); #{$bits.size}] = [" # Sort the bit patterns by decreasing value so that we can print the densest # possible to-string representation of an Effect. For example, Frame instead of # PC|Stack|Locals $numeric_bits.sort_by {|key, val| -val}.each {|effect_name, _| puts " (\"#{effect_name}\", #{effect_name})," } puts " ];" puts " pub const NumEffectBits: #{$int_label} = #{num_bits}; }" puts "pub mod effect_types {" puts " pub type EffectBits = #{$int_label};" puts "}" puts "pub mod abstract_heaps { use super::*;" $bits.keys.sort.map {|effect_name| puts " pub const #{effect_name}: AbstractHeap = AbstractHeap::from_bits(bits::#{effect_name});" } puts "}" puts "pub mod effects { use super::*;" $bits.keys.sort.map {|effect_name| puts " pub const #{effect_name}: Effect = Effect::promote(abstract_heaps::#{effect_name});" } puts "}"