summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yjit/src/codegen.rs45
-rw-r--r--yjit/src/core.rs19
2 files changed, 45 insertions, 19 deletions
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index 7fae75eba6..1b8b99d530 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -1716,7 +1716,7 @@ fn gen_putstring(
jit_mov_gc_ptr(jit, cb, C_ARG_REGS[1], put_val);
call_ptr(cb, REG0, rb_ec_str_resurrect as *const u8);
- let stack_top = ctx.stack_push(Type::String);
+ let stack_top = ctx.stack_push(Type::CString);
mov(cb, stack_top, RAX);
KeepCompiling
@@ -2189,7 +2189,8 @@ fn gen_checktype(
// Check if we know from type information
match (type_val, val_type) {
- (RUBY_T_STRING, Type::String)
+ (RUBY_T_STRING, Type::TString)
+ | (RUBY_T_STRING, Type::CString)
| (RUBY_T_ARRAY, Type::Array)
| (RUBY_T_HASH, Type::Hash) => {
// guaranteed type match
@@ -2258,7 +2259,7 @@ fn gen_concatstrings(
call_ptr(cb, REG0, rb_str_concat_literals as *const u8);
ctx.stack_pop(n.as_usize());
- let stack_ret = ctx.stack_push(Type::String);
+ let stack_ret = ctx.stack_push(Type::CString);
mov(cb, stack_ret, RAX);
KeepCompiling
@@ -2470,7 +2471,8 @@ fn gen_equality_specialized(
je_label(cb, ret);
// Otherwise guard that b is a T_STRING (from type info) or String (from runtime guard)
- if ctx.get_opnd_type(StackOpnd(0)) != Type::String {
+ let btype = ctx.get_opnd_type(StackOpnd(0));
+ if btype != Type::TString && btype != Type::CString {
mov(cb, REG0, C_ARG_REGS[1]);
// Note: any T_STRING is valid here, but we check for a ::String for simplicity
// To pass a mutable static variable (rb_cString) requires an unsafe block
@@ -3029,7 +3031,7 @@ fn gen_opt_str_freeze(
jit_mov_gc_ptr(jit, cb, REG0, str);
// Push the return value onto the stack
- let stack_ret = ctx.stack_push(Type::String);
+ let stack_ret = ctx.stack_push(Type::CString);
mov(cb, stack_ret, REG0);
KeepCompiling
@@ -3049,7 +3051,7 @@ fn gen_opt_str_uminus(
jit_mov_gc_ptr(jit, cb, REG0, str);
// Push the return value onto the stack
- let stack_ret = ctx.stack_push(Type::String);
+ let stack_ret = ctx.stack_push(Type::CString);
mov(cb, stack_ret, REG0);
KeepCompiling
@@ -3444,6 +3446,11 @@ fn jit_guard_known_klass(
jit_mov_gc_ptr(jit, cb, REG1, sample_instance);
cmp(cb, REG0, REG1);
jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
+ } else if val_type == Type::CString && unsafe { known_klass == rb_cString } {
+ // guard elided because the context says we've already checked
+ unsafe {
+ assert_eq!(sample_instance.class_of(), rb_cString, "context says class is exactly ::String")
+ };
} else {
assert!(!val_type.is_imm());
@@ -3468,6 +3475,10 @@ fn jit_guard_known_klass(
jit_mov_gc_ptr(jit, cb, REG1, known_klass);
cmp(cb, klass_opnd, REG1);
jit_chain_guard(JCC_JNE, jit, ctx, cb, ocb, max_chain_depth, side_exit);
+
+ if known_klass == unsafe { rb_cString } {
+ ctx.upgrade_opnd_type(insn_opnd, Type::CString);
+ }
}
}
@@ -3631,7 +3642,8 @@ fn jit_rb_str_uplus(
// Return value is in REG0, drop through and return it.
cb.write_label(ret_label);
- let stack_ret = ctx.stack_push(Type::String);
+ // We guard for an exact-class match on the receiver of rb_cString
+ let stack_ret = ctx.stack_push(Type::CString);
mov(cb, stack_ret, REG0);
cb.link_labels();
@@ -3705,7 +3717,8 @@ fn jit_rb_str_concat(
// String#<< can take an integer codepoint as an argument, but we don't optimise that.
// Also, a non-string argument would have to call .to_str on itself before being treated
// as a string, and that would require saving pc/sp, which we don't do here.
- if comptime_arg_type != Type::String {
+ // TODO: figure out how we should optimise a string-subtype argument here
+ if comptime_arg_type != Type::CString && comptime_arg.class_of() != unsafe { rb_cString } {
return false;
}
@@ -3761,7 +3774,7 @@ fn jit_rb_str_concat(
// Drop through to return
cb.write_label(ret_label);
- let stack_ret = ctx.stack_push(Type::String);
+ let stack_ret = ctx.stack_push(Type::CString);
mov(cb, stack_ret, RAX);
cb.link_labels();
@@ -5272,7 +5285,7 @@ fn gen_anytostring(
call_ptr(cb, REG0, rb_obj_as_string_result as *const u8);
// Push the return value
- let stack_ret = ctx.stack_push(Type::String);
+ let stack_ret = ctx.stack_push(Type::TString);
mov(cb, stack_ret, RAX);
KeepCompiling
@@ -6392,7 +6405,7 @@ mod tests {
let (mut jit, mut context, mut cb, mut ocb) = setup_codegen();
context.stack_push(Type::Fixnum);
context.stack_push(Type::Flonum);
- context.stack_push(Type::String);
+ context.stack_push(Type::CString);
let mut value_array: [u64; 2] = [0, 2];
let pc: *mut VALUE = &mut value_array as *mut u64 as *mut VALUE;
@@ -6402,9 +6415,9 @@ mod tests {
assert_eq!(status, KeepCompiling);
- assert_eq!(Type::String, context.get_opnd_type(StackOpnd(2)));
+ assert_eq!(Type::CString, context.get_opnd_type(StackOpnd(2)));
assert_eq!(Type::Flonum, context.get_opnd_type(StackOpnd(1)));
- assert_eq!(Type::String, context.get_opnd_type(StackOpnd(0)));
+ assert_eq!(Type::CString, context.get_opnd_type(StackOpnd(0)));
assert!(cb.get_write_pos() > 0);
}
@@ -6413,7 +6426,7 @@ mod tests {
fn test_gen_topn() {
let (mut jit, mut context, mut cb, mut ocb) = setup_codegen();
context.stack_push(Type::Flonum);
- context.stack_push(Type::String);
+ context.stack_push(Type::CString);
let mut value_array: [u64; 2] = [0, 1];
let pc: *mut VALUE = &mut value_array as *mut u64 as *mut VALUE;
@@ -6424,7 +6437,7 @@ mod tests {
assert_eq!(status, KeepCompiling);
assert_eq!(Type::Flonum, context.get_opnd_type(StackOpnd(2)));
- assert_eq!(Type::String, context.get_opnd_type(StackOpnd(1)));
+ assert_eq!(Type::CString, context.get_opnd_type(StackOpnd(1)));
assert_eq!(Type::Flonum, context.get_opnd_type(StackOpnd(0)));
assert!(cb.get_write_pos() > 0); // Write some movs
@@ -6434,7 +6447,7 @@ mod tests {
fn test_gen_adjuststack() {
let (mut jit, mut context, mut cb, mut ocb) = setup_codegen();
context.stack_push(Type::Flonum);
- context.stack_push(Type::String);
+ context.stack_push(Type::CString);
context.stack_push(Type::Fixnum);
let mut value_array: [u64; 3] = [0, 2, 0];
diff --git a/yjit/src/core.rs b/yjit/src/core.rs
index 6d6877f273..8242c9477e 100644
--- a/yjit/src/core.rs
+++ b/yjit/src/core.rs
@@ -38,7 +38,8 @@ pub enum Type {
#[allow(unused)]
HeapSymbol,
- String,
+ TString, // An object with the T_STRING flag set, possibly an rb_cString
+ CString, // An un-subclassed string of type rb_cString (can have instance vars in some cases)
}
// Default initialization
@@ -68,10 +69,16 @@ impl Type {
unreachable!()
}
} else {
+ // Core.rs can't reference rb_cString because it's linked by Rust-only tests.
+ // But CString vs TString is only an optimisation and shouldn't affect correctness.
+ #[cfg(not(test))]
+ if val.class_of() == unsafe { rb_cString } {
+ return Type::CString;
+ }
match val.builtin_type() {
RUBY_T_ARRAY => Type::Array,
RUBY_T_HASH => Type::Hash,
- RUBY_T_STRING => Type::String,
+ RUBY_T_STRING => Type::TString,
_ => Type::UnknownHeap,
}
}
@@ -113,7 +120,8 @@ impl Type {
Type::Array => true,
Type::Hash => true,
Type::HeapSymbol => true,
- Type::String => true,
+ Type::TString => true,
+ Type::CString => true,
_ => false,
}
}
@@ -133,6 +141,11 @@ impl Type {
return 1;
}
+ // A CString is also a TString.
+ if self == Type::CString && dst == Type::TString {
+ return 1;
+ }
+
// Specific heap type into unknown heap type is imperfect but valid
if self.is_heap() && dst == Type::UnknownHeap {
return 1;