diff options
Diffstat (limited to 'lib/ruby_vm/rjit/type.rb')
-rw-r--r-- | lib/ruby_vm/rjit/type.rb | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/lib/ruby_vm/rjit/type.rb b/lib/ruby_vm/rjit/type.rb new file mode 100644 index 0000000000..119692014b --- /dev/null +++ b/lib/ruby_vm/rjit/type.rb @@ -0,0 +1,221 @@ +module RubyVM::RJIT + # Represent the type of a value (local/stack/self) in RJIT + Type = Data.define(:type) do + # Check if the type is an immediate + def imm? + case self + in Type::UnknownImm then true + in Type::Nil then true + in Type::True then true + in Type::False then true + in Type::Fixnum then true + in Type::Flonum then true + in Type::ImmSymbol then true + else false + end + end + + # Returns true when the type is not specific. + def unknown? + case self + in Type::Unknown | Type::UnknownImm | Type::UnknownHeap then true + else false + end + end + + # Returns true when we know the VALUE is a specific handle type, + # such as a static symbol ([Type::ImmSymbol], i.e. true from RB_STATIC_SYM_P()). + # Opposite of [Self::is_unknown]. + def specific? + !self.unknown? + end + + # Check if the type is a heap object + def heap? + case self + in Type::UnknownHeap then true + in Type::TArray then true + in Type::Hash then true + in Type::HeapSymbol then true + in Type::TString then true + in Type::CString then true + in Type::BlockParamProxy then true + else false + end + end + + # Check if it's a T_ARRAY object + def array? + case self + in Type::TArray then true + else false + end + end + + # Check if it's a T_STRING object (both TString and CString are T_STRING) + def string? + case self + in Type::TString then true + in Type::CString then true + else false + end + end + + # Returns the class if it is known, otherwise nil + def known_class + case self + in Type::Nil then C.rb_cNilClass + in Type::True then C.rb_cTrueClass + in Type::False then C.rb_cFalseClass + in Type::Fixnum then C.rb_cInteger + in Type::Flonum then C.rb_cFloat + in Type::ImmSymbol | Type::HeapSymbol then C.rb_cSymbol + in Type::CString then C.rb_cString + else nil + end + end + + # Returns a boolean representing whether the value is truthy if known, otherwise nil + def known_truthy + case self + in Type::Nil then false + in Type::False then false + in Type::UnknownHeap then false + in Type::Unknown | Type::UnknownImm then nil + else true + end + end + + # Returns a boolean representing whether the value is equal to nil if known, otherwise nil + def known_nil + case [self, self.known_truthy] + in Type::Nil, _ then true + in Type::False, _ then false # Qfalse is not nil + in _, true then false # if truthy, can't be nil + in _, _ then nil # otherwise unknown + end + end + + def diff(dst) + # Perfect match, difference is zero + if self == dst + return TypeDiff::Compatible[0] + end + + # Any type can flow into an unknown type + if dst == Type::Unknown + return TypeDiff::Compatible[1] + end + + # A CString is also a TString. + if self == Type::CString && dst == Type::TString + return TypeDiff::Compatible[1] + end + + # Specific heap type into unknown heap type is imperfect but valid + if self.heap? && dst == Type::UnknownHeap + return TypeDiff::Compatible[1] + end + + # Specific immediate type into unknown immediate type is imperfect but valid + if self.imm? && dst == Type::UnknownImm + return TypeDiff::Compatible[1] + end + + # Incompatible types + return TypeDiff::Incompatible + end + + def upgrade(new_type) + assert(new_type.diff(self) != TypeDiff::Incompatible) + new_type + end + + private + + def assert(cond) + unless cond + raise "'#{cond.inspect}' was not true" + end + end + end + + # This returns an appropriate Type based on a known value + class << Type + def from(val) + if C::SPECIAL_CONST_P(val) + if fixnum?(val) + Type::Fixnum + elsif val.nil? + Type::Nil + elsif val == true + Type::True + elsif val == false + Type::False + elsif static_symbol?(val) + Type::ImmSymbol + elsif flonum?(val) + Type::Flonum + else + raise "Illegal value: #{val.inspect}" + end + else + val_class = C.to_value(C.rb_class_of(val)) + if val_class == C.rb_cString && C.rb_obj_frozen_p(val) + return Type::CString + end + if C.to_value(val) == C.rb_block_param_proxy + return Type::BlockParamProxy + end + case C::BUILTIN_TYPE(val) + in C::RUBY_T_ARRAY + Type::TArray + in C::RUBY_T_HASH + Type::Hash + in C::RUBY_T_STRING + Type::TString + else + Type::UnknownHeap + end + end + end + + private + + def fixnum?(obj) + (C.to_value(obj) & C::RUBY_FIXNUM_FLAG) == C::RUBY_FIXNUM_FLAG + end + + def flonum?(obj) + (C.to_value(obj) & C::RUBY_FLONUM_MASK) == C::RUBY_FLONUM_FLAG + end + + def static_symbol?(obj) + (C.to_value(obj) & 0xff) == C::RUBY_SYMBOL_FLAG + end + end + + # List of types + Type::Unknown = Type[:Unknown] + Type::UnknownImm = Type[:UnknownImm] + Type::UnknownHeap = Type[:UnknownHeap] + Type::Nil = Type[:Nil] + Type::True = Type[:True] + Type::False = Type[:False] + Type::Fixnum = Type[:Fixnum] + Type::Flonum = Type[:Flonum] + Type::Hash = Type[:Hash] + Type::ImmSymbol = Type[:ImmSymbol] + Type::HeapSymbol = Type[:HeapSymbol] + + Type::TString = Type[:TString] # An object with the T_STRING flag set, possibly an rb_cString + Type::CString = Type[:CString] # An un-subclassed string of type rb_cString (can have instance vars in some cases) + Type::TArray = Type[:TArray] # An object with the T_ARRAY flag set, possibly an rb_cArray + + Type::BlockParamProxy = Type[:BlockParamProxy] # A special sentinel value indicating the block parameter should be read from + + module TypeDiff + Compatible = Data.define(:diversion) # The smaller, the more compatible. + Incompatible = :Incompatible + end +end |