summaryrefslogtreecommitdiff
path: root/lib/ruby_vm/rjit/type.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ruby_vm/rjit/type.rb')
-rw-r--r--lib/ruby_vm/rjit/type.rb221
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