@@ -2,14 +2,15 @@ use std::{fmt, sync::Arc};
22
33use crate :: CallableValue ;
44use crate :: builtins:: BuiltinFunction ;
5+ use crate :: compiler:: TypeSchema ;
56use crate :: vm:: { OpCode , Program , Value , ValueType , checked_int_div} ;
67
78use super :: JitTraceTerminal ;
89use super :: deopt:: materialize_ssa_values;
9- use super :: inline:: { InlineCandidate , classify_static_inline_candidate} ;
10+ use super :: inline:: { InlineCandidate , InlineRejectReason , classify_static_inline_candidate} ;
1011use super :: ir:: {
1112 SsaBranchTarget , SsaInstKind , SsaMaterialization , SsaTerminator , SsaTrace , SsaTraceBuilder ,
12- SsaValue , SsaValueRepr , VirtualFrameSnapshot ,
13+ SsaValue , SsaValueId , SsaValueRepr , VirtualFrameSnapshot ,
1314} ;
1415
1516#[ derive( Clone , Debug , PartialEq ) ]
@@ -263,6 +264,99 @@ struct SymbolicValue {
263264 info : ValueInfo ,
264265}
265266
267+ fn inline_schema_guard_type ( schema : & TypeSchema ) -> Option < Option < ValueType > > {
268+ match schema {
269+ TypeSchema :: Unknown | TypeSchema :: GenericParam ( _) => Some ( None ) ,
270+ TypeSchema :: Int => Some ( Some ( ValueType :: Int ) ) ,
271+ TypeSchema :: Float => Some ( Some ( ValueType :: Float ) ) ,
272+ TypeSchema :: Bool => Some ( Some ( ValueType :: Bool ) ) ,
273+ TypeSchema :: String => Some ( Some ( ValueType :: String ) ) ,
274+ TypeSchema :: Bytes => Some ( Some ( ValueType :: Bytes ) ) ,
275+ TypeSchema :: Named ( _, _) | TypeSchema :: Map ( _) | TypeSchema :: Object ( _) => {
276+ Some ( Some ( ValueType :: Map ) )
277+ }
278+ TypeSchema :: Array ( _) | TypeSchema :: ArrayTuple ( _) | TypeSchema :: ArrayTupleRest { .. } => {
279+ Some ( Some ( ValueType :: Array ) )
280+ }
281+ TypeSchema :: Null
282+ | TypeSchema :: Number
283+ | TypeSchema :: Optional ( _)
284+ | TypeSchema :: Callable { .. } => None ,
285+ }
286+ }
287+
288+ fn inline_argument_schemas_supported (
289+ arguments : & [ SymbolicValue ] ,
290+ schema : Option < & TypeSchema > ,
291+ ) -> bool {
292+ let Some ( TypeSchema :: Callable { params, .. } ) = schema else {
293+ return schema. is_none ( ) ;
294+ } ;
295+ params. len ( ) == arguments. len ( )
296+ && params. iter ( ) . zip ( arguments) . all ( |( schema, argument) | {
297+ let Some ( guard_type) = inline_schema_guard_type ( schema) else {
298+ return false ;
299+ } ;
300+ match ( guard_type, argument. info . repr ) {
301+ ( None , _) | ( Some ( _) , SsaValueRepr :: Tagged ) => true ,
302+ ( Some ( ValueType :: Int ) , SsaValueRepr :: I64 )
303+ | ( Some ( ValueType :: Float ) , SsaValueRepr :: F64 )
304+ | ( Some ( ValueType :: Bool ) , SsaValueRepr :: Bool ) => true ,
305+ ( Some ( expected) , SsaValueRepr :: HeapPtr ( actual) ) => expected == actual,
306+ _ => false ,
307+ }
308+ } )
309+ }
310+
311+ fn append_inline_argument_schema_guards (
312+ builder : & mut SsaTraceBuilder ,
313+ block : super :: ir:: SsaBlockId ,
314+ ip : usize ,
315+ arguments : & [ SymbolicValue ] ,
316+ schema : Option < & TypeSchema > ,
317+ ) -> Result < Option < SsaValueId > , TraceRecordError > {
318+ let Some ( TypeSchema :: Callable { params, .. } ) = schema else {
319+ return Ok ( None ) ;
320+ } ;
321+ let mut guard = None ;
322+ for ( schema, argument) in params. iter ( ) . zip ( arguments) {
323+ let Some ( Some ( expected) ) = inline_schema_guard_type ( schema) else {
324+ continue ;
325+ } ;
326+ if argument. info . repr != SsaValueRepr :: Tagged {
327+ continue ;
328+ }
329+ let predicate = builder
330+ . append_value_inst (
331+ block,
332+ ip,
333+ SsaValueRepr :: Bool ,
334+ SsaInstKind :: ValueIsType {
335+ input : argument. value . id ,
336+ tag : expected,
337+ } ,
338+ )
339+ . map_err ( |err| TraceRecordError :: InvalidIr ( err. to_string ( ) ) ) ?;
340+ guard = Some ( if let Some ( previous) = guard {
341+ builder
342+ . append_value_inst (
343+ block,
344+ ip,
345+ SsaValueRepr :: Bool ,
346+ SsaInstKind :: BoolAnd {
347+ lhs : previous,
348+ rhs : predicate. id ,
349+ } ,
350+ )
351+ . map_err ( |err| TraceRecordError :: InvalidIr ( err. to_string ( ) ) ) ?
352+ . id
353+ } else {
354+ predicate. id
355+ } ) ;
356+ }
357+ Ok ( guard)
358+ }
359+
266360#[ derive( Clone , Debug , PartialEq ) ]
267361struct SymbolicFrame {
268362 stack : Vec < SymbolicValue > ,
@@ -1241,12 +1335,30 @@ pub(crate) fn record_trace_with_local_count(
12411335 callable. info . source_local ,
12421336 argc,
12431337 max_trace_len. saturating_sub ( cursor. recorded_ops ) ,
1244- ) ;
1338+ )
1339+ . and_then ( |candidate| {
1340+ let prototype = & program. callable_prototypes [ candidate. prototype_id as usize ] ;
1341+ let argument_start = frame. stack . len ( ) - usize:: from ( argc) ;
1342+ inline_argument_schemas_supported (
1343+ & frame. stack [ argument_start..] ,
1344+ prototype. schema . as_ref ( ) ,
1345+ )
1346+ . then_some ( candidate)
1347+ . ok_or ( InlineRejectReason :: SchemaUnproven )
1348+ } ) ;
12451349 let inline_reject_reason = candidate. as_ref ( ) . err ( ) . copied ( ) ;
12461350 if inline_frame. is_none ( )
12471351 && let Ok ( candidate) = candidate
12481352 {
12491353 let prototype = & program. callable_prototypes [ candidate. prototype_id as usize ] ;
1354+ let argument_start = frame. stack . len ( ) - usize:: from ( argc) ;
1355+ let schema_guard = append_inline_argument_schema_guards (
1356+ & mut builder,
1357+ current_block,
1358+ ip,
1359+ & frame. stack [ argument_start..] ,
1360+ prototype. schema . as_ref ( ) ,
1361+ ) ?;
12501362 let expected_callable = builder
12511363 . append_value_inst (
12521364 current_block,
@@ -1270,6 +1382,22 @@ pub(crate) fn record_trace_with_local_count(
12701382 } ,
12711383 )
12721384 . map_err ( |err| TraceRecordError :: InvalidIr ( err. to_string ( ) ) ) ?;
1385+ let inline_guard = if let Some ( schema_guard) = schema_guard {
1386+ builder
1387+ . append_value_inst (
1388+ current_block,
1389+ ip,
1390+ SsaValueRepr :: Bool ,
1391+ SsaInstKind :: BoolAnd {
1392+ lhs : schema_guard,
1393+ rhs : callable_matches. id ,
1394+ } ,
1395+ )
1396+ . map_err ( |err| TraceRecordError :: InvalidIr ( err. to_string ( ) ) ) ?
1397+ . id
1398+ } else {
1399+ callable_matches. id
1400+ } ;
12731401 let identity_exit =
12741402 add_symbolic_exit ( & mut builder, ip, & frame, inline_frame. as_ref ( ) ) ;
12751403 let ( guarded_block, guarded_frame, guard_args) = continue_with_inline_frame (
@@ -1282,7 +1410,7 @@ pub(crate) fn record_trace_with_local_count(
12821410 . set_terminator (
12831411 current_block,
12841412 SsaTerminator :: BranchBool {
1285- condition : callable_matches . id ,
1413+ condition : inline_guard ,
12861414 if_true : SsaBranchTarget :: Block {
12871415 target : guarded_block,
12881416 args : guard_args,
0 commit comments