diff options
Diffstat (limited to 'gcc/config')
-rw-r--r-- | gcc/config/arm/arm.md | 97 |
1 files changed, 62 insertions, 35 deletions
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index a26550a..8e52003 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -4710,47 +4710,74 @@ ;; Negate an extended 32-bit value. (define_insn_and_split "*negdi_extendsidi" - [(set (match_operand:DI 0 "s_register_operand" "=r,&r,l,&l") - (neg:DI (sign_extend:DI (match_operand:SI 1 "s_register_operand" "0,r,0,l")))) + [(set (match_operand:DI 0 "s_register_operand" "=l,r") + (neg:DI (sign_extend:DI + (match_operand:SI 1 "s_register_operand" "l,r")))) (clobber (reg:CC CC_REGNUM))] "TARGET_32BIT" - "#" ; rsb\\t%Q0, %1, #0\;asr\\t%R0, %Q0, #31 + "#" "&& reload_completed" [(const_int 0)] { - operands[2] = gen_highpart (SImode, operands[0]); - operands[0] = gen_lowpart (SImode, operands[0]); - rtx tmp = gen_rtx_SET (VOIDmode, - operands[0], - gen_rtx_MINUS (SImode, - const0_rtx, - operands[1])); - if (TARGET_ARM) - { - emit_insn (tmp); - } - else - { - /* Set the flags, to emit the short encoding in Thumb2. */ - rtx flags = gen_rtx_SET (VOIDmode, - gen_rtx_REG (CCmode, CC_REGNUM), - gen_rtx_COMPARE (CCmode, - const0_rtx, - operands[1])); - emit_insn (gen_rtx_PARALLEL (VOIDmode, - gen_rtvec (2, - flags, - tmp))); - } - emit_insn (gen_rtx_SET (VOIDmode, - operands[2], - gen_rtx_ASHIFTRT (SImode, - operands[0], - GEN_INT (31)))); - DONE; + rtx low = gen_lowpart (SImode, operands[0]); + rtx high = gen_highpart (SImode, operands[0]); + + if (reg_overlap_mentioned_p (low, operands[1])) + { + /* Input overlaps the low word of the output. Use: + asr Rhi, Rin, #31 + rsbs Rlo, Rin, #0 + rsc Rhi, Rhi, #0 (thumb2: sbc Rhi, Rhi, Rhi, lsl #1). */ + rtx cc_reg = gen_rtx_REG (CC_Cmode, CC_REGNUM); + + emit_insn (gen_rtx_SET (VOIDmode, high, + gen_rtx_ASHIFTRT (SImode, operands[1], + GEN_INT (31)))); + + emit_insn (gen_subsi3_compare (low, const0_rtx, operands[1])); + if (TARGET_ARM) + emit_insn (gen_rtx_SET (VOIDmode, high, + gen_rtx_MINUS (SImode, + gen_rtx_MINUS (SImode, + const0_rtx, + high), + gen_rtx_LTU (SImode, + cc_reg, + const0_rtx)))); + else + { + rtx two_x = gen_rtx_ASHIFT (SImode, high, GEN_INT (1)); + emit_insn (gen_rtx_SET (VOIDmode, high, + gen_rtx_MINUS (SImode, + gen_rtx_MINUS (SImode, + high, + two_x), + gen_rtx_LTU (SImode, + cc_reg, + const0_rtx)))); + } + } + else + { + /* No overlap, or overlap on high word. Use: + rsb Rlo, Rin, #0 + bic Rhi, Rlo, Rin + asr Rhi, Rhi, #31 + Flags not needed for this sequence. */ + emit_insn (gen_rtx_SET (VOIDmode, low, + gen_rtx_NEG (SImode, operands[1]))); + emit_insn (gen_rtx_SET (VOIDmode, high, + gen_rtx_AND (SImode, + gen_rtx_NOT (SImode, operands[1]), + low))); + emit_insn (gen_rtx_SET (VOIDmode, high, + gen_rtx_ASHIFTRT (SImode, high, + GEN_INT (31)))); + } + DONE; } - [(set_attr "length" "8,8,4,4") - (set_attr "arch" "a,a,t2,t2") + [(set_attr "length" "12") + (set_attr "arch" "t2,*") (set_attr "type" "multiple")] ) |