1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Separate Type::String into Type::CString and Type::TString.

Also slightly broaden the cases where << on two strings will generate
specialised code rather than a plain method call.
This commit is contained in:
Noah Gibbs (and/or Benchmark CI) 2022-06-13 15:17:06 +00:00 committed by Aaron Patterson
parent f9f85a513b
commit 0fab06f3c3
Notes: git 2022-06-28 01:26:25 +09:00
2 changed files with 45 additions and 19 deletions

View file

@ -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];

View file

@ -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;