@@ -141,6 +141,7 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> {
141141 write ! ( f, "BOPRedefined(" ) ?;
142142 match klass {
143143 INTEGER_REDEFINED_OP_FLAG => write ! ( f, "INTEGER_REDEFINED_OP_FLAG" ) ?,
144+ ARRAY_REDEFINED_OP_FLAG => write ! ( f, "ARRAY_REDEFINED_OP_FLAG" ) ?,
144145 _ => write ! ( f, "{klass}" ) ?,
145146 }
146147 write ! ( f, ", " ) ?;
@@ -156,6 +157,7 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> {
156157 BOP_LE => write ! ( f, "BOP_LE" ) ?,
157158 BOP_GT => write ! ( f, "BOP_GT" ) ?,
158159 BOP_GE => write ! ( f, "BOP_GE" ) ?,
160+ BOP_MAX => write ! ( f, "BOP_MAX" ) ?,
159161 _ => write ! ( f, "{bop}" ) ?,
160162 }
161163 write ! ( f, ")" )
@@ -310,6 +312,7 @@ pub enum Insn {
310312 NewArray { elements : Vec < InsnId > , state : InsnId } ,
311313 ArraySet { array : InsnId , idx : usize , val : InsnId } ,
312314 ArrayDup { val : InsnId , state : InsnId } ,
315+ ArrayMax { elements : Vec < InsnId > , state : InsnId } ,
313316
314317 // Check if the value is truthy and "return" a C boolean. In reality, we will likely fuse this
315318 // with IfTrue/IfFalse in the backend to generate jcc.
@@ -441,6 +444,15 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
441444 }
442445 Ok ( ( ) )
443446 }
447+ Insn :: ArrayMax { elements, .. } => {
448+ write ! ( f, "ArrayMax" ) ?;
449+ let mut prefix = " " ;
450+ for element in elements {
451+ write ! ( f, "{prefix}{element}" ) ?;
452+ prefix = ", " ;
453+ }
454+ Ok ( ( ) )
455+ }
444456 Insn :: ArraySet { array, idx, val } => { write ! ( f, "ArraySet {array}, {idx}, {val}" ) }
445457 Insn :: ArrayDup { val, .. } => { write ! ( f, "ArrayDup {val}" ) }
446458 Insn :: StringCopy { val } => { write ! ( f, "StringCopy {val}" ) }
@@ -744,20 +756,27 @@ impl Function {
744756 }
745757 } ;
746758 }
759+ macro_rules! find_vec {
760+ ( $x: expr ) => {
761+ {
762+ $x. iter( ) . map( |arg| find!( * arg) ) . collect( )
763+ }
764+ } ;
765+ }
747766 macro_rules! find_branch_edge {
748767 ( $edge: ident ) => {
749768 {
750769 BranchEdge {
751770 target: $edge. target,
752- args: $edge. args. iter ( ) . map ( |x| find! ( * x ) ) . collect ( ) ,
771+ args: find_vec! ( $edge. args) ,
753772 }
754773 }
755774 } ;
756775 }
757776 let insn_id = self . union_find . borrow_mut ( ) . find ( insn_id) ;
758777 use Insn :: * ;
759778 match & self . insns [ insn_id. 0 ] {
760- result@( PutSelf | Const { ..} | Param { ..} | NewArray { .. } | GetConstantPath { ..}
779+ result@( PutSelf | Const { ..} | Param { ..} | GetConstantPath { ..}
761780 | PatchPoint { ..} ) => result. clone ( ) ,
762781 Snapshot { state : FrameState { iseq, insn_idx, pc, stack, locals } } =>
763782 Snapshot {
@@ -816,6 +835,8 @@ impl Function {
816835 ArrayDup { val , state } => ArrayDup { val : find ! ( * val) , state : * state } ,
817836 CCall { cfun, args, name, return_type } => CCall { cfun : * cfun, args : args. iter ( ) . map ( |arg| find ! ( * arg) ) . collect ( ) , name : * name, return_type : * return_type } ,
818837 Defined { .. } => todo ! ( "find(Defined)" ) ,
838+ NewArray { elements, state } => NewArray { elements : find_vec ! ( * elements) , state : find ! ( * state) } ,
839+ ArrayMax { elements, state } => ArrayMax { elements : find_vec ! ( * elements) , state : find ! ( * state) } ,
819840 }
820841 }
821842
@@ -882,6 +903,7 @@ impl Function {
882903 Insn :: PutSelf => types:: BasicObject ,
883904 Insn :: Defined { .. } => types:: BasicObject ,
884905 Insn :: GetConstantPath { .. } => types:: BasicObject ,
906+ Insn :: ArrayMax { .. } => types:: BasicObject ,
885907 }
886908 }
887909
@@ -1309,9 +1331,13 @@ impl Function {
13091331 necessary[ insn_id. 0 ] = true ;
13101332 match self . find ( insn_id) {
13111333 Insn :: PutSelf | Insn :: Const { .. } | Insn :: Param { .. }
1312- | Insn :: NewArray { .. } | Insn :: PatchPoint ( ..)
1313- | Insn :: GetConstantPath { .. } =>
1334+ | Insn :: PatchPoint ( ..) | Insn :: GetConstantPath { .. } =>
13141335 { }
1336+ Insn :: ArrayMax { elements, state }
1337+ | Insn :: NewArray { elements, state } => {
1338+ worklist. extend ( elements) ;
1339+ worklist. push_back ( state) ;
1340+ }
13151341 Insn :: StringCopy { val }
13161342 | Insn :: StringIntern { val }
13171343 | Insn :: Return { val }
@@ -1636,6 +1662,7 @@ pub enum CallType {
16361662pub enum ParseError {
16371663 StackUnderflow ( FrameState ) ,
16381664 UnknownOpcode ( String ) ,
1665+ UnknownNewArraySend ( String ) ,
16391666 UnhandledCallType ( CallType ) ,
16401667}
16411668
@@ -1755,6 +1782,26 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
17551782 elements. reverse ( ) ;
17561783 state. stack_push ( fun. push_insn ( block, Insn :: NewArray { elements, state : exit_id } ) ) ;
17571784 }
1785+ YARVINSN_opt_newarray_send => {
1786+ let count = get_arg ( pc, 0 ) . as_usize ( ) ;
1787+ let method = get_arg ( pc, 1 ) . as_u32 ( ) ;
1788+ let mut elements = vec ! [ ] ;
1789+ for _ in 0 ..count {
1790+ elements. push ( state. stack_pop ( ) ?) ;
1791+ }
1792+ elements. reverse ( ) ;
1793+ let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state. clone ( ) } ) ;
1794+ let ( bop, insn) = match method {
1795+ VM_OPT_NEWARRAY_SEND_MAX => ( BOP_MAX , Insn :: ArrayMax { elements, state : exit_id } ) ,
1796+ VM_OPT_NEWARRAY_SEND_MIN => return Err ( ParseError :: UnknownNewArraySend ( "min" . into ( ) ) ) ,
1797+ VM_OPT_NEWARRAY_SEND_HASH => return Err ( ParseError :: UnknownNewArraySend ( "hash" . into ( ) ) ) ,
1798+ VM_OPT_NEWARRAY_SEND_PACK => return Err ( ParseError :: UnknownNewArraySend ( "pack" . into ( ) ) ) ,
1799+ VM_OPT_NEWARRAY_SEND_PACK_BUFFER => return Err ( ParseError :: UnknownNewArraySend ( "pack_buffer" . into ( ) ) ) ,
1800+ _ => return Err ( ParseError :: UnknownNewArraySend ( format ! ( "{method}" ) ) ) ,
1801+ } ;
1802+ fun. push_insn ( block, Insn :: PatchPoint ( Invariant :: BOPRedefined { klass : ARRAY_REDEFINED_OP_FLAG , bop } ) ) ;
1803+ state. stack_push ( fun. push_insn ( block, insn) ) ;
1804+ }
17581805 YARVINSN_duparray => {
17591806 let val = fun. push_insn ( block, Insn :: Const { val : Const :: Value ( get_arg ( pc, 0 ) ) } ) ;
17601807 let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state. clone ( ) } ) ;
@@ -2831,6 +2878,35 @@ mod tests {
28312878 Return v10
28322879 "# ] ] ) ;
28332880 }
2881+
2882+ #[ test]
2883+ fn test_opt_newarray_send_max_no_elements ( ) {
2884+ eval ( "
2885+ def test = [].max
2886+ " ) ;
2887+ // TODO(max): Rewrite to nil
2888+ assert_method_hir ( "test" , expect ! [ [ r#"
2889+ fn test:
2890+ bb0():
2891+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX)
2892+ v3:BasicObject = ArrayMax
2893+ Return v3
2894+ "# ] ] ) ;
2895+ }
2896+
2897+ #[ test]
2898+ fn test_opt_newarray_send_max ( ) {
2899+ eval ( "
2900+ def test(a,b) = [a,b].max
2901+ " ) ;
2902+ assert_method_hir ( "test" , expect ! [ [ r#"
2903+ fn test:
2904+ bb0(v0:BasicObject, v1:BasicObject):
2905+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX)
2906+ v5:BasicObject = ArrayMax v0, v1
2907+ Return v5
2908+ "# ] ] ) ;
2909+ }
28342910}
28352911
28362912#[ cfg( test) ]
0 commit comments