Skip to content

Commit b24dd7b

Browse files
committed
Create initial effect system.
1 parent b8a7988 commit b24dd7b

5 files changed

Lines changed: 809 additions & 0 deletions

File tree

zjit/src/hir.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::{
1212
cell::RefCell, collections::{BTreeSet, HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, c_int, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter
1313
};
1414
use crate::hir_type::{Type, types};
15+
use crate::hir_effect::{Effect, effects};
1516
use crate::bitset::BitSet;
1617
use crate::profile::{TypeDistributionSummary, ProfiledType};
1718
use crate::stats::Counter;
@@ -2129,6 +2130,124 @@ impl Function {
21292130
}
21302131
}
21312132

2133+
// Unused variables should NOT be allowed. We temporarily allow this to create the skeleton
2134+
// structure for an effects system. Changes that specify refined effects should remove this
2135+
// unused variables attribute.
2136+
#[allow(unused_variables)]
2137+
fn effects_of(&self, insn: InsnId) -> Effect {
2138+
assert!(self.insns[insn.0].has_output());
2139+
match &self.insns[insn.0] {
2140+
Insn::Param => unimplemented!("params should not be present in block.insns"),
2141+
Insn::SetGlobal { .. } | Insn::Jump(_) | Insn::EntryPoint { .. }
2142+
| Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. } | Insn::Throw { .. }
2143+
| Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. }
2144+
| Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } | Insn::IncrCounter(_)
2145+
| Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::IncrCounterPtr { .. }
2146+
| Insn::SetInstanceVariable { .. } | Insn::StoreField { .. } | Insn::WriteBarrier { .. } =>
2147+
panic!("Cannot infer type of instruction with no output: {}. See Insn::has_output().", self.insns[insn.0]),
2148+
Insn::Const { val: Const::Value(val) } => effects::Any,
2149+
Insn::Const { val: Const::CBool(val) } => effects::Any,
2150+
Insn::Const { val: Const::CInt8(val) } => effects::Any,
2151+
Insn::Const { val: Const::CInt16(val) } => effects::Any,
2152+
Insn::Const { val: Const::CInt32(val) } => effects::Any,
2153+
Insn::Const { val: Const::CInt64(val) } => effects::Any,
2154+
Insn::Const { val: Const::CUInt8(val) } => effects::Any,
2155+
Insn::Const { val: Const::CUInt16(val) } => effects::Any,
2156+
Insn::Const { val: Const::CUInt32(val) } => effects::Any,
2157+
Insn::Const { val: Const::CUInt64(val) } => effects::Any,
2158+
Insn::Const { val: Const::CPtr(val) } => effects::Any,
2159+
Insn::Const { val: Const::CDouble(val) } => effects::Any,
2160+
Insn::Test { val } if self.type_of(*val).is_known_falsy() => effects::Any,
2161+
Insn::Test { val } if self.type_of(*val).is_known_truthy() => effects::Any,
2162+
Insn::Test { .. } => effects::Any,
2163+
Insn::IsNil { val } if self.is_a(*val, types::NilClass) => effects::Any,
2164+
Insn::IsNil { val } if !self.type_of(*val).could_be(types::NilClass) => effects::Any,
2165+
Insn::IsNil { .. } => effects::Any,
2166+
Insn::IsMethodCfunc { .. } => effects::Any,
2167+
Insn::IsBitEqual { .. } => effects::Any,
2168+
Insn::IsBitNotEqual { .. } => effects::Any,
2169+
Insn::BoxBool { .. } => effects::Any,
2170+
Insn::BoxFixnum { .. } => effects::Any,
2171+
Insn::UnboxFixnum { .. } => effects::Any,
2172+
Insn::StringCopy { .. } => effects::Any,
2173+
Insn::StringIntern { .. } => effects::Any,
2174+
Insn::StringConcat { .. } => effects::Any,
2175+
Insn::StringGetbyteFixnum { .. } => effects::Any,
2176+
Insn::StringSetbyteFixnum { .. } => effects::Any,
2177+
Insn::StringAppend { .. } => effects::Any,
2178+
Insn::ToRegexp { .. } => effects::Any,
2179+
Insn::NewArray { .. } => effects::Any,
2180+
Insn::ArrayDup { .. } => effects::Any,
2181+
Insn::ArrayArefFixnum { .. } => effects::Any,
2182+
Insn::ArrayPop { .. } => effects::Any,
2183+
Insn::ArrayLength { .. } => effects::Any,
2184+
Insn::HashAref { .. } => effects::Any,
2185+
Insn::NewHash { .. } => effects::Any,
2186+
Insn::HashDup { .. } => effects::Any,
2187+
Insn::NewRange { .. } => effects::Any,
2188+
Insn::NewRangeFixnum { .. } => effects::Any,
2189+
Insn::ObjectAlloc { .. } => effects::Any,
2190+
Insn::ObjectAllocClass { class, .. } => effects::Any,
2191+
&Insn::CCallWithFrame { return_type, .. } => effects::Any,
2192+
Insn::CCall { return_type, .. } => effects::Any,
2193+
&Insn::CCallVariadic { return_type, .. } => effects::Any,
2194+
Insn::GuardType { val, guard_type, .. } => effects::Any,
2195+
Insn::GuardTypeNot { .. } => effects::Any,
2196+
Insn::GuardBitEquals { val, expected, .. } => effects::Any,
2197+
Insn::GuardShape { val, .. } => effects::Any,
2198+
Insn::GuardNotFrozen { recv, .. } => effects::Any,
2199+
Insn::GuardLess { left, .. } => effects::Any,
2200+
Insn::GuardGreaterEq { left, .. } => effects::Any,
2201+
Insn::FixnumAdd { .. } => effects::Any,
2202+
Insn::FixnumSub { .. } => effects::Any,
2203+
Insn::FixnumMult { .. } => effects::Any,
2204+
Insn::FixnumDiv { .. } => effects::Any,
2205+
Insn::FixnumMod { .. } => effects::Any,
2206+
Insn::FixnumEq { .. } => effects::Any,
2207+
Insn::FixnumNeq { .. } => effects::Any,
2208+
Insn::FixnumLt { .. } => effects::Any,
2209+
Insn::FixnumLe { .. } => effects::Any,
2210+
Insn::FixnumGt { .. } => effects::Any,
2211+
Insn::FixnumGe { .. } => effects::Any,
2212+
Insn::FixnumAnd { .. } => effects::Any,
2213+
Insn::FixnumOr { .. } => effects::Any,
2214+
Insn::FixnumXor { .. } => effects::Any,
2215+
Insn::PutSpecialObject { .. } => effects::Any,
2216+
Insn::SendWithoutBlock { .. } => effects::Any,
2217+
Insn::SendWithoutBlockDirect { .. } => effects::Any,
2218+
Insn::Send { .. } => effects::Any,
2219+
Insn::SendForward { .. } => effects::Any,
2220+
Insn::InvokeSuper { .. } => effects::Any,
2221+
Insn::InvokeBlock { .. } => effects::Any,
2222+
Insn::InvokeBuiltin { return_type, .. } => effects::Any,
2223+
Insn::Defined { pushval, .. } => effects::Any,
2224+
Insn::DefinedIvar { pushval, .. } => effects::Any,
2225+
Insn::GetConstantPath { .. } => effects::Any,
2226+
Insn::IsBlockGiven => effects::Any,
2227+
Insn::FixnumBitCheck { .. } => effects::Any,
2228+
Insn::ArrayMax { .. } => effects::Any,
2229+
Insn::ArrayInclude { .. } => effects::Any,
2230+
Insn::DupArrayInclude { .. } => effects::Any,
2231+
Insn::GetGlobal { .. } => effects::Any,
2232+
Insn::GetIvar { .. } => effects::Any,
2233+
Insn::LoadPC => effects::Any,
2234+
Insn::LoadSelf => effects::Any,
2235+
&Insn::LoadField { return_type, .. } => effects::Any,
2236+
Insn::GetSpecialSymbol { .. } => effects::Any,
2237+
Insn::GetSpecialNumber { .. } => effects::Any,
2238+
Insn::GetClassVar { .. } => effects::Any,
2239+
Insn::ToNewArray { .. } => effects::Any,
2240+
Insn::ToArray { .. } => effects::Any,
2241+
Insn::ObjToString { .. } => effects::Any,
2242+
Insn::AnyToString { .. } => effects::Any,
2243+
Insn::GetLocal { rest_param: true, .. } => effects::Any,
2244+
Insn::GetLocal { .. } => effects::Any,
2245+
// The type of Snapshot doesn't really matter; it's never materialized. It's used only
2246+
// as a reference for FrameState, which we use to generate side-exit code.
2247+
Insn::Snapshot { .. } => effects::Any,
2248+
}
2249+
}
2250+
21322251
/// Set self.param_types. They are copied to the param types of jit_entry_blocks.
21332252
fn set_param_types(&mut self) {
21342253
let iseq = self.iseq;
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Generate hir_effect.inc.rs. To do this, we build up a DAG that
2+
# represents a slice of the ZJIT effect hierarchy.
3+
4+
require 'set'
5+
6+
# Type represents not just a Ruby class but a named union of other types.
7+
class Type
8+
attr_accessor :name, :subtypes
9+
10+
def initialize name, subtypes=nil
11+
@name = name
12+
@subtypes = subtypes || []
13+
end
14+
15+
def all_subtypes
16+
subtypes.flat_map { |subtype| subtype.all_subtypes } + subtypes
17+
end
18+
19+
def subtype name
20+
result = Type.new name
21+
@subtypes << result
22+
result
23+
end
24+
end
25+
26+
# Helper to generate graphviz.
27+
def to_graphviz_rec type
28+
type.subtypes.each {|subtype|
29+
puts type.name + "->" + subtype.name + ";"
30+
}
31+
type.subtypes.each {|subtype|
32+
to_graphviz_rec subtype
33+
}
34+
end
35+
36+
# Generate graphviz.
37+
def to_graphviz type
38+
puts "digraph G {"
39+
to_graphviz_rec type
40+
puts "}"
41+
end
42+
43+
# ===== Start generating the type DAG =====
44+
45+
# Start at Any. All types are subtypes of Any.
46+
any = Type.new "Any"
47+
# Build the effect universe.
48+
frame = any.subtype "Frame"
49+
world = any.subtype "World"
50+
pc = frame.subtype "PC"
51+
locals = frame.subtype "Locals"
52+
stack = frame.subtype "Stack"
53+
54+
# Define a new type that can be subclassed (most of them).
55+
# If c_name is given, mark the rb_cXYZ object as equivalent to this exact type.
56+
def base_type name, c_name: nil
57+
type = $object.subtype name
58+
exact = type.subtype(name+"Exact")
59+
subclass = type.subtype(name+"Subclass")
60+
if c_name
61+
$exact_c_names[exact.name] = c_name
62+
$inexact_c_names[subclass.name] = c_name
63+
end
64+
$builtin_exact << exact.name
65+
$subclass << subclass.name
66+
[type, exact]
67+
end
68+
69+
# Define a new type that cannot be subclassed.
70+
# If c_name is given, mark the rb_cXYZ object as equivalent to this type.
71+
def final_type name, base: $object, c_name: nil
72+
if c_name
73+
$exact_c_names[name] = c_name
74+
end
75+
type = base.subtype name
76+
$builtin_exact << type.name
77+
type
78+
end
79+
80+
# Assign individual bits to type leaves and union bit patterns to nodes with subtypes
81+
num_bits = 0
82+
$bits = {"None" => ["0u64"]}
83+
$numeric_bits = {"None" => 0}
84+
Set[any, *any.all_subtypes].sort_by(&:name).each {|type|
85+
subtypes = type.subtypes
86+
if subtypes.empty?
87+
# Assign bits for leaves
88+
$bits[type.name] = ["1u64 << #{num_bits}"]
89+
$numeric_bits[type.name] = 1 << num_bits
90+
num_bits += 1
91+
else
92+
# Assign bits for unions
93+
$bits[type.name] = subtypes.map(&:name).sort
94+
end
95+
}
96+
[*any.all_subtypes, any].each {|type|
97+
subtypes = type.subtypes
98+
unless subtypes.empty?
99+
$numeric_bits[type.name] = subtypes.map {|ty| $numeric_bits[ty.name]}.reduce(&:|)
100+
end
101+
}
102+
103+
# Unions are for names of groups of type bit patterns that don't fit neatly
104+
# into the Ruby class hierarchy. For example, we might want to refer to a union
105+
# of TrueClassExact|FalseClassExact by the name BoolExact even though a "bool"
106+
# doesn't exist as a class in Ruby.
107+
def add_union name, type_names
108+
type_names = type_names.sort
109+
$bits[name] = type_names
110+
$numeric_bits[name] = type_names.map {|type_name| $numeric_bits[type_name]}.reduce(&:|)
111+
end
112+
113+
# ===== Finished generating the DAG; write Rust code =====
114+
115+
puts "// This file is @generated by src/hir/gen_hir_effect.rb."
116+
puts "mod bits {"
117+
$bits.keys.sort.map {|type_name|
118+
subtypes = $bits[type_name].join(" | ")
119+
puts " pub const #{type_name}: u64 = #{subtypes};"
120+
}
121+
puts " pub const AllBitPatterns: [(&str, u64); #{$bits.size}] = ["
122+
# Sort the bit patterns by decreasing value so that we can print the densest
123+
# possible to-string representation of a Type. For example, CSigned instead of
124+
# CInt8|CInt16|...
125+
$numeric_bits.sort_by {|key, val| -val}.each {|type_name, _|
126+
puts " (\"#{type_name}\", #{type_name}),"
127+
}
128+
puts " ];"
129+
puts " pub const NumEffectBits: u64 = #{num_bits};
130+
}"
131+
132+
puts "pub mod effects {
133+
use super::*;"
134+
$bits.keys.sort.map {|type_name|
135+
puts " pub const #{type_name}: Effect = Effect::from_bits(bits::#{type_name});"
136+
}
137+
puts "}"

zjit/src/hir_effect/hir_effect.inc.rs

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)