Class: RubyVM::RJIT::Context
- Inherits:
-
Struct
- Object
- Struct
- RubyVM::RJIT::Context
- Defined in:
- lib/ruby_vm/rjit/context.rb
Instance Attribute Summary collapse
-
#chain_depth ⇒ Object
Returns the value of attribute chain_depth.
-
#local_types ⇒ Object
Returns the value of attribute local_types.
-
#self_type ⇒ Object
Returns the value of attribute self_type.
-
#sp_offset ⇒ Object
Returns the value of attribute sp_offset.
-
#stack_size ⇒ Object
Returns the value of attribute stack_size.
-
#temp_mapping ⇒ Object
Returns the value of attribute temp_mapping.
-
#temp_types ⇒ Object
Returns the value of attribute temp_types.
Instance Method Summary collapse
-
#clear_local_types ⇒ Object
Erase local variable type information eg: because of a call we can’t track.
-
#diff(dst) ⇒ Object
Compute a difference score for two context objects.
-
#dup ⇒ Object
Deep dup by default for safety.
-
#get_local_type(idx) ⇒ Object
Get the currently tracked type for a local variable.
-
#get_opnd_mapping(opnd) ⇒ Object
Get both the type and mapping (where the value originates) of an operand.
-
#get_opnd_type(opnd) ⇒ Object
Get the type of an instruction operand.
-
#initialize(stack_size: 0, sp_offset: 0, chain_depth: 0, local_types: [Type::Unknown] * MAX_LOCAL_TYPES, temp_types: [Type::Unknown] * MAX_TEMP_TYPES, self_type: Type::Unknown, temp_mapping: [MapToStack] * MAX_TEMP_TYPES) ⇒ Context
constructor
A new instance of Context.
-
#set_local_type(local_idx, local_type) ⇒ Object
Set the type of a local variable.
-
#set_opnd_mapping(opnd, mapping_opnd_type) ⇒ Object
Overwrite both the type and mapping of a stack operand.
- #shift_stack(argc) ⇒ Object
- #sp_opnd(offset_bytes = 0) ⇒ Object
- #stack_opnd(depth_from_top) ⇒ Object
-
#stack_pop(n = 1) ⇒ Object
Pop N values off the stack Return a pointer to the stack top before the pop operation.
-
#stack_push(val_type) ⇒ Object
Push one new value on the temp stack Return a pointer to the new stack top.
-
#stack_push_local(local_idx) ⇒ Object
Push a local variable on the stack.
-
#stack_push_mapping(mapping_temp_type) ⇒ Object
Push one new value on the temp stack with an explicit mapping Return a pointer to the new stack top.
-
#stack_push_self ⇒ Object
Push the self value on the stack.
-
#upgrade_opnd_type(opnd, opnd_type) ⇒ Object
Upgrade (or “learn”) the type of an instruction operand This value must be compatible and at least as specific as the previously known type.
-
#with_stack_size(stack_size) ⇒ Object
Create a new Context instance with a given stack_size and sp_offset adjusted accordingly.
Constructor Details
#initialize(stack_size: 0, sp_offset: 0, chain_depth: 0, local_types: [Type::Unknown] * MAX_LOCAL_TYPES, temp_types: [Type::Unknown] * MAX_TEMP_TYPES, self_type: Type::Unknown, temp_mapping: [MapToStack] * MAX_TEMP_TYPES) ⇒ Context
Returns a new instance of Context.
26 27 28 29 30 31 32 33 34 |
# File 'lib/ruby_vm/rjit/context.rb', line 26 def initialize( stack_size: 0, sp_offset: 0, chain_depth: 0, local_types: [Type::Unknown] * MAX_LOCAL_TYPES, temp_types: [Type::Unknown] * MAX_TEMP_TYPES, self_type: Type::Unknown, temp_mapping: [MapToStack] * MAX_TEMP_TYPES ) = super |
Instance Attribute Details
#chain_depth ⇒ Object
Returns the value of attribute chain_depth
17 18 19 |
# File 'lib/ruby_vm/rjit/context.rb', line 17 def chain_depth @chain_depth end |
#local_types ⇒ Object
Returns the value of attribute local_types
17 18 19 |
# File 'lib/ruby_vm/rjit/context.rb', line 17 def local_types @local_types end |
#self_type ⇒ Object
Returns the value of attribute self_type
17 18 19 |
# File 'lib/ruby_vm/rjit/context.rb', line 17 def self_type @self_type end |
#sp_offset ⇒ Object
Returns the value of attribute sp_offset
17 18 19 |
# File 'lib/ruby_vm/rjit/context.rb', line 17 def sp_offset @sp_offset end |
#stack_size ⇒ Object
Returns the value of attribute stack_size
17 18 19 |
# File 'lib/ruby_vm/rjit/context.rb', line 17 def stack_size @stack_size end |
#temp_mapping ⇒ Object
Returns the value of attribute temp_mapping
17 18 19 |
# File 'lib/ruby_vm/rjit/context.rb', line 17 def temp_mapping @temp_mapping end |
#temp_types ⇒ Object
Returns the value of attribute temp_types
17 18 19 |
# File 'lib/ruby_vm/rjit/context.rb', line 17 def temp_types @temp_types end |
Instance Method Details
#clear_local_types ⇒ Object
Erase local variable type information eg: because of a call we can’t track
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/ruby_vm/rjit/context.rb', line 282 def clear_local_types # When clearing local types we must detach any stack mappings to those # locals. Even if local values may have changed, stack values will not. MAX_TEMP_TYPES.times do |stack_idx| case self.temp_mapping[stack_idx] in MapToStack # noop in MapToSelf # noop in MapToLocal[local_idx] self.temp_types[stack_idx] = self.local_types[local_idx] self.temp_mapping[stack_idx] = MapToStack end end # Clear the local types self.local_types = [Type::Unknown] * MAX_LOCAL_TYPES end |
#diff(dst) ⇒ Object
Compute a difference score for two context objects
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/ruby_vm/rjit/context.rb', line 302 def diff(dst) # Self is the source context (at the end of the predecessor) src = self # Can only lookup the first version in the chain if dst.chain_depth != 0 return TypeDiff::Incompatible end # Blocks with depth > 0 always produce new versions # Sidechains cannot overlap if src.chain_depth != 0 return TypeDiff::Incompatible end if dst.stack_size != src.stack_size return TypeDiff::Incompatible end if dst.sp_offset != src.sp_offset return TypeDiff::Incompatible end # Difference sum diff = 0 # Check the type of self diff += case src.self_type.diff(dst.self_type) in TypeDiff::Compatible[diff] then diff in TypeDiff::Incompatible then return TypeDiff::Incompatible end # For each local type we track src.local_types.size.times do |i| t_src = src.local_types[i] t_dst = dst.local_types[i] diff += case t_src.diff(t_dst) in TypeDiff::Compatible[diff] then diff in TypeDiff::Incompatible then return TypeDiff::Incompatible end end # For each value on the temp stack src.stack_size.times do |i| src_mapping, src_type = src.get_opnd_mapping(StackOpnd[i]) dst_mapping, dst_type = dst.get_opnd_mapping(StackOpnd[i]) # If the two mappings aren't the same if src_mapping != dst_mapping if dst_mapping == MapToStack # We can safely drop information about the source of the temp # stack operand. diff += 1 else return TypeDiff::Incompatible end end diff += case src_type.diff(dst_type) in TypeDiff::Compatible[diff] then diff in TypeDiff::Incompatible then return TypeDiff::Incompatible end end return TypeDiff::Compatible[diff] end |
#dup ⇒ Object
Deep dup by default for safety
37 38 39 40 41 42 43 |
# File 'lib/ruby_vm/rjit/context.rb', line 37 def dup ctx = super ctx.local_types = ctx.local_types.dup ctx.temp_types = ctx.temp_types.dup ctx.temp_mapping = ctx.temp_mapping.dup ctx end |
#get_local_type(idx) ⇒ Object
Get the currently tracked type for a local variable
173 174 175 |
# File 'lib/ruby_vm/rjit/context.rb', line 173 def get_local_type(idx) self.local_types[idx] || Type::Unknown end |
#get_opnd_mapping(opnd) ⇒ Object
Get both the type and mapping (where the value originates) of an operand. This is can be used with stack_push_mapping or set_opnd_mapping to copy a stack value’s type while maintaining the mapping.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/ruby_vm/rjit/context.rb', line 211 def get_opnd_mapping(opnd) opnd_type = self.get_opnd_type(opnd) case opnd in SelfOpnd return [MapToSelf, opnd_type] in StackOpnd[idx] assert(idx < self.stack_size) stack_idx = self.stack_size - 1 - idx if stack_idx < MAX_TEMP_TYPES return [self.temp_mapping[stack_idx], opnd_type] else # We can't know the source of this stack operand, so we assume it is # a stack-only temporary. type will be UNKNOWN assert(opnd_type == Type::Unknown) return [MapToStack, opnd_type] end end end |
#get_opnd_type(opnd) ⇒ Object
Get the type of an instruction operand
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/ruby_vm/rjit/context.rb', line 145 def get_opnd_type(opnd) case opnd in SelfOpnd self.self_type in StackOpnd[idx] assert(idx < self.stack_size) stack_idx = self.stack_size - 1 - idx # If outside of tracked range, do nothing if stack_idx >= MAX_TEMP_TYPES return Type::Unknown end mapping = self.temp_mapping[stack_idx] case mapping in MapToSelf self.self_type in MapToStack self.temp_types[self.stack_size - 1 - idx] in MapToLocal[idx] assert(idx < MAX_LOCAL_TYPES) self.local_types[idx] end end end |
#set_local_type(local_idx, local_type) ⇒ Object
Set the type of a local variable
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/ruby_vm/rjit/context.rb', line 255 def set_local_type(local_idx, local_type) if local_idx >= MAX_LOCAL_TYPES return end # If any values on the stack map to this local we must detach them MAX_TEMP_TYPES.times do |stack_idx| case self.temp_mapping[stack_idx] in MapToStack # noop in MapToSelf # noop in MapToLocal[idx] if idx == local_idx self.temp_types[stack_idx] = self.local_types[idx] self.temp_mapping[stack_idx] = MapToStack else # noop end end end self.local_types[local_idx] = local_type end |
#set_opnd_mapping(opnd, mapping_opnd_type) ⇒ Object
Overwrite both the type and mapping of a stack operand.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/ruby_vm/rjit/context.rb', line 233 def set_opnd_mapping(opnd, mapping_opnd_type) case opnd in SelfOpnd raise 'self always maps to self' in StackOpnd[idx] assert(idx < self.stack_size) stack_idx = self.stack_size - 1 - idx # If outside of tracked range, do nothing if stack_idx >= MAX_TEMP_TYPES return end mapping, opnd_type = mapping_opnd_type self.temp_mapping[stack_idx] = mapping # Only used when mapping == MAP_STACK self.temp_types[stack_idx] = opnd_type end end |
#shift_stack(argc) ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/ruby_vm/rjit/context.rb', line 130 def shift_stack(argc) assert(argc < self.stack_size) method_name_index = self.stack_size - argc - 1 (method_name_index...(self.stack_size - 1)).each do |i| if i + 1 < MAX_TEMP_TYPES self.temp_types[i] = self.temp_types[i + 1] self.temp_mapping[i] = self.temp_mapping[i + 1] end end self.stack_pop(1) end |
#sp_opnd(offset_bytes = 0) ⇒ Object
59 60 61 |
# File 'lib/ruby_vm/rjit/context.rb', line 59 def sp_opnd(offset_bytes = 0) [SP, (C.VALUE.size * self.sp_offset) + offset_bytes] end |
#stack_opnd(depth_from_top) ⇒ Object
55 56 57 |
# File 'lib/ruby_vm/rjit/context.rb', line 55 def stack_opnd(depth_from_top) [SP, C.VALUE.size * (self.sp_offset - 1 - depth_from_top)] end |
#stack_pop(n = 1) ⇒ Object
Pop N values off the stack Return a pointer to the stack top before the pop operation
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/ruby_vm/rjit/context.rb', line 109 def stack_pop(n = 1) assert(n <= self.stack_size) top = self.stack_opnd(0) # Clear the types of the popped values n.times do |i| idx = self.stack_size - i - 1 if idx < MAX_TEMP_TYPES self.temp_types[idx] = Type::Unknown self.temp_mapping[idx] = MapToStack end end self.stack_size -= n self.sp_offset -= n return top end |
#stack_push(val_type) ⇒ Object
Push one new value on the temp stack Return a pointer to the new stack top
89 90 91 |
# File 'lib/ruby_vm/rjit/context.rb', line 89 def stack_push(val_type) return self.stack_push_mapping([MapToStack, val_type]) end |
#stack_push_local(local_idx) ⇒ Object
Push a local variable on the stack
99 100 101 102 103 104 105 |
# File 'lib/ruby_vm/rjit/context.rb', line 99 def stack_push_local(local_idx) if local_idx >= MAX_LOCAL_TYPES return self.stack_push(Type::Unknown) end return self.stack_push_mapping([MapToLocal[local_idx], Type::Unknown]) end |
#stack_push_mapping(mapping_temp_type) ⇒ Object
Push one new value on the temp stack with an explicit mapping Return a pointer to the new stack top
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/ruby_vm/rjit/context.rb', line 65 def stack_push_mapping(mapping_temp_type) stack_size = self.stack_size # Keep track of the type and mapping of the value if stack_size < MAX_TEMP_TYPES mapping, temp_type = mapping_temp_type self.temp_mapping[stack_size] = mapping self.temp_types[stack_size] = temp_type case mapping in MapToLocal[idx] assert(idx < MAX_LOCAL_TYPES) else end end self.stack_size += 1 self.sp_offset += 1 return self.stack_opnd(0) end |
#stack_push_self ⇒ Object
Push the self value on the stack
94 95 96 |
# File 'lib/ruby_vm/rjit/context.rb', line 94 def stack_push_self return self.stack_push_mapping([MapToStack, Type::Unknown]) end |
#upgrade_opnd_type(opnd, opnd_type) ⇒ Object
Upgrade (or “learn”) the type of an instruction operand This value must be compatible and at least as specific as the previously known type. If this value originated from self, or an lvar, the learned type will be propagated back to its source.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/ruby_vm/rjit/context.rb', line 181 def upgrade_opnd_type(opnd, opnd_type) case opnd in SelfOpnd self.self_type = self.self_type.upgrade(opnd_type) in StackOpnd[idx] assert(idx < self.stack_size) stack_idx = self.stack_size - 1 - idx # If outside of tracked range, do nothing if stack_idx >= MAX_TEMP_TYPES return end mapping = self.temp_mapping[stack_idx] case mapping in MapToSelf self.self_type = self.self_type.upgrade(opnd_type) in MapToStack self.temp_types[stack_idx] = self.temp_types[stack_idx].upgrade(opnd_type) in MapToLocal[idx] assert(idx < MAX_LOCAL_TYPES) self.local_types[idx] = self.local_types[idx].upgrade(opnd_type) end end end |
#with_stack_size(stack_size) ⇒ Object
Create a new Context instance with a given stack_size and sp_offset adjusted accordingly. This is useful when you want to virtually rewind a stack_size for generating a side exit while considering past sp_offset changes on gen_save_sp.
48 49 50 51 52 53 |
# File 'lib/ruby_vm/rjit/context.rb', line 48 def with_stack_size(stack_size) ctx = self.dup ctx.sp_offset -= ctx.stack_size - stack_size ctx.stack_size = stack_size ctx end |