@@ -234,6 +234,11 @@ const char* ir_op_name(int op)
234234 case OP_AND : return "and" ;
235235 case OP_CALL : return "call" ;
236236 case OP_CCALL : return "ccall" ;
237+ case OP_CMOV_GE : return "cmovge" ;
238+ case OP_CMOV_GT : return "cmovg" ;
239+ case OP_CMOV_LE : return "cmovle" ;
240+ case OP_CMOV_LT : return "cmovl" ;
241+ case OP_CMP : return "cmp" ;
237242 case OP_COMMENT : return "comment" ;
238243 case OP_JUMP_EQ : return "jumpeq" ;
239244 case OP_JUMP_NE : return "jumpne" ;
@@ -243,10 +248,15 @@ const char* ir_op_name(int op)
243248 case OP_NOT : return "not" ;
244249 case OP_RET : return "ret" ;
245250 case OP_RETVAL : return "retval" ;
251+ case OP_SELECT_GE : return "selectge" ;
252+ case OP_SELECT_GT : return "selectgt" ;
253+ case OP_SELECT_LE : return "selectle" ;
254+ case OP_SELECT_LT : return "selectlt" ;
246255 case OP_SUB : return "sub" ;
247256
248257 default :
249258 RUBY_ASSERT (false && "unknown opnd type" );
259+ return "unknown" ;
250260 }
251261}
252262
@@ -310,6 +320,18 @@ void ir_print_to_dot(jitstate_t *jit)
310320
311321#define ir_ccall (jit , ...) ir_push_insn(jit, OP_CCALL, __VA_ARGS__)
312322
323+ #define ir_cmov_ge (jit , opnd0 , opnd1 ) \
324+ ir_push_insn(jit, OP_CMOV_GE, opnd0, opnd1)
325+
326+ #define ir_cmov_gt (jit , opnd0 , opnd1 ) \
327+ ir_push_insn(jit, OP_CMOV_GT, opnd0, opnd1)
328+
329+ #define ir_cmov_le (jit , opnd0 , opnd1 ) \
330+ ir_push_insn(jit, OP_CMOV_LE, opnd0, opnd1)
331+
332+ #define ir_cmov_lt (jit , opnd0 , opnd1 ) \
333+ ir_push_insn(jit, OP_CMOV_LT, opnd0, opnd1)
334+
313335void ir_comment (jitstate_t * jit , ir_opnd_t opnd )
314336{
315337 ir_push_insn (jit , OP_COMMENT , opnd );
@@ -345,6 +367,12 @@ ir_opnd_t ir_mov(jitstate_t *jit, ir_opnd_t opnd0, ir_opnd_t opnd1)
345367 return ir_push_insn (jit , OP_MOV , opnd0 , opnd1 );
346368}
347369
370+ ir_opnd_t ir_cmp (jitstate_t * jit , ir_opnd_t opnd0 , ir_opnd_t opnd1 )
371+ {
372+ RUBY_ASSERT ((opnd0 .kind == EIR_INSN_OUT || opnd0 .kind == EIR_REG ) && "can only cmp with a EIR_INSN_OUT or EIR_REG" );
373+ return ir_push_insn (jit , OP_CMP , opnd0 , opnd1 );
374+ }
375+
348376#define ir_not (jit , opnd ) ir_push_insn(jit, OP_NOT, opnd)
349377
350378void ir_ret (jitstate_t * jit )
@@ -357,6 +385,18 @@ void ir_retval(jitstate_t *jit, ir_opnd_t opnd)
357385 ir_push_insn (jit , OP_RETVAL , opnd );
358386}
359387
388+ #define ir_select_ge (jit , opnd0 , opnd1 , opnd2 , opnd3 ) \
389+ ir_push_insn(jit, OP_SELECT_GE, opnd0, opnd1, opnd2, opnd3)
390+
391+ #define ir_select_gt (jit , opnd0 , opnd1 , opnd2 , opnd3 ) \
392+ ir_push_insn(jit, OP_SELECT_GT, opnd0, opnd1, opnd2, opnd3)
393+
394+ #define ir_select_le (jit , opnd0 , opnd1 , opnd2 , opnd3 ) \
395+ ir_push_insn(jit, OP_SELECT_LE, opnd0, opnd1, opnd2, opnd3)
396+
397+ #define ir_select_lt (jit , opnd0 , opnd1 , opnd2 , opnd3 ) \
398+ ir_push_insn(jit, OP_SELECT_LT, opnd0, opnd1, opnd2, opnd3)
399+
360400#define ir_sub (jit , opnd0 , opnd1 ) ir_push_insn(jit, OP_SUB, opnd0, opnd1)
361401
362402/*************************************************/
@@ -379,7 +419,9 @@ int32_t ir_next_scr_reg_idx(int32_t active[NUM_SCR_REGS])
379419 return index ;
380420 }
381421 }
422+
382423 RUBY_ASSERT (false && "out of free registers" );
424+ return -1 ;
383425}
384426
385427// When we're about to walk through the instructions and perform some kind of
@@ -629,11 +671,67 @@ void ir_alloc_regs(jitstate_t *jit)
629671 rb_darray_append (& jit -> insns , call );
630672 break ;
631673 }
674+ case OP_SELECT_GE :
675+ case OP_SELECT_GT :
676+ case OP_SELECT_LE :
677+ case OP_SELECT_LT : {
678+ ir_opnd_t left = ir_opnd (insn , 0 , allocations );
679+ ir_opnd_t right = ir_opnd (insn , 1 , allocations );
680+
681+ ir_opnd_t then_case = ir_opnd (insn , 2 , allocations );
682+ ir_opnd_t else_case = ir_opnd (insn , 3 , allocations );
683+
684+ // Always allocate a register, since we need a place for the
685+ // cases to live.
686+ int32_t allocated_index = ir_next_scr_reg_idx (active );
687+ ir_opnd_t allocated = SCR_REGS [allocated_index ];
688+
689+ allocations [insn_idx ] = allocated_index ;
690+ active [allocated_index ] = last_insn_index ;
691+
692+ ir_mov (jit , allocated , left );
693+ ir_cmp (jit , allocated , right );
694+
695+ // Now that the comparison is done, we can safely move the
696+ // else_case operand into the allocated register while
697+ // maintaining the comparison flags.
698+ ir_mov (jit , allocated , else_case );
699+
700+ if (then_case .kind != EIR_REG ) {
701+ // Since we don't have as register as the then_case operand,
702+ // we have to first allocate one and then mov the value into
703+ // that register.
704+ int32_t then_allocated_index = ir_next_scr_reg_idx (active );
705+ ir_opnd_t then_allocated = SCR_REGS [then_allocated_index ];
706+
707+ active [then_allocated_index ] = last_insn_index ;
708+ ir_mov (jit , then_allocated , then_case );
709+ then_case = then_allocated ;
710+ }
711+
712+ // Now we're going to conditionally move the then_case into the
713+ // allocated register depending on if the comparison flags line
714+ // up to the expected values depending on the current
715+ // instruction.
716+ switch (insn .op ) {
717+ case OP_SELECT_GE : ir_cmov_ge (jit , allocated , then_case ); break ;
718+ case OP_SELECT_GT : ir_cmov_gt (jit , allocated , then_case ); break ;
719+ case OP_SELECT_LE : ir_cmov_le (jit , allocated , then_case ); break ;
720+ case OP_SELECT_LT : ir_cmov_lt (jit , allocated , then_case ); break ;
721+ }
722+
723+ break ;
724+ }
632725 }
633726
634727 switch (insn .op ) {
635728 // These instructions are just copied over between passes.
636729 case OP_CALL :
730+ case OP_CMOV_GE :
731+ case OP_CMOV_GT :
732+ case OP_CMOV_LE :
733+ case OP_CMOV_LT :
734+ case OP_CMP :
637735 case OP_COMMENT :
638736 case OP_JUMP_OVF :
639737 case OP_LABEL :
@@ -755,6 +853,36 @@ void ir_gen_x86(codeblock_t *cb, jitstate_t *jit)
755853 call (cb , pointer );
756854 break ;
757855 }
856+ case OP_CMOV_GE : {
857+ x86opnd_t opnd0 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 0 ));
858+ x86opnd_t opnd1 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 1 ));
859+ cmovge (cb , opnd0 , opnd1 );
860+ break ;
861+ }
862+ case OP_CMOV_GT : {
863+ x86opnd_t opnd0 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 0 ));
864+ x86opnd_t opnd1 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 1 ));
865+ cmovg (cb , opnd0 , opnd1 );
866+ break ;
867+ }
868+ case OP_CMOV_LE : {
869+ x86opnd_t opnd0 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 0 ));
870+ x86opnd_t opnd1 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 1 ));
871+ cmovle (cb , opnd0 , opnd1 );
872+ break ;
873+ }
874+ case OP_CMOV_LT : {
875+ x86opnd_t opnd0 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 0 ));
876+ x86opnd_t opnd1 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 1 ));
877+ cmovl (cb , opnd0 , opnd1 );
878+ break ;
879+ }
880+ case OP_CMP : {
881+ x86opnd_t opnd0 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 0 ));
882+ x86opnd_t opnd1 = ir_gen_x86opnd (rb_darray_get (insn -> opnds , 1 ));
883+ cmp (cb , opnd0 , opnd1 );
884+ break ;
885+ }
758886 case OP_COMMENT :
759887 // Do nothing here until/unless we have a way to consistently
760888 // represent comments in the assembly
@@ -948,6 +1076,38 @@ void test_backend()
9481076 ir_retval (jit , ir_ccall (jit , ir_const_ptr ((void * )& ir_test_function_add ), ir_imm (3 ), ir_imm (4 )));
9491077 });
9501078
1079+ TEST ("select greater than or equal to" , 1 , {
1080+ ir_retval (jit , ir_select_ge (jit , ir_imm (3 ), ir_imm (3 ), ir_imm (1 ), ir_imm (2 )));
1081+ })
1082+
1083+ TEST ("select not greater than or equal to" , 2 , {
1084+ ir_retval (jit , ir_select_ge (jit , ir_imm (3 ), ir_imm (4 ), ir_imm (1 ), ir_imm (2 )));
1085+ })
1086+
1087+ TEST ("select greater than" , 1 , {
1088+ ir_retval (jit , ir_select_gt (jit , ir_imm (4 ), ir_imm (3 ), ir_imm (1 ), ir_imm (2 )));
1089+ })
1090+
1091+ TEST ("select not greater than" , 2 , {
1092+ ir_retval (jit , ir_select_gt (jit , ir_imm (3 ), ir_imm (3 ), ir_imm (1 ), ir_imm (2 )));
1093+ })
1094+
1095+ TEST ("select less than or equal to" , 1 , {
1096+ ir_retval (jit , ir_select_le (jit , ir_imm (3 ), ir_imm (3 ), ir_imm (1 ), ir_imm (2 )));
1097+ })
1098+
1099+ TEST ("select not less than or equal to" , 2 , {
1100+ ir_retval (jit , ir_select_le (jit , ir_imm (4 ), ir_imm (3 ), ir_imm (1 ), ir_imm (2 )));
1101+ })
1102+
1103+ TEST ("select less than" , 1 , {
1104+ ir_retval (jit , ir_select_lt (jit , ir_imm (3 ), ir_imm (4 ), ir_imm (1 ), ir_imm (2 )));
1105+ })
1106+
1107+ TEST ("select not less than" , 2 , {
1108+ ir_retval (jit , ir_select_lt (jit , ir_imm (3 ), ir_imm (3 ), ir_imm (1 ), ir_imm (2 )));
1109+ })
1110+
9511111 #undef TEST
9521112 printf ("Backend tests done\n" );
9531113}
0 commit comments