@@ -19,6 +19,8 @@ unsafe extern "C" {
1919 fn rb_builtin_ary_at_end ( ec : EcPtr , self_ : VALUE , index : VALUE ) -> VALUE ;
2020 fn rb_builtin_ary_at ( ec : EcPtr , self_ : VALUE , index : VALUE ) -> VALUE ;
2121 fn rb_builtin_fixnum_inc ( ec : EcPtr , self_ : VALUE , num : VALUE ) -> VALUE ;
22+ fn rb_builtin_ary_first ( ec : EcPtr , self_ : VALUE ) -> VALUE ;
23+ fn rb_builtin_ary_last ( ec : EcPtr , self_ : VALUE ) -> VALUE ;
2224 fn rb_str_equal ( str1 : VALUE , str2 : VALUE ) -> VALUE ;
2325}
2426
@@ -301,6 +303,10 @@ pub fn init() -> Annotations {
301303 builtin_funcs. insert ( rb_builtin_ary_at as * mut c_void , FnProperties { inline : inline_ary_at, ..Default :: default ( ) } ) ;
302304 builtin_funcs. insert ( rb_builtin_ary_at_end as * mut c_void , FnProperties { inline : inline_ary_at_end, return_type : types:: BoolExact , ..Default :: default ( ) } ) ;
303305
306+ // Builtins used by Array#first and Array#last when called with no arguments
307+ builtin_funcs. insert ( rb_builtin_ary_first as * mut c_void , FnProperties { inline : inline_ary_first, no_gc : true , leaf : true , elidable : true , ..Default :: default ( ) } ) ;
308+ builtin_funcs. insert ( rb_builtin_ary_last as * mut c_void , FnProperties { inline : inline_ary_last, no_gc : true , leaf : true , elidable : true , ..Default :: default ( ) } ) ;
309+
304310 Annotations {
305311 cfuncs : std:: mem:: take ( cfuncs) ,
306312 builtin_funcs : std:: mem:: take ( builtin_funcs) ,
@@ -1057,3 +1063,35 @@ fn inline_ary_at_end(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::I
10571063 let result = fun. push_insn ( block, hir:: Insn :: FixnumGe { left : index, right : length } ) ;
10581064 Some ( result)
10591065}
1066+
1067+ /// Inline `ary_first(ec, self)` from Array#first with no arguments.
1068+ /// Guards that the array is non-empty and loads the element at index 0;
1069+ /// side-exits on empty arrays and lets the interpreter return nil.
1070+ fn inline_ary_first ( fun : & mut hir:: Function , block : hir:: BlockId , _recv : hir:: InsnId , args : & [ hir:: InsnId ] , state : hir:: InsnId ) -> Option < hir:: InsnId > {
1071+ use crate :: hir:: SideExitReason ;
1072+ let & [ recv] = args else { return None ; } ;
1073+ let recv = fun. push_insn ( block, hir:: Insn :: RefineType { val : recv, new_type : types:: Array } ) ;
1074+ let length = fun. push_insn ( block, hir:: Insn :: ArrayLength { array : recv } ) ;
1075+ let zero = fun. push_insn ( block, hir:: Insn :: Const { val : hir:: Const :: CInt64 ( 0 ) } ) ;
1076+ let index = fun. push_insn ( block, hir:: Insn :: GuardLess { left : zero, right : length, reason : Box :: new ( SideExitReason :: GuardLess ) , state } ) ;
1077+ let result = fun. push_insn ( block, hir:: Insn :: ArrayAref { array : recv, index } ) ;
1078+ Some ( result)
1079+ }
1080+
1081+ /// Inline `ary_last(ec, self)` from Array#last with no arguments.
1082+ /// Guards that the array is non-empty and loads the element at index length-1;
1083+ /// side-exits on empty arrays and lets the interpreter return nil.
1084+ fn inline_ary_last ( fun : & mut hir:: Function , block : hir:: BlockId , _recv : hir:: InsnId , args : & [ hir:: InsnId ] , state : hir:: InsnId ) -> Option < hir:: InsnId > {
1085+ use crate :: hir:: SideExitReason ;
1086+ let & [ recv] = args else { return None ; } ;
1087+ let recv = fun. push_insn ( block, hir:: Insn :: RefineType { val : recv, new_type : types:: Array } ) ;
1088+ let length = fun. push_insn ( block, hir:: Insn :: ArrayLength { array : recv } ) ;
1089+ let minus_one = fun. push_insn ( block, hir:: Insn :: Const { val : hir:: Const :: CInt64 ( -1 ) } ) ;
1090+ // AdjustBounds computes length - 1 since the index is negative
1091+ // TODO: Consider doing something like IntSub instead
1092+ let index = fun. push_insn ( block, hir:: Insn :: AdjustBounds { index : minus_one, length } ) ;
1093+ let zero = fun. push_insn ( block, hir:: Insn :: Const { val : hir:: Const :: CInt64 ( 0 ) } ) ;
1094+ let index = fun. push_insn ( block, hir:: Insn :: GuardGreaterEq { left : index, right : zero, reason : Box :: new ( SideExitReason :: GuardGreaterEq ) , state } ) ;
1095+ let result = fun. push_insn ( block, hir:: Insn :: ArrayAref { array : recv, index } ) ;
1096+ Some ( result)
1097+ }
0 commit comments