Some platforms like s390 and spu provide instructions that implement the math.h:signbit function. Using this instruction is faster and shorter than making a lib call, or using the optimization currently performend by builtins.c:expand_builtin_signbit. The signbit instruction also has the advantage that it avoids the local variable currently introduced by expand_builtin_signbit. This patch enhances the middle end with an interface via optabs so that back ends can provide insns named signbit_ if they like, and it also contains an appropriate implementation for s390. For s390, I also introduced a CCZ to INTEGER conversion and renamed the CMPINT UNSPEC to CCU_TO_INT. Verification: the patched compiler bootstraps and does not introduce new reg.test failures on s390 (which now has signbit insns) and on i686 (without signbit insn). I also checked the generated assembler code manually for some example programs using signbit on s390. Here is a suggestion for the Changelog: 2007-03-29 Wolfgang Gellerich * optabs.h: added declaration for signbit_optab. * optabs.c (init_optabs): added initialization for signbit_optab. * genoptinit.c: added handling of signbit insns. * builtins.c (expand_builtin_signbit): Added code to use a signbit insn, if available. * s390.h.c: Added named constants for bit masks controllong the test data class instruction * s390.c (s390_canonicalize_comparison): renamed CMPINT to CCU_TO_INT, added a CCU-like optimization for CCU_TO_INT * s390.md: added insns signbit_, TDC_insn_, ccz_to_int, renamed UNSPEC_CMPINT to UNSPEC_CCU_TO_INT, added UNSPEC_CCZ_TO_INT Regards, Wolfgang --- Dr. Wolfgang Gellerich IBM Deutschland Entwicklung GmbH Schönaicher Strasse 220 71032 Böblingen, Germany Tel. +49 / 7031 / 162598 gellerich@de.ibm.com ======================= IBM Deutschland Entwicklung GmbH Vorsitzender des Aufsichtsrats: Johann Weihen Geschäftsführung: Herbert Kircher Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294 Index: gcc/optabs.c =================================================================== --- gcc/optabs.c (Revision 123324) +++ gcc/optabs.c (Arbeitskopie) @@ -5584,6 +5584,7 @@ for (i = 0; i < NUM_MACHINE_MODES; i++) { movmem_optab[i] = CODE_FOR_nothing; + signbit_optab[i] = CODE_FOR_nothing; cmpstr_optab[i] = CODE_FOR_nothing; cmpstrn_optab[i] = CODE_FOR_nothing; cmpmem_optab[i] = CODE_FOR_nothing; Index: gcc/optabs.h =================================================================== --- gcc/optabs.h (Revision 123324) +++ gcc/optabs.h (Arbeitskopie) @@ -513,6 +513,9 @@ /* This array records the insn_code of insns to perform block moves. */ extern enum insn_code movmem_optab[NUM_MACHINE_MODES]; +/* This array records the insn_code of insns to implement the signbit function. */ +extern enum insn_code signbit_optab[NUM_MACHINE_MODES]; + /* This array records the insn_code of insns to perform block sets. */ extern enum insn_code setmem_optab[NUM_MACHINE_MODES]; Index: gcc/genopinit.c =================================================================== --- gcc/genopinit.c (Revision 123324) +++ gcc/genopinit.c (Arbeitskopie) @@ -172,6 +172,7 @@ "push_optab->handlers[$A].insn_code = CODE_FOR_$(push$a1$)", "reload_in_optab[$A] = CODE_FOR_$(reload_in$a$)", "reload_out_optab[$A] = CODE_FOR_$(reload_out$a$)", + "signbit_optab[$A] = CODE_FOR_$(signbit_$F$a$)", "movmem_optab[$A] = CODE_FOR_$(movmem$a$)", "cmpstr_optab[$A] = CODE_FOR_$(cmpstr$a$)", "cmpstrn_optab[$A] = CODE_FOR_$(cmpstrn$a$)", Index: gcc/builtins.c =================================================================== --- gcc/builtins.c (Revision 123324) +++ gcc/builtins.c (Arbeitskopie) @@ -230,6 +230,10 @@ int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t)); static tree do_mpfr_sincos (tree, tree, tree); +/* This array records the insn_code of insns to imlement the signbit function. */ +enum insn_code signbit_optab[NUM_MACHINE_MODES]; + + /* Return true if NODE should be considered for inline expansion regardless of the optimization level. This means whenever a function is invoked with its "internal" name, which normally contains the prefix "__builtin". */ @@ -5553,11 +5557,15 @@ return tramp; } -/* Expand a call to the built-in signbit, signbitf or signbitl function. - Return NULL_RTX if a normal call should be emitted rather than expanding - the function in-line. EXP is the expression that is a call to the builtin - function; if convenient, the result should be placed in TARGET. */ +/* Expand a call to the built-in signbit, signbitf or signbitl function. The + function first checks whether the back end provides an insn to implement + signbit for the respective mode. If not, it checks whether the floating + point format of the value is such that the sign bit can be extracted. If + that is not the case, the function returns NULL_RTX to indicate that a + normal call should be emitted rather than expanding the function in-line. + EXP is the expression that is a call to the builtin function; if + convenient, the result should be placed in TARGET. */ static rtx expand_builtin_signbit (tree exp, rtx target) { @@ -5566,6 +5574,9 @@ HOST_WIDE_INT hi, lo; tree arg; int word, bitpos; + + enum insn_code signbit_insn_code; + rtx temp; if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE)) @@ -5576,6 +5587,28 @@ rmode = TYPE_MODE (TREE_TYPE (exp)); fmt = REAL_MODE_FORMAT (fmode); + /* Expand the argument yielding a RTX expression. */ + temp = expand_normal (arg); + + /* Check if the back end provides an insn that handles signbit for the + argument's mode. */ + signbit_insn_code = signbit_optab [(int) fmode]; + if (signbit_insn_code != CODE_FOR_nothing) + { + rtx result = gen_reg_rtx (SImode); + rtx signbit_insn; + + if (!insn_data[(int) signbit_insn_code].operand[1].predicate (temp, fmode)) + temp = force_reg (fmode, temp); + + signbit_insn = GEN_FCN (signbit_insn_code) (temp, result); + if (signbit_insn == NULL_RTX) + gcc_unreachable (); + emit_insn (signbit_insn); + + return result; + } + /* For floating point formats without a sign bit, implement signbit as "ARG < 0.0". */ bitpos = fmt->signbit_ro; @@ -5590,7 +5623,6 @@ return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL); } - temp = expand_normal (arg); if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD) { imode = int_mode_for_mode (fmode); @@ -5653,6 +5685,7 @@ return temp; } + /* Expand fork or exec calls. TARGET is the desired target of the call. EXP is the call. FN is the identificator of the actual function. IGNORE is nonzero if the Index: gcc/config/s390/s390.c =================================================================== --- gcc/config/s390/s390.c (Revision 123324) +++ gcc/config/s390/s390.c (Arbeitskopie) @@ -700,9 +700,9 @@ } - /* Remove redundant UNSPEC_CMPINT conversions if possible. */ + /* Remove redundant UNSPEC_CCU_TO_INT conversions if possible. */ if (GET_CODE (*op0) == UNSPEC - && XINT (*op0, 1) == UNSPEC_CMPINT + && XINT (*op0, 1) == UNSPEC_CCU_TO_INT && XVECLEN (*op0, 0) == 1 && GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode && GET_CODE (XVECEXP (*op0, 0, 0)) == REG @@ -728,6 +728,32 @@ } } + + /* Remove redundant UNSPEC_CCZ_TO_INT conversions if possible. */ + if (GET_CODE (*op0) == UNSPEC + && XINT (*op0, 1) == UNSPEC_CCZ_TO_INT + && XVECLEN (*op0, 0) == 1 + && GET_MODE (XVECEXP (*op0, 0, 0)) == CCZmode + && GET_CODE (XVECEXP (*op0, 0, 0)) == REG + && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM + && *op1 == const0_rtx) + { + enum rtx_code new_code = UNKNOWN; + switch (*code) + { + case EQ: new_code = EQ; break; + case NE: new_code = NE; break; + default: break; + } + + if (new_code != UNKNOWN) + { + *op0 = XVECEXP (*op0, 0, 0); + *code = new_code; + } + } + + /* Simplify cascaded EQ, NE with const0_rtx. */ if ((*code == NE || *code == EQ) && (GET_CODE (*op0) == EQ || GET_CODE (*op0) == NE) @@ -753,6 +779,8 @@ } } + + /* Emit a compare instruction suitable to implement the comparison OP0 CODE OP1. Return the correct condition RTL to be placed in the IF_THEN_ELSE of the conditional branch testing the result. */ Index: gcc/config/s390/s390.h =================================================================== --- gcc/config/s390/s390.h (Revision 123324) +++ gcc/config/s390/s390.h (Arbeitskopie) @@ -146,7 +146,27 @@ /* Frame pointer is not used for debugging. */ #define CAN_DEBUG_WITHOUT_FP +/* Constants needed to control the TEST DATA CLASS (TDC) instruction. */ +#define s390_TDC_positive_Zero (1 << 11) +#define s390_TDC_negative_Zero (1 << 10) +#define s390_TDC_positive_normalized_Number (1 << 9) +#define s390_TDC_negative_normalized_Number (1 << 8) +#define s390_TDC_positive_denormalized_Number (1 << 7) +#define s390_TDC_negative_denormalized_Number (1 << 6) +#define s390_TDC_positive_Infinity (1 << 5) +#define s390_TDC_negative_Infinity (1 << 4) +#define s390_TDC_positive_quite_NaN (1 << 3) +#define s390_TDC_negative_quite_NaN (1 << 2) +#define s390_TDC_positive_signaling_NaN (1 << 1) +#define s390_TDC_negative_signaling_NaN (1 << 0) +#define s390_TDC_signbit_set (s390_TDC_negative_Zero \ + | s390_TDC_negative_normalized_Number \ + | s390_TDC_negative_denormalized_Number\ + | s390_TDC_negative_Infinity \ + | s390_TDC_negative_quite_NaN \ + | s390_TDC_negative_signaling_NaN ) + /* In libgcc2, determine target settings as compile-time constants. */ #ifdef IN_LIBGCC2 #undef TARGET_64BIT Index: gcc/config/s390/s390.md =================================================================== --- gcc/config/s390/s390.md (Revision 123324) +++ gcc/config/s390/s390.md (Arbeitskopie) @@ -59,7 +59,8 @@ (define_constants [; Miscellaneous (UNSPEC_ROUND 1) - (UNSPEC_CMPINT 2) + (UNSPEC_CCU_TO_INT 2) + (UNSPEC_CCZ_TO_INT 3) (UNSPEC_ICM 10) ; GOT/PLT and lt-relative accesses @@ -97,11 +98,15 @@ ; Stack Smashing Protector (UNSPEC_SP_SET 700) (UNSPEC_SP_TEST 701) - + ; Copy sign instructions (UNSPEC_COPYSIGN 800) + + ; Test Data Class (TDC) + (UNSPEC_TDC_INSN 900) ]) + ;; ;; UNSPEC_VOLATILE usage ;; @@ -2090,7 +2095,7 @@ (use (reg:SI 0))]) (parallel [(set (match_operand:SI 0 "register_operand" "=d") - (unspec:SI [(reg:CCU CC_REGNUM)] UNSPEC_CMPINT)) + (unspec:SI [(reg:CCU CC_REGNUM)] UNSPEC_CCU_TO_INT)) (clobber (reg:CC CC_REGNUM))])] "" { @@ -2288,7 +2293,56 @@ [(set_attr "length" "8") (set_attr "type" "vs")]) + + + ; +; Test data class. +; + +(define_expand "signbit_" + [(set (reg:CCZ CC_REGNUM) + (unspec:CCZ [(match_operand:BFP 0 "register_operand" "f") + (match_dup 2)] + UNSPEC_TDC_INSN)) + (parallel + [ (set (match_operand:SI 1 "register_operand" "=d") + (unspec:SI [(reg:CCZ CC_REGNUM)] UNSPEC_CCZ_TO_INT)) + (clobber (reg:CC CC_REGNUM))]) + ] + "TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" + { + operands[2] = GEN_INT (s390_TDC_signbit_set); + } +) + + +; This insn is used to generate all variants of the Test Data Class +; instruction. The insn's first operand is the register to be tested +; and the second one is the bit mask specifying the required test(s). +; +(define_insn "*TDC_insn_" + [(set (reg:CCZ CC_REGNUM) + (unspec:CCZ [(match_operand:BFP 0 "register_operand" "f") + (match_operand:SI 1 "const_int_operand")] UNSPEC_TDC_INSN)) + ] + "TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" + "tcb\t%0,%1" +) + + +(define_insn_and_split "*ccz_to_int" + [(set (match_operand:SI 0 "register_operand" "=d") + (unspec:SI [(match_operand:CCZ 1 "register_operand" "0")] + UNSPEC_CCZ_TO_INT)) + (clobber (reg:CC CC_REGNUM))] + "" + "#" + "reload_completed" + [(set (match_dup 0) (lshiftrt:SI (match_dup 0) (const_int 28)))]) + + +; ; setmemM instruction pattern(s). ; @@ -2564,7 +2618,7 @@ (define_insn_and_split "cmpint" [(set (match_operand:SI 0 "register_operand" "=d") (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] - UNSPEC_CMPINT)) + UNSPEC_CCU_TO_INT)) (clobber (reg:CC CC_REGNUM))] "" "#" @@ -2577,10 +2631,10 @@ (define_insn_and_split "*cmpint_cc" [(set (reg CC_REGNUM) (compare (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] - UNSPEC_CMPINT) + UNSPEC_CCU_TO_INT) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=d") - (unspec:SI [(match_dup 1)] UNSPEC_CMPINT))] + (unspec:SI [(match_dup 1)] UNSPEC_CCU_TO_INT))] "s390_match_ccmode (insn, CCSmode)" "#" "&& reload_completed" @@ -2597,7 +2651,7 @@ (define_insn_and_split "*cmpint_sign" [(set (match_operand:DI 0 "register_operand" "=d") (sign_extend:DI (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] - UNSPEC_CMPINT))) + UNSPEC_CCU_TO_INT))) (clobber (reg:CC CC_REGNUM))] "TARGET_64BIT" "#" @@ -2611,11 +2665,11 @@ [(set (reg CC_REGNUM) (compare (ashiftrt:DI (ashift:DI (subreg:DI (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] - UNSPEC_CMPINT) 0) + UNSPEC_CCU_TO_INT) 0) (const_int 32)) (const_int 32)) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=d") - (sign_extend:DI (unspec:SI [(match_dup 1)] UNSPEC_CMPINT)))] + (sign_extend:DI (unspec:SI [(match_dup 1)] UNSPEC_CCU_TO_INT)))] "s390_match_ccmode (insn, CCSmode) && TARGET_64BIT" "#" "&& reload_completed"