Skip to content

Commit de05152

Browse files
committed
fix(jit): keep scalar to_string on tagged path
1 parent 1b03f34 commit de05152

2 files changed

Lines changed: 74 additions & 5 deletions

File tree

src/vm/jit/recorder.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3274,11 +3274,11 @@ fn select_specialized_builtin_kind(
32743274
});
32753275
}
32763276
BuiltinFunction::ToString => {
3277-
return Some(if container.known_type == Some(ValueType::String) {
3278-
SpecializedBuiltinKind::ToStringIdentity
3279-
} else {
3280-
SpecializedBuiltinKind::ToString
3281-
});
3277+
return match (container.known_type, container.repr) {
3278+
(Some(ValueType::String), _) => Some(SpecializedBuiltinKind::ToStringIdentity),
3279+
(_, SsaValueRepr::Tagged) => Some(SpecializedBuiltinKind::ToString),
3280+
_ => None,
3281+
};
32823282
}
32833283
BuiltinFunction::StringSplitLiteral => {
32843284
return Some(SpecializedBuiltinKind::StringSplitLiteral);
@@ -5272,6 +5272,38 @@ mod tests {
52725272
);
52735273
}
52745274

5275+
#[test]
5276+
fn scalar_to_string_stays_on_general_call_path() {
5277+
let program = Program::new(Vec::new(), Vec::new());
5278+
for info in [
5279+
ValueInfo::int(None),
5280+
ValueInfo::float(None),
5281+
ValueInfo::bool(None),
5282+
] {
5283+
assert_eq!(
5284+
select_specialized_builtin_kind(
5285+
&program,
5286+
0,
5287+
BuiltinFunction::ToString,
5288+
info,
5289+
false,
5290+
),
5291+
None,
5292+
"unboxed scalar to_string needs a tagged Value slot"
5293+
);
5294+
}
5295+
assert_eq!(
5296+
select_specialized_builtin_kind(
5297+
&program,
5298+
0,
5299+
BuiltinFunction::ToString,
5300+
ValueInfo::tagged(),
5301+
false,
5302+
),
5303+
Some(SpecializedBuiltinKind::ToString)
5304+
);
5305+
}
5306+
52755307
#[test]
52765308
fn unboxed_numeric_compare_ignores_stale_operand_type_hint() {
52775309
let program = Program::new(Vec::new(), Vec::new()).with_type_map(crate::TypeMap {

tests/jit/jit_tests.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6100,6 +6100,43 @@ fn trace_jit_specializes_regex_builtins_without_call_boundary() {
61006100
assert!(vm.regex_cache_hit_count() >= 14);
61016101
}
61026102

6103+
#[test]
6104+
fn trace_jit_scalar_to_string_uses_safe_call_boundary() {
6105+
if !native_jit_supported() {
6106+
return;
6107+
}
6108+
let source = r#"
6109+
let mut i = 0;
6110+
let mut rendered = "";
6111+
while i < 16 {
6112+
rendered = i + "";
6113+
i = i + 1;
6114+
}
6115+
rendered;
6116+
"#;
6117+
let compiled = compile_source(source).expect("scalar to_string loop should compile");
6118+
let mut vm = Vm::new(compiled.program.with_local_count(compiled.locals));
6119+
vm.set_jit_native_direct_links_enabled(false);
6120+
vm.set_jit_config(JitConfig {
6121+
enabled: true,
6122+
hot_loop_threshold: 1,
6123+
max_trace_len: 512,
6124+
});
6125+
6126+
assert_eq!(
6127+
vm.run().expect("scalar to_string JIT should run"),
6128+
VmStatus::Halted
6129+
);
6130+
assert_eq!(vm.stack(), &[Value::string("15")]);
6131+
let snapshot = vm.jit_snapshot();
6132+
assert!(vm.jit_native_exec_count() > 0, "{}", vm.dump_jit_info());
6133+
assert!(
6134+
!snapshot.traces.is_empty(),
6135+
"scalar to_string should leave surrounding loop traceable:\n{}",
6136+
vm.dump_jit_info()
6137+
);
6138+
}
6139+
61036140
#[test]
61046141
fn trace_jit_executes_hot_loop_inside_script_callable_frame() {
61056142
if !native_jit_supported() {

0 commit comments

Comments
 (0)