This patch adds suppport for the following VIS instructions, which are introduced in the Oracle SPARC Architecture 2017 and implemented by the SPARC M8 cpu:
- Dictionary unpack. - Partitioned compare with shifted result. - Unsigned partitioned compare with shifted result. - Partitioned dual-equal compare with shifted result. - Partitioned unsigned range compare with shifted result. The facilities introduced are: - A new option -mvis4b. - Compiler built-ins for the above mentioned instructions. Tests and documentation are also provided. gcc/ChangeLog: * config/sparc/sparc.opt: New option -mvis4b. * config/sparc/sparc.c (dump_target_flag_bits): Handle MASK_VIS4B. (sparc_option_override): Handle VIS4B. (enum sparc_builtins): Define SPARC_BUILTIN_DICTUNPACK{8,16,32}, SPARC_BUILTIN_FPCMP{LE,GT,EQ,NE}{8,16,32}SHL, SPARC_BUILTIN_FPCMPU{LE,GT}{8,16,32}SHL, SPARC_BUILTIN_FPCMPDE{8,16,32}SHL and SPARC_BUILTIN_FPCMPUR{8,16,32}SHL. (check_constant_argument): New function. (sparc_vis_init_builtins): Define builtins __builtin_vis_dictunpack{8,16,32}, __builtin_vis_fpcmp{le,gt,eq,ne}{8,16,32}shl, __builtin_vis_fpcmpu{le,gt}{8,16,32}shl, __builtin_vis_fpcmpde{8,16,32}shl and __builtin_vis_fpcmpur{8,16,32}shl. (sparc_expand_builtin): Check that the constant operands to __builtin_vis_fpcmp*shl and _builtin_vis_dictunpack* are indeed constant and in range. * config/sparc/sparc-c.c (sparc_target_macros): Handle TARGET_VIS4B. * config/sparc/sparc.h (SPARC_IMM2_P): Define. (SPARC_IMM5_P): Likewise. * config/sparc/sparc.md (cpu_feature): Add new feagure "vis4b". (enabled): Handle vis4b. (UNSPEC_DICTUNPACK): New unspec. (UNSPEC_FPCMPSHL): Likewise. (UNSPEC_FPUCMPSHL): Likewise. (UNSPEC_FPCMPDESHL): Likewise. (UNSPEC_FPCMPURSHL): Likewise. (cpu_feature): New CPU feature `vis4b'. (dictunpack{8,16,32}): New insns. (FPCSMODE): New mode iterator. (fpcscond): New code iterator. (fpcsucond): Likewise. (fpcmp{le,gt,eq,ne}{8,16,32}{si,di}shl): New insns. (fpcmpu{le,gt}{8,16,32}{si,di}shl): Likewise. (fpcmpde{8,16,32}{si,di}shl): Likewise. (fpcmpur{8,16,32}{si,di}shl): Likewise. * config/sparc/constraints.md: Define constraints `q' for unsigned 2-bit integer constants and `t' for unsigned 5-bit integer constants. * config/sparc/predicates.md (imm5_operand_dictunpack8): New predicate. (imm5_operand_dictunpack16): Likewise. (imm5_operand_dictunpack32): Likewise. (imm2_operand): Likewise. * doc/invoke.texi (SPARC Options): Document -mvis4b. * doc/extend.texi (SPARC VIS Built-in Functions): Document the ditunpack* and fpcmp*shl builtins. gcc/testsuite/ChangeLog: * gcc.target/sparc/dictunpack.c: New file. * gcc.target/sparc/fpcmpdeshl.c: Likewise. * gcc.target/sparc/fpcmpshl.c: Likewise. * gcc.target/sparc/fpcmpurshl.c: Likewise. * gcc.target/sparc/fpcmpushl.c: Likewise. --- gcc/ChangeLog | 53 ++++++ gcc/config/sparc/constraints.md | 12 +- gcc/config/sparc/predicates.md | 27 +++ gcc/config/sparc/sparc-c.c | 7 +- gcc/config/sparc/sparc.c | 247 +++++++++++++++++++++++++++- gcc/config/sparc/sparc.h | 4 + gcc/config/sparc/sparc.md | 73 +++++++- gcc/config/sparc/sparc.opt | 4 + gcc/doc/extend.texi | 39 +++++ gcc/doc/invoke.texi | 13 ++ gcc/testsuite/ChangeLog | 8 + gcc/testsuite/gcc.target/sparc/dictunpack.c | 25 +++ gcc/testsuite/gcc.target/sparc/fpcmpdeshl.c | 25 +++ gcc/testsuite/gcc.target/sparc/fpcmpshl.c | 81 +++++++++ gcc/testsuite/gcc.target/sparc/fpcmpurshl.c | 25 +++ gcc/testsuite/gcc.target/sparc/fpcmpushl.c | 43 +++++ 16 files changed, 677 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/gcc.target/sparc/dictunpack.c create mode 100644 gcc/testsuite/gcc.target/sparc/fpcmpdeshl.c create mode 100644 gcc/testsuite/gcc.target/sparc/fpcmpshl.c create mode 100644 gcc/testsuite/gcc.target/sparc/fpcmpurshl.c create mode 100644 gcc/testsuite/gcc.target/sparc/fpcmpushl.c diff --git a/gcc/config/sparc/constraints.md b/gcc/config/sparc/constraints.md index 7c9ef74..cff5a61 100644 --- a/gcc/config/sparc/constraints.md +++ b/gcc/config/sparc/constraints.md @@ -19,7 +19,7 @@ ;;; Unused letters: ;;; B -;;; a jkl q tuv xyz +;;; a jkl uv xyz ;; Register constraints @@ -58,6 +58,16 @@ ;; Integer constant constraints +(define_constraint "q" + "Unsigned 2-bit integer constant" + (and (match_code "const_int") + (match_test "SPARC_IMM2_P (ival)"))) + +(define_constraint "t" + "Unsigned 5-bit integer constant" + (and (match_code "const_int") + (match_test "SPARC_IMM5_P (ival)"))) + (define_constraint "A" "Signed 5-bit integer constant" (and (match_code "const_int") diff --git a/gcc/config/sparc/predicates.md b/gcc/config/sparc/predicates.md index 951933e..3f8526d 100644 --- a/gcc/config/sparc/predicates.md +++ b/gcc/config/sparc/predicates.md @@ -328,6 +328,33 @@ (and (match_code "const_int") (match_test "SPARC_SIMM5_P (INTVAL (op))")))) +;; Return true if OP is a constant in the range 0..7. This is an +;; acceptable second operand for dictunpack instructions setting a +;; V8QI mode in the destination register. +(define_predicate "imm5_operand_dictunpack8" + (and (match_code "const_int") + (match_test "(INTVAL (op) >= 0 && INTVAL (op) < 8)"))) + +;; Return true if OP is a constant in the range 7..15. This is an +;; acceptable second operand for dictunpack instructions setting a +;; V4HI mode in the destination register. +(define_predicate "imm5_operand_dictunpack16" + (and (match_code "const_int") + (match_test "(INTVAL (op) >= 8 && INTVAL (op) < 16)"))) + +;; Return true if OP is a constant in the range 15..31. This is an +;; acceptable second operand for dictunpack instructions setting a +;; V2SI mode in the destination register. +(define_predicate "imm5_operand_dictunpack32" + (and (match_code "const_int") + (match_test "(INTVAL (op) >= 16 && INTVAL (op) < 32)"))) + +;; Return true if OP is a constant that is representable by a 2-bit +;; unsigned field. This is an acceptable third operand for +;; fpcmp*shl instructions. +(define_predicate "imm2_operand" + (and (match_code "const_int") + (match_test "SPARC_IMM2_P (INTVAL (op))"))) ;; Predicates for miscellaneous instructions. diff --git a/gcc/config/sparc/sparc-c.c b/gcc/config/sparc/sparc-c.c index 9603173..4aacfff 100644 --- a/gcc/config/sparc/sparc-c.c +++ b/gcc/config/sparc/sparc-c.c @@ -40,7 +40,12 @@ sparc_target_macros (void) cpp_assert (parse_in, "machine=sparc"); } - if (TARGET_VIS4) + if (TARGET_VIS4B) + { + cpp_define (parse_in, "__VIS__=0x410"); + cpp_define (parse_in, "__VIS=0x410"); + } + else if (TARGET_VIS4) { cpp_define (parse_in, "__VIS__=0x400"); cpp_define (parse_in, "__VIS=0x400"); diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index 0644ab5..9f9a29a 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -1246,6 +1246,8 @@ dump_target_flag_bits (const int flags) fprintf (stderr, "VIS3 "); if (flags & MASK_VIS4) fprintf (stderr, "VIS4 "); + if (flags & MASK_VIS4B) + fprintf (stderr, "VIS4B "); if (flags & MASK_CBCOND) fprintf (stderr, "CBCOND "); if (flags & MASK_DEPRECATED_V8_INSNS) @@ -1365,7 +1367,8 @@ sparc_option_override (void) MASK_V9|MASK_POPC|MASK_VIS4|MASK_FMAF|MASK_CBCOND|MASK_SUBXC }, /* UltraSPARC M8 */ { "m8", MASK_ISA, - MASK_V9|MASK_POPC|MASK_VIS4|MASK_FMAF|MASK_CBCOND|MASK_SUBXC } + MASK_V9|MASK_POPC|MASK_VIS4|MASK_FMAF|MASK_CBCOND|MASK_SUBXC + |MASK_VIS4B } }; const struct cpu_table *cpu; unsigned int i; @@ -1495,6 +1498,9 @@ sparc_option_override (void) #ifndef HAVE_AS_SPARC5_VIS4 & ~(MASK_VIS4 | MASK_SUBXC) #endif +#ifndef HAVE_AS_SPARC6 + & ~(MASK_VIS4B) +#endif #ifndef HAVE_AS_LEON & ~(MASK_LEON | MASK_LEON3) #endif @@ -1513,11 +1519,15 @@ sparc_option_override (void) if (TARGET_VIS4) target_flags |= MASK_VIS3 | MASK_VIS2 | MASK_VIS; - /* Don't allow -mvis, -mvis2, -mvis3, -mvis4 or -mfmaf if FPU is - disabled. */ + /* -mvis4b implies -mvis4, -mvis3, -mvis2 and -mvis */ + if (TARGET_VIS4B) + target_flags |= MASK_VIS4 | MASK_VIS3 | MASK_VIS2 | MASK_VIS; + + /* Don't allow -mvis, -mvis2, -mvis3, -mvis4, -mvis4b and -mfmaf if + FPU is disabled. */ if (! TARGET_FPU) target_flags &= ~(MASK_VIS | MASK_VIS2 | MASK_VIS3 | MASK_VIS4 - | MASK_FMAF); + | MASK_VIS4B | MASK_FMAF); /* -mvis assumes UltraSPARC+, so we are sure v9 instructions are available; -m64 also implies v9. */ @@ -10384,6 +10394,45 @@ enum sparc_builtins SPARC_BUILTIN_FPSUBS8, SPARC_BUILTIN_FPSUBUS8, SPARC_BUILTIN_FPSUBUS16, + + /* VIS 4.0B builtins. */ + + /* Note that all the DICTUNPACK* entries should be kept + contiguous. */ + SPARC_BUILTIN_FIRST_DICTUNPACK, + SPARC_BUILTIN_DICTUNPACK8 = SPARC_BUILTIN_FIRST_DICTUNPACK, + SPARC_BUILTIN_DICTUNPACK16, + SPARC_BUILTIN_DICTUNPACK32, + SPARC_BUILTIN_LAST_DICTUNPACK = SPARC_BUILTIN_DICTUNPACK32, + + /* Note that all the FPCMP*SHL entries should be kept + contiguous. */ + SPARC_BUILTIN_FIRST_FPCMPSHL, + SPARC_BUILTIN_FPCMPLE8SHL = SPARC_BUILTIN_FIRST_FPCMPSHL, + SPARC_BUILTIN_FPCMPGT8SHL, + SPARC_BUILTIN_FPCMPEQ8SHL, + SPARC_BUILTIN_FPCMPNE8SHL, + SPARC_BUILTIN_FPCMPLE16SHL, + SPARC_BUILTIN_FPCMPGT16SHL, + SPARC_BUILTIN_FPCMPEQ16SHL, + SPARC_BUILTIN_FPCMPNE16SHL, + SPARC_BUILTIN_FPCMPLE32SHL, + SPARC_BUILTIN_FPCMPGT32SHL, + SPARC_BUILTIN_FPCMPEQ32SHL, + SPARC_BUILTIN_FPCMPNE32SHL, + SPARC_BUILTIN_FPCMPULE8SHL, + SPARC_BUILTIN_FPCMPUGT8SHL, + SPARC_BUILTIN_FPCMPULE16SHL, + SPARC_BUILTIN_FPCMPUGT16SHL, + SPARC_BUILTIN_FPCMPULE32SHL, + SPARC_BUILTIN_FPCMPUGT32SHL, + SPARC_BUILTIN_FPCMPDE8SHL, + SPARC_BUILTIN_FPCMPDE16SHL, + SPARC_BUILTIN_FPCMPDE32SHL, + SPARC_BUILTIN_FPCMPUR8SHL, + SPARC_BUILTIN_FPCMPUR16SHL, + SPARC_BUILTIN_FPCMPUR32SHL, + SPARC_BUILTIN_LAST_FPCMPSHL = SPARC_BUILTIN_FPCMPUR32SHL, SPARC_BUILTIN_MAX }; @@ -10391,6 +10440,27 @@ enum sparc_builtins static GTY (()) tree sparc_builtins[(int) SPARC_BUILTIN_MAX]; static enum insn_code sparc_builtins_icode[(int) SPARC_BUILTIN_MAX]; +/* Return true if OPVAL can be used for operand OPNUM of instruction ICODE. + The instruction should require a constant operand of some sort. The + function prints an error if OPVAL is not valid. */ + +static int +check_constant_argument (enum insn_code icode, int opnum, rtx opval) +{ + if (GET_CODE (opval) != CONST_INT) + { + error ("%qs expects a constant argument", insn_data[icode].name); + return false; + } + + if (!(*insn_data[icode].operand[opnum].predicate) (opval, VOIDmode)) + { + error ("constant argument out of range for %qs", insn_data[icode].name); + return false; + } + return true; +} + /* Add a SPARC builtin function with NAME, ICODE, CODE and TYPE. Return the function decl or NULL_TREE if the builtin was not added. */ @@ -10484,6 +10554,12 @@ sparc_vis_init_builtins (void) v8qi, v8qi, 0); tree si_ftype_v8qi_v8qi = build_function_type_list (intSI_type_node, v8qi, v8qi, 0); + tree v8qi_ftype_df_si = build_function_type_list (v8qi, double_type_node, + intSI_type_node, 0); + tree v4hi_ftype_df_si = build_function_type_list (v4hi, double_type_node, + intSI_type_node, 0); + tree v2si_ftype_df_si = build_function_type_list (v2si, double_type_node, + intDI_type_node, 0); tree di_ftype_di_di = build_function_type_list (intDI_type_node, intDI_type_node, intDI_type_node, 0); @@ -10938,6 +11014,156 @@ sparc_vis_init_builtins (void) def_builtin_const ("__builtin_vis_fpsubus16", CODE_FOR_ussubv4hi3, SPARC_BUILTIN_FPSUBUS16, v4hi_ftype_v4hi_v4hi); } + + if (TARGET_VIS4B) + { + def_builtin_const ("__builtin_vis_dictunpack8", CODE_FOR_dictunpack8, + SPARC_BUILTIN_DICTUNPACK8, v8qi_ftype_df_si); + def_builtin_const ("__builtin_vis_dictunpack16", CODE_FOR_dictunpack16, + SPARC_BUILTIN_DICTUNPACK16, v4hi_ftype_df_si); + def_builtin_const ("__builtin_vis_dictunpack32", CODE_FOR_dictunpack32, + SPARC_BUILTIN_DICTUNPACK32, v2si_ftype_df_si); + + if (TARGET_ARCH64) + { + tree di_ftype_v8qi_v8qi_si = build_function_type_list (intDI_type_node, + v8qi, v8qi, + intSI_type_node, 0); + tree di_ftype_v4hi_v4hi_si = build_function_type_list (intDI_type_node, + v4hi, v4hi, + intSI_type_node, 0); + tree di_ftype_v2si_v2si_si = build_function_type_list (intDI_type_node, + v2si, v2si, + intSI_type_node, 0); + + def_builtin_const ("__builtin_vis_fpcmple8shl", CODE_FOR_fpcmple8dishl, + SPARC_BUILTIN_FPCMPLE8SHL, di_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpgt8shl", CODE_FOR_fpcmpgt8dishl, + SPARC_BUILTIN_FPCMPGT8SHL, di_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpeq8shl", CODE_FOR_fpcmpeq8dishl, + SPARC_BUILTIN_FPCMPEQ8SHL, di_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpne8shl", CODE_FOR_fpcmpne8dishl, + SPARC_BUILTIN_FPCMPNE8SHL, di_ftype_v8qi_v8qi_si); + + def_builtin_const ("__builtin_vis_fpcmple16shl", CODE_FOR_fpcmple16dishl, + SPARC_BUILTIN_FPCMPLE16SHL, di_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpgt16shl", CODE_FOR_fpcmpgt16dishl, + SPARC_BUILTIN_FPCMPGT16SHL, di_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpeq16shl", CODE_FOR_fpcmpeq16dishl, + SPARC_BUILTIN_FPCMPEQ16SHL, di_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpne16shl", CODE_FOR_fpcmpne16dishl, + SPARC_BUILTIN_FPCMPNE16SHL, di_ftype_v4hi_v4hi_si); + + def_builtin_const ("__builtin_vis_fpcmple32shl", CODE_FOR_fpcmple32dishl, + SPARC_BUILTIN_FPCMPLE32SHL, di_ftype_v2si_v2si_si); + def_builtin_const ("__builtin_vis_fpcmpgt32shl", CODE_FOR_fpcmpgt32dishl, + SPARC_BUILTIN_FPCMPGT32SHL, di_ftype_v2si_v2si_si); + def_builtin_const ("__builtin_vis_fpcmpeq32shl", CODE_FOR_fpcmpeq32dishl, + SPARC_BUILTIN_FPCMPEQ32SHL, di_ftype_v2si_v2si_si); + def_builtin_const ("__builtin_vis_fpcmpne32shl", CODE_FOR_fpcmpne32dishl, + SPARC_BUILTIN_FPCMPNE32SHL, di_ftype_v2si_v2si_si); + + + def_builtin_const ("__builtin_vis_fpcmpule8shl", CODE_FOR_fpcmpule8dishl, + SPARC_BUILTIN_FPCMPULE8SHL, di_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpugt8shl", CODE_FOR_fpcmpugt8dishl, + SPARC_BUILTIN_FPCMPUGT8SHL, di_ftype_v8qi_v8qi_si); + + def_builtin_const ("__builtin_vis_fpcmpule16shl", CODE_FOR_fpcmpule16dishl, + SPARC_BUILTIN_FPCMPULE16SHL, di_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpugt16shl", CODE_FOR_fpcmpugt16dishl, + SPARC_BUILTIN_FPCMPUGT16SHL, di_ftype_v4hi_v4hi_si); + + def_builtin_const ("__builtin_vis_fpcmpule32shl", CODE_FOR_fpcmpule32dishl, + SPARC_BUILTIN_FPCMPULE32SHL, di_ftype_v2si_v2si_si); + def_builtin_const ("__builtin_vis_fpcmpugt32shl", CODE_FOR_fpcmpugt32dishl, + SPARC_BUILTIN_FPCMPUGT32SHL, di_ftype_v2si_v2si_si); + + def_builtin_const ("__builtin_vis_fpcmpde8shl", CODE_FOR_fpcmpde8dishl, + SPARC_BUILTIN_FPCMPDE8SHL, di_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpde16shl", CODE_FOR_fpcmpde16dishl, + SPARC_BUILTIN_FPCMPDE16SHL, di_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpde32shl", CODE_FOR_fpcmpde32dishl, + SPARC_BUILTIN_FPCMPDE32SHL, di_ftype_v2si_v2si_si); + + def_builtin_const ("__builtin_vis_fpcmpur8shl", CODE_FOR_fpcmpur8dishl, + SPARC_BUILTIN_FPCMPUR8SHL, di_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpur16shl", CODE_FOR_fpcmpur16dishl, + SPARC_BUILTIN_FPCMPUR16SHL, di_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpur32shl", CODE_FOR_fpcmpur32dishl, + SPARC_BUILTIN_FPCMPUR32SHL, di_ftype_v2si_v2si_si); + + } + else + { + tree si_ftype_v8qi_v8qi_si = build_function_type_list (intSI_type_node, + v8qi, v8qi, + intSI_type_node, 0); + tree si_ftype_v4hi_v4hi_si = build_function_type_list (intSI_type_node, + v4hi, v4hi, + intSI_type_node, 0); + tree si_ftype_v2si_v2si_si = build_function_type_list (intSI_type_node, + v2si, v2si, + intSI_type_node, 0); + + def_builtin_const ("__builtin_vis_fpcmple8shl", CODE_FOR_fpcmple8sishl, + SPARC_BUILTIN_FPCMPLE8SHL, si_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpgt8shl", CODE_FOR_fpcmpgt8sishl, + SPARC_BUILTIN_FPCMPGT8SHL, si_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpeq8shl", CODE_FOR_fpcmpeq8sishl, + SPARC_BUILTIN_FPCMPEQ8SHL, si_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpne8shl", CODE_FOR_fpcmpne8sishl, + SPARC_BUILTIN_FPCMPNE8SHL, si_ftype_v8qi_v8qi_si); + + def_builtin_const ("__builtin_vis_fpcmple16shl", CODE_FOR_fpcmple16sishl, + SPARC_BUILTIN_FPCMPLE16SHL, si_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpgt16shl", CODE_FOR_fpcmpgt16sishl, + SPARC_BUILTIN_FPCMPGT16SHL, si_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpeq16shl", CODE_FOR_fpcmpeq16sishl, + SPARC_BUILTIN_FPCMPEQ16SHL, si_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpne16shl", CODE_FOR_fpcmpne16sishl, + SPARC_BUILTIN_FPCMPNE16SHL, si_ftype_v4hi_v4hi_si); + + def_builtin_const ("__builtin_vis_fpcmple32shl", CODE_FOR_fpcmple32sishl, + SPARC_BUILTIN_FPCMPLE32SHL, si_ftype_v2si_v2si_si); + def_builtin_const ("__builtin_vis_fpcmpgt32shl", CODE_FOR_fpcmpgt32sishl, + SPARC_BUILTIN_FPCMPGT32SHL, si_ftype_v2si_v2si_si); + def_builtin_const ("__builtin_vis_fpcmpeq32shl", CODE_FOR_fpcmpeq32sishl, + SPARC_BUILTIN_FPCMPEQ32SHL, si_ftype_v2si_v2si_si); + def_builtin_const ("__builtin_vis_fpcmpne32shl", CODE_FOR_fpcmpne32sishl, + SPARC_BUILTIN_FPCMPNE32SHL, si_ftype_v2si_v2si_si); + + + def_builtin_const ("__builtin_vis_fpcmpule8shl", CODE_FOR_fpcmpule8sishl, + SPARC_BUILTIN_FPCMPULE8SHL, si_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpugt8shl", CODE_FOR_fpcmpugt8sishl, + SPARC_BUILTIN_FPCMPUGT8SHL, si_ftype_v8qi_v8qi_si); + + def_builtin_const ("__builtin_vis_fpcmpule16shl", CODE_FOR_fpcmpule16sishl, + SPARC_BUILTIN_FPCMPULE16SHL, si_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpugt16shl", CODE_FOR_fpcmpugt16sishl, + SPARC_BUILTIN_FPCMPUGT16SHL, si_ftype_v4hi_v4hi_si); + + def_builtin_const ("__builtin_vis_fpcmpule32shl", CODE_FOR_fpcmpule32sishl, + SPARC_BUILTIN_FPCMPULE32SHL, si_ftype_v2si_v2si_si); + def_builtin_const ("__builtin_vis_fpcmpugt32shl", CODE_FOR_fpcmpugt32sishl, + SPARC_BUILTIN_FPCMPUGT32SHL, si_ftype_v2si_v2si_si); + + def_builtin_const ("__builtin_vis_fpcmpde8shl", CODE_FOR_fpcmpde8sishl, + SPARC_BUILTIN_FPCMPDE8SHL, si_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpde16shl", CODE_FOR_fpcmpde16sishl, + SPARC_BUILTIN_FPCMPDE16SHL, si_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpde32shl", CODE_FOR_fpcmpde32sishl, + SPARC_BUILTIN_FPCMPDE32SHL, si_ftype_v2si_v2si_si); + + def_builtin_const ("__builtin_vis_fpcmpur8shl", CODE_FOR_fpcmpur8sishl, + SPARC_BUILTIN_FPCMPUR8SHL, si_ftype_v8qi_v8qi_si); + def_builtin_const ("__builtin_vis_fpcmpur16shl", CODE_FOR_fpcmpur16sishl, + SPARC_BUILTIN_FPCMPUR16SHL, si_ftype_v4hi_v4hi_si); + def_builtin_const ("__builtin_vis_fpcmpur32shl", CODE_FOR_fpcmpur32sishl, + SPARC_BUILTIN_FPCMPUR32SHL, si_ftype_v2si_v2si_si); + } + } } /* Implement TARGET_BUILTIN_DECL hook. */ @@ -10992,6 +11218,19 @@ sparc_expand_builtin (tree exp, rtx target, insn_op = &insn_data[icode].operand[idx]; op[arg_count] = expand_normal (arg); + /* Some of the builtins require constant arguments. We check + for this here. */ + if ((code >= SPARC_BUILTIN_FIRST_FPCMPSHL + && code <= SPARC_BUILTIN_LAST_FPCMPSHL + && arg_count == 3) + || (code >= SPARC_BUILTIN_FIRST_DICTUNPACK + && code <= SPARC_BUILTIN_LAST_DICTUNPACK + && arg_count == 2)) + { + if (!check_constant_argument (icode, idx, op[arg_count])) + return const0_rtx; + } + if (code == SPARC_BUILTIN_LDFSR || code == SPARC_BUILTIN_STFSR) { if (!address_operand (op[arg_count], SImode)) diff --git a/gcc/config/sparc/sparc.h b/gcc/config/sparc/sparc.h index 2bd667f..d7c617e 100644 --- a/gcc/config/sparc/sparc.h +++ b/gcc/config/sparc/sparc.h @@ -1047,6 +1047,10 @@ extern char leaf_reg_remap[]; /* Local macro to handle the two v9 classes of FP regs. */ #define FP_REG_CLASS_P(CLASS) ((CLASS) == FP_REGS || (CLASS) == EXTRA_FP_REGS) +/* Predicate for 2-bit and 5-bit unsigned constants. */ +#define SPARC_IMM2_P(X) (((unsigned HOST_WIDE_INT) (X) & ~0x3) == 0) +#define SPARC_IMM5_P(X) (((unsigned HOST_WIDE_INT) (X) & ~0x1F) == 0) + /* Predicates for 5-bit, 10-bit, 11-bit and 13-bit signed constants. */ #define SPARC_SIMM5_P(X) ((unsigned HOST_WIDE_INT) (X) + 0x10 < 0x20) #define SPARC_SIMM10_P(X) ((unsigned HOST_WIDE_INT) (X) + 0x200 < 0x400) diff --git a/gcc/config/sparc/sparc.md b/gcc/config/sparc/sparc.md index b666992..407544b 100644 --- a/gcc/config/sparc/sparc.md +++ b/gcc/config/sparc/sparc.md @@ -94,6 +94,12 @@ UNSPEC_ADDV UNSPEC_SUBV UNSPEC_NEGV + + UNSPEC_DICTUNPACK + UNSPEC_FPCMPSHL + UNSPEC_FPUCMPSHL + UNSPEC_FPCMPDESHL + UNSPEC_FPCMPURSHL ]) (define_c_enum "unspecv" [ @@ -252,7 +258,7 @@ (symbol_ref "TARGET_SPARCLET") (const_string "sparclet")] (const_string "v7")))) -(define_attr "cpu_feature" "none,fpu,fpunotv9,v9,vis,vis3,vis4" +(define_attr "cpu_feature" "none,fpu,fpunotv9,v9,vis,vis3,vis4,vis4b" (const_string "none")) (define_attr "lra" "disabled,enabled" @@ -266,7 +272,8 @@ (eq_attr "cpu_feature" "v9") (symbol_ref "TARGET_V9") (eq_attr "cpu_feature" "vis") (symbol_ref "TARGET_VIS") (eq_attr "cpu_feature" "vis3") (symbol_ref "TARGET_VIS3") - (eq_attr "cpu_feature" "vis4") (symbol_ref "TARGET_VIS4")] + (eq_attr "cpu_feature" "vis4") (symbol_ref "TARGET_VIS4") + (eq_attr "cpu_feature" "vis4b") (symbol_ref "TARGET_VIS4B")] (const_int 0))) ;; The SPARC instructions used by the backend are organized into a @@ -329,7 +336,7 @@ ;; fga/maxmin: FP{MAX,MIN}[U]{8,16,32} ;; fga/cmask: CMASK{8,16,32} ;; fga/other: BSHUFFLE FALIGNDATAg FP{ADD,SUB}[S]{8,16,32} -;; FP{ADD,SUB}US{8,16} +;; FP{ADD,SUB}US{8,16} DICTUNPACK ;; gsr/reg: RDGSR WRGSR ;; gsr/alignaddr: ALIGNADDRESS[_LITTLE] ;; vismv/double: FSRC2d @@ -343,6 +350,8 @@ ;; visl/double: FONEd FZEROd FNOT1d F{OR,AND,XOR}d F{NOR,NAND,XNOR}d ;; F{OR,AND}NOT1d F{OR,AND}NOT2d ;; viscmp: FPCMP{LE,GT,NE,EQ}{8,16,32} FPCMPU{LE,GT,NE,EQ}{8,16,32} +;; FPCMP{LE,GT,EQ,NE}{8,16,32}SHL FPCMPU{LE,GT,EQ,NE}{8,16,32}SHL +;; FPCMPDE{8,16,32}SHL FPCMPUR{8,16,32}SHL ;; fgm_pack: FPACKFIX FPACK{8,16,32} ;; fgm_mul: FMUL8SUx16 FMUL8ULx16 FMUL8x16 FMUL8x16AL ;; FMUL8x16AU FMULD8SUx16 FMULD8ULx16 @@ -9656,4 +9665,62 @@ visl") [(set_attr "type" "fp") (set_attr "fptype" "double")]) +;; VIS4B instructions. + +(define_mode_iterator DUMODE [V2SI V4HI V8QI]) + +(define_insn "dictunpack<DUMODE:vbits>" + [(set (match_operand:DUMODE 0 "register_operand" "=e") + (unspec:DUMODE [(match_operand:DF 1 "register_operand" "e") + (match_operand:SI 2 "imm5_operand_dictunpack<DUMODE:vbits>" "t")] + UNSPEC_DICTUNPACK))] + "TARGET_VIS4B" + "dictunpack\t%1, %2, %0" + [(set_attr "type" "fga") + (set_attr "subtype" "other")]) + +(define_mode_iterator FPCSMODE [V2SI V4HI V8QI]) +(define_code_iterator fpcscond [le gt eq ne]) +(define_code_iterator fpcsucond [le gt]) + +(define_insn "fpcmp<fpcscond:code><FPCSMODE:vbits><P:mode>shl" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(fpcscond:FPCSMODE (match_operand:FPCSMODE 1 "register_operand" "e") + (match_operand:FPCSMODE 2 "register_operand" "e")) + (match_operand:SI 3 "imm2_operand" "q")] + UNSPEC_FPCMPSHL))] + "TARGET_VIS4B" + "fpcmp<fpcscond:code><FPCSMODE:vbits>shl\t%1, %2, %3, %0" + [(set_attr "type" "viscmp")]) + +(define_insn "fpcmpu<fpcsucond:code><FPCSMODE:vbits><P:mode>shl" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(fpcsucond:FPCSMODE (match_operand:FPCSMODE 1 "register_operand" "e") + (match_operand:FPCSMODE 2 "register_operand" "e")) + (match_operand:SI 3 "imm2_operand" "q")] + UNSPEC_FPUCMPSHL))] + "TARGET_VIS4B" + "fpcmpu<fpcsucond:code><FPCSMODE:vbits>shl\t%1, %2, %3, %0" + [(set_attr "type" "viscmp")]) + +(define_insn "fpcmpde<FPCSMODE:vbits><P:mode>shl" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(match_operand:FPCSMODE 1 "register_operand" "e") + (match_operand:FPCSMODE 2 "register_operand" "e") + (match_operand:SI 3 "imm2_operand" "q")] + UNSPEC_FPCMPDESHL))] + "TARGET_VIS4B" + "fpcmpde<FPCSMODE:vbits>shl\t%1, %2, %3, %0" + [(set_attr "type" "viscmp")]) + +(define_insn "fpcmpur<FPCSMODE:vbits><P:mode>shl" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(match_operand:FPCSMODE 1 "register_operand" "e") + (match_operand:FPCSMODE 2 "register_operand" "e") + (match_operand:SI 3 "imm2_operand" "q")] + UNSPEC_FPCMPURSHL))] + "TARGET_VIS4B" + "fpcmpur<FPCSMODE:vbits>shl\t%1, %2, %3, %0" + [(set_attr "type" "viscmp")]) + (include "sync.md") diff --git a/gcc/config/sparc/sparc.opt b/gcc/config/sparc/sparc.opt index 4f30cc4..cc51bd4 100644 --- a/gcc/config/sparc/sparc.opt +++ b/gcc/config/sparc/sparc.opt @@ -81,6 +81,10 @@ mvis4 Target Report Mask(VIS4) Use UltraSPARC Visual Instruction Set version 4.0 extensions. +mvis4b +Target Report Mask(VIS4B) +Use additional VIS instructions introduced in OSA2017. + mcbcond Target Report Mask(CBCOND) Use UltraSPARC Compare-and-Branch extensions. diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 03ba8fc..07360aa 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -19226,6 +19226,45 @@ v4hi __builtin_vis_fpminu16 (v4hi, v4hi); v2si __builtin_vis_fpminu32 (v2si, v2si); @end smallexample +When you use the @option{-mvis4b} switch, the VIS version 4.0B +built-in functions also become available: + +@smallexample +v8qi __builtin_vis_dictunpack8 (double, int); +v4hi __builtin_vis_dictunpack16 (double, int); +v2si __builtin_vis_dictunpack32 (double, int); + +long __builtin_vis_fpcmple8shl (v8qi, v8qi, int); +long __builtin_vis_fpcmpgt8shl (v8qi, v8qi, int); +long __builtin_vis_fpcmpeq8shl (v8qi, v8qi, int); +long __builtin_vis_fpcmpne8shl (v8qi, v8qi, int); + +long __builtin_vis_fpcmple16shl (v4hi, v4hi, int); +long __builtin_vis_fpcmpgt16shl (v4hi, v4hi, int); +long __builtin_vis_fpcmpeq16shl (v4hi, v4hi, int); +long __builtin_vis_fpcmpne16shl (v4hi, v4hi, int); + +long __builtin_vis_fpcmple32shl (v2si, v2si, int); +long __builtin_vis_fpcmpgt32shl (v2si, v2si, int); +long __builtin_vis_fpcmpeq32shl (v2si, v2si, int); +long __builtin_vis_fpcmpne32shl (v2si, v2si, int); + +long __builtin_vis_fpcmpule8shl (v8qi, v8qi, int); +long __builtin_vis_fpcmpugt8shl (v8qi, v8qi, int); +long __builtin_vis_fpcmpule16shl (v4hi, v4hi, int); +long __builtin_vis_fpcmpugt16shl (v4hi, v4hi, int); +long __builtin_vis_fpcmpule32shl (v2si, v2si, int); +long __builtin_vis_fpcmpugt32shl (v2si, v2si, int); + +long __builtin_vis_fpcmpde8shl (v8qi, v8qi, int); +long __builtin_vis_fpcmpde16shl (v4hi, v4hi, int); +long __builtin_vis_fpcmpde32shl (v2si, v2si, int); + +long __builtin_vis_fpcmpur8shl (v8qi, v8qi, int); +long __builtin_vis_fpcmpur16shl (v4hi, v4hi, int); +long __builtin_vis_fpcmpur32shl (v2si, v2si, int); +@end smallexample + @node SPU Built-in Functions @subsection SPU Built-in Functions diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index d1ce94c..1fd85bc 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -1125,6 +1125,7 @@ See RS/6000 and PowerPC Options. -muser-mode -mno-user-mode @gol -mv8plus -mno-v8plus -mvis -mno-vis @gol -mvis2 -mno-vis2 -mvis3 -mno-vis3 @gol +-mvis4 -mno-vis4 -mvis4b -mno-vis4b @gol -mcbcond -mno-cbcond -mfmaf -mno-fmaf @gol -mpopc -mno-popc -msubxc -mno-subxc@gol -mfix-at697f -mfix-ut699 @gol @@ -24017,6 +24018,18 @@ default is @option{-mvis4} when targeting a cpu that supports such instructions, such as niagara-7 and later. Setting @option{-mvis4} also sets @option{-mvis3}, @option{-mvis2} and @option{-mvis}. +@item -mvis4b +@itemx -mno-vis4b +@opindex mvis4b +@opindex mno-vis4b +With @option{-mvis4b}, GCC generates code that takes advantage of +version 4.0 of the UltraSPARC Visual Instruction Set extensions, plus +the additional VIS instructions introduced in the Oracle SPARC +Architecture 2017. The default is @option{-mvis4b} when targeting a +cpu that supports such instructions, such as m8 and later. Setting +@option{-mvis4b} also sets @option{-mvis4}, @option{-mvis3}, +@option{-mvis2} and @option{-mvis}. + @item -mcbcond @itemx -mno-cbcond @opindex mcbcond diff --git a/gcc/testsuite/gcc.target/sparc/dictunpack.c b/gcc/testsuite/gcc.target/sparc/dictunpack.c new file mode 100644 index 0000000..4334dee --- /dev/null +++ b/gcc/testsuite/gcc.target/sparc/dictunpack.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-mvis4b" } */ + +typedef unsigned char vec8 __attribute__((vector_size(8))); +typedef short vec16 __attribute__((vector_size(8))); +typedef int vec32 __attribute__((vector_size(8))); + +vec8 test_dictunpack8 (double a) +{ + return __builtin_vis_dictunpack8 (a, 6); +} + +vec16 test_dictunpack16 (double a) +{ + return __builtin_vis_dictunpack16 (a, 14); +} + +vec32 test_dictunpack32 (double a) +{ + return __builtin_vis_dictunpack32 (a, 30); +} + +/* { dg-final { scan-assembler "dictunpack\t%" } } */ +/* { dg-final { scan-assembler "dictunpack\t%" } } */ +/* { dg-final { scan-assembler "dictunpack\t%" } } */ diff --git a/gcc/testsuite/gcc.target/sparc/fpcmpdeshl.c b/gcc/testsuite/gcc.target/sparc/fpcmpdeshl.c new file mode 100644 index 0000000..3e3daa6 --- /dev/null +++ b/gcc/testsuite/gcc.target/sparc/fpcmpdeshl.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-mvis4b" } */ + +typedef unsigned char vec8 __attribute__((vector_size(8))); +typedef short vec16 __attribute__((vector_size(8))); +typedef int vec32 __attribute__((vector_size(8))); + +long test_fpcmpde8shl (vec8 a, vec8 b) +{ + return __builtin_vis_fpcmpde8shl (a, b, 2); +} + +long test_fpcmpde16shl (vec16 a, vec16 b) +{ + return __builtin_vis_fpcmpde16shl (a, b, 2); +} + +long test_fpcmpde32shl (vec32 a, vec32 b) +{ + return __builtin_vis_fpcmpde32shl (a, b, 2); +} + +/* { dg-final { scan-assembler "fpcmpde8shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpde16shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpde32shl\t%" } } */ diff --git a/gcc/testsuite/gcc.target/sparc/fpcmpshl.c b/gcc/testsuite/gcc.target/sparc/fpcmpshl.c new file mode 100644 index 0000000..0985251 --- /dev/null +++ b/gcc/testsuite/gcc.target/sparc/fpcmpshl.c @@ -0,0 +1,81 @@ +/* { dg-do compile } */ +/* { dg-options "-mvis4b" } */ + +typedef unsigned char vec8 __attribute__((vector_size(8))); +typedef short vec16 __attribute__((vector_size(8))); +typedef int vec32 __attribute__((vector_size(8))); + +long test_fpcmple8shl (vec8 a, vec8 b) +{ + return __builtin_vis_fpcmple8shl (a, b, 2); +} + +long test_fpcmpgt8shl (vec8 a, vec8 b) +{ + return __builtin_vis_fpcmpgt8shl (a, b, 2); +} + +long test_fpcmpeq8shl (vec8 a, vec8 b) +{ + return __builtin_vis_fpcmpeq8shl (a, b, 2); +} + +long test_fpcmpne8shl (vec8 a, vec8 b) +{ + return __builtin_vis_fpcmpne8shl (a, b, 2); +} + +long test_fpcmple16shl (vec16 a, vec16 b) +{ + return __builtin_vis_fpcmple16shl (a, b, 2); +} + +long test_fpcmpgt16shl (vec16 a, vec16 b) +{ + return __builtin_vis_fpcmpgt16shl (a, b, 2); +} + +long test_fpcmpeq16shl (vec16 a, vec16 b) +{ + return __builtin_vis_fpcmpeq16shl (a, b, 2); +} + +long test_fpcmpne16shl (vec16 a, vec16 b) +{ + return __builtin_vis_fpcmpne16shl (a, b, 2); +} + +long test_fpcmple32shl (vec32 a, vec32 b) +{ + return __builtin_vis_fpcmple32shl (a, b, 2); +} + +long test_fpcmpgt32shl (vec32 a, vec32 b) +{ + return __builtin_vis_fpcmpgt32shl (a, b, 2); +} + +long test_fpcmpeq32shl (vec32 a, vec32 b) +{ + return __builtin_vis_fpcmpeq32shl (a, b, 2); +} + +long test_fpcmpne32shl (vec32 a, vec32 b) +{ + return __builtin_vis_fpcmpne32shl (a, b, 2); +} + +/* { dg-final { scan-assembler "fpcmple8shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpgt8shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpeq8shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpne8shl\t%" } } */ + +/* { dg-final { scan-assembler "fpcmple16shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpgt16shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpeq16shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpne16shl\t%" } } */ + +/* { dg-final { scan-assembler "fpcmple32shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpgt32shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpeq32shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpne32shl\t%" } } */ diff --git a/gcc/testsuite/gcc.target/sparc/fpcmpurshl.c b/gcc/testsuite/gcc.target/sparc/fpcmpurshl.c new file mode 100644 index 0000000..db74e01 --- /dev/null +++ b/gcc/testsuite/gcc.target/sparc/fpcmpurshl.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-mvis4b" } */ + +typedef unsigned char vec8 __attribute__((vector_size(8))); +typedef short vec16 __attribute__((vector_size(8))); +typedef int vec32 __attribute__((vector_size(8))); + +long test_fpcmpur8shl (vec8 a, vec8 b) +{ + return __builtin_vis_fpcmpur8shl (a, b, 2); +} + +long test_fpcmpur16shl (vec16 a, vec16 b) +{ + return __builtin_vis_fpcmpur16shl (a, b, 2); +} + +long test_fpcmpur32shl (vec32 a, vec32 b) +{ + return __builtin_vis_fpcmpur32shl (a, b, 2); +} + +/* { dg-final { scan-assembler "fpcmpur8shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpur16shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpur32shl\t%" } } */ diff --git a/gcc/testsuite/gcc.target/sparc/fpcmpushl.c b/gcc/testsuite/gcc.target/sparc/fpcmpushl.c new file mode 100644 index 0000000..fc58dedd --- /dev/null +++ b/gcc/testsuite/gcc.target/sparc/fpcmpushl.c @@ -0,0 +1,43 @@ +/* { dg-do compile } */ +/* { dg-options "-mvis4b" } */ + +typedef unsigned char vec8 __attribute__((vector_size(8))); +typedef short vec16 __attribute__((vector_size(8))); +typedef int vec32 __attribute__((vector_size(8))); + +long test_fpcmpule8shl (vec8 a, vec8 b) +{ + return __builtin_vis_fpcmpule8shl (a, b, 2); +} + +long test_fpcmpugt8shl (vec8 a, vec8 b) +{ + return __builtin_vis_fpcmpugt8shl (a, b, 2); +} + +long test_fpcmpule16shl (vec16 a, vec16 b) +{ + return __builtin_vis_fpcmpule16shl (a, b, 2); +} + +long test_fpcmpugt16shl (vec16 a, vec16 b) +{ + return __builtin_vis_fpcmpugt16shl (a, b, 2); +} + +long test_fpcmpule32shl (vec32 a, vec32 b) +{ + return __builtin_vis_fpcmpule32shl (a, b, 2); +} + +long test_fpcmpugt32shl (vec32 a, vec32 b) +{ + return __builtin_vis_fpcmpugt32shl (a, b, 2); +} + +/* { dg-final { scan-assembler "fpcmpule8shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpugt8shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpule16shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpugt16shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpule32shl\t%" } } */ +/* { dg-final { scan-assembler "fpcmpugt32shl\t%" } } */ -- 2.3.4