diff options
-rw-r--r-- | yjit/src/codegen.rs | 14 | ||||
-rw-r--r-- | yjit/src/core.rs | 110 |
2 files changed, 59 insertions, 65 deletions
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 4cd86ed91a..da87104ef6 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -292,7 +292,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) { let self_val_type = Type::from(self_val); // Verify self operand type - if self_val_type.diff(ctx.get_opnd_type(SelfOpnd)) == usize::MAX { + if self_val_type.diff(ctx.get_opnd_type(SelfOpnd)) == TypeDiff::Incompatible { panic!( "verify_ctx: ctx self type ({:?}) incompatible with actual value of self {}", ctx.get_opnd_type(SelfOpnd), @@ -333,7 +333,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) { } // If the actual type differs from the learned type - if val_type.diff(learned_type) == usize::MAX { + if val_type.diff(learned_type) == TypeDiff::Incompatible { panic!( "verify_ctx: ctx type ({:?}) incompatible with actual value on stack: {}", learned_type, @@ -350,7 +350,7 @@ fn verify_ctx(jit: &JITState, ctx: &Context) { let local_val = jit.peek_at_local(i as i32); let local_type = Type::from(local_val); - if local_type.diff(learned_type) == usize::MAX { + if local_type.diff(learned_type) == TypeDiff::Incompatible { panic!( "verify_ctx: ctx type ({:?}) incompatible with actual value of local: {} (type {:?})", learned_type, @@ -1314,7 +1314,7 @@ fn guard_object_is_heap( asm.cmp(object, Qfalse.into()); asm.je(side_exit); - if object_type.diff(Type::UnknownHeap) != usize::MAX { + if object_type.diff(Type::UnknownHeap) != TypeDiff::Incompatible { ctx.upgrade_opnd_type(object_opnd, Type::UnknownHeap); } } @@ -1347,7 +1347,7 @@ fn guard_object_is_array( asm.cmp(flags_opnd, (RUBY_T_ARRAY as u64).into()); asm.jne(side_exit); - if object_type.diff(Type::TArray) != usize::MAX { + if object_type.diff(Type::TArray) != TypeDiff::Incompatible { ctx.upgrade_opnd_type(object_opnd, Type::TArray); } } @@ -8066,7 +8066,7 @@ mod tests { asm.compile(&mut cb); assert_eq!(status, KeepCompiling); - assert_eq!(context.diff(&Context::default()), 0); + assert_eq!(context.diff(&Context::default()), TypeDiff::Compatible(0)); assert_eq!(cb.get_write_pos(), 0); } @@ -8078,7 +8078,7 @@ mod tests { let status = gen_pop(&mut jit, &mut context, &mut asm, &mut ocb); assert_eq!(status, KeepCompiling); - assert_eq!(context.diff(&Context::default()), 0); + assert_eq!(context.diff(&Context::default()), TypeDiff::Compatible(0)); } #[test] diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 513459d338..df884515d6 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -221,53 +221,58 @@ impl Type { } /// Compute a difference between two value types - /// Returns 0 if the two are the same - /// Returns > 0 if different but compatible - /// Returns usize::MAX if incompatible - pub fn diff(self, dst: Self) -> usize { + pub fn diff(self, dst: Self) -> TypeDiff { // Perfect match, difference is zero if self == dst { - return 0; + return TypeDiff::Compatible(0); } // Any type can flow into an unknown type if dst == Type::Unknown { - return 1; + return TypeDiff::Compatible(1); } // A CString is also a TString. if self == Type::CString && dst == Type::TString { - return 1; + return TypeDiff::Compatible(1); } // A CArray is also a TArray. if self == Type::CArray && dst == Type::TArray { - return 1; + return TypeDiff::Compatible(1); } // Specific heap type into unknown heap type is imperfect but valid if self.is_heap() && dst == Type::UnknownHeap { - return 1; + return TypeDiff::Compatible(1); } // Specific immediate type into unknown immediate type is imperfect but valid if self.is_imm() && dst == Type::UnknownImm { - return 1; + return TypeDiff::Compatible(1); } // Incompatible types - return usize::MAX; + return TypeDiff::Incompatible; } /// Upgrade this type into a more specific compatible type /// The new type must be compatible and at least as specific as the previously known type. fn upgrade(&mut self, src: Self) { // Here we're checking that src is more specific than self - assert!(src.diff(*self) != usize::MAX); + assert!(src.diff(*self) != TypeDiff::Incompatible); *self = src; } } +#[derive(Debug, Eq, PartialEq)] +pub enum TypeDiff { + // usize == 0: Same type + // usize >= 1: Different but compatible. The smaller, the more compatible. + Compatible(usize), + Incompatible, +} + // Potential mapping of a value on the temporary stack to // self, a local variable or constant so that we can track its type #[derive(Copy, Clone, Eq, PartialEq, Debug)] @@ -967,13 +972,14 @@ fn find_block_version(blockid: BlockId, ctx: &Context) -> Option<BlockRef> { // For each version matching the blockid for blockref in versions.iter_mut() { let block = blockref.borrow(); - let diff = ctx.diff(&block.ctx); - // Note that we always prefer the first matching // version found because of inline-cache chains - if diff < best_diff { - best_version = Some(blockref.clone()); - best_diff = diff; + match ctx.diff(&block.ctx) { + TypeDiff::Compatible(diff) if diff < best_diff => { + best_version = Some(blockref.clone()); + best_diff = diff; + } + _ => {} } } @@ -1005,7 +1011,7 @@ pub fn limit_block_versions(blockid: BlockId, ctx: &Context) -> Context { generic_ctx.sp_offset = ctx.sp_offset; debug_assert_ne!( - usize::MAX, + TypeDiff::Incompatible, ctx.diff(&generic_ctx), "should substitute a compatible context", ); @@ -1468,55 +1474,46 @@ impl Context { } /// Compute a difference score for two context objects - /// Returns 0 if the two contexts are the same - /// Returns > 0 if different but compatible - /// Returns usize::MAX if incompatible - pub fn diff(&self, dst: &Context) -> usize { + pub fn diff(&self, dst: &Context) -> TypeDiff { // Self is the source context (at the end of the predecessor) let src = self; // Can only lookup the first version in the chain if dst.chain_depth != 0 { - return usize::MAX; + return TypeDiff::Incompatible; } // Blocks with depth > 0 always produce new versions // Sidechains cannot overlap if src.chain_depth != 0 { - return usize::MAX; + return TypeDiff::Incompatible; } if dst.stack_size != src.stack_size { - return usize::MAX; + return TypeDiff::Incompatible; } if dst.sp_offset != src.sp_offset { - return usize::MAX; + return TypeDiff::Incompatible; } // Difference sum let mut diff = 0; // Check the type of self - let self_diff = src.self_type.diff(dst.self_type); - - if self_diff == usize::MAX { - return usize::MAX; - } - - diff += self_diff; + diff += match src.self_type.diff(dst.self_type) { + TypeDiff::Compatible(diff) => diff, + TypeDiff::Incompatible => return TypeDiff::Incompatible, + }; // For each local type we track for i in 0..src.local_types.len() { let t_src = src.local_types[i]; let t_dst = dst.local_types[i]; - let temp_diff = t_src.diff(t_dst); - - if temp_diff == usize::MAX { - return usize::MAX; - } - - diff += temp_diff; + diff += match t_src.diff(t_dst) { + TypeDiff::Compatible(diff) => diff, + TypeDiff::Incompatible => return TypeDiff::Incompatible, + }; } // For each value on the temp stack @@ -1531,20 +1528,17 @@ impl Context { // stack operand. diff += 1; } else { - return usize::MAX; + return TypeDiff::Incompatible; } } - let temp_diff = src_type.diff(dst_type); - - if temp_diff == usize::MAX { - return usize::MAX; - } - - diff += temp_diff; + diff += match src_type.diff(dst_type) { + TypeDiff::Compatible(diff) => diff, + TypeDiff::Incompatible => return TypeDiff::Incompatible, + }; } - return diff; + return TypeDiff::Compatible(diff); } pub fn two_fixnums_on_stack(&self, jit: &mut JITState) -> Option<bool> { @@ -2509,22 +2503,22 @@ mod tests { #[test] fn types() { // Valid src => dst - assert_eq!(Type::Unknown.diff(Type::Unknown), 0); - assert_eq!(Type::UnknownImm.diff(Type::UnknownImm), 0); - assert_ne!(Type::UnknownImm.diff(Type::Unknown), usize::MAX); - assert_ne!(Type::Fixnum.diff(Type::Unknown), usize::MAX); - assert_ne!(Type::Fixnum.diff(Type::UnknownImm), usize::MAX); + assert_eq!(Type::Unknown.diff(Type::Unknown), TypeDiff::Compatible(0)); + assert_eq!(Type::UnknownImm.diff(Type::UnknownImm), TypeDiff::Compatible(0)); + assert_ne!(Type::UnknownImm.diff(Type::Unknown), TypeDiff::Incompatible); + assert_ne!(Type::Fixnum.diff(Type::Unknown), TypeDiff::Incompatible); + assert_ne!(Type::Fixnum.diff(Type::UnknownImm), TypeDiff::Incompatible); // Invalid src => dst - assert_eq!(Type::Unknown.diff(Type::UnknownImm), usize::MAX); - assert_eq!(Type::Unknown.diff(Type::Fixnum), usize::MAX); - assert_eq!(Type::Fixnum.diff(Type::UnknownHeap), usize::MAX); + assert_eq!(Type::Unknown.diff(Type::UnknownImm), TypeDiff::Incompatible); + assert_eq!(Type::Unknown.diff(Type::Fixnum), TypeDiff::Incompatible); + assert_eq!(Type::Fixnum.diff(Type::UnknownHeap), TypeDiff::Incompatible); } #[test] fn context() { // Valid src => dst - assert_eq!(Context::default().diff(&Context::default()), 0); + assert_eq!(Context::default().diff(&Context::default()), TypeDiff::Compatible(0)); // Try pushing an operand and getting its type let mut ctx = Context::default(); |