;; DImode/DFmode patterns description of Andes NDS32 cpu for GNU compiler
;; Copyright (C) 2012-2017 Free Software Foundation, Inc.
;; Contributed by Andes Technology Corporation.
;;
;; This file is part of GCC.
;;
;; GCC is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published
;; by the Free Software Foundation; either version 3, or (at your
;; option) any later version.
;;
;; GCC is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
;; License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; .
;; -------------------------------------------------------------
;; Move DImode/DFmode instructions.
;; -------------------------------------------------------------
(define_expand "movdi"
[(set (match_operand:DI 0 "general_operand" "")
(match_operand:DI 1 "general_operand" ""))]
""
{
/* Need to force register if mem <- !reg. */
if (MEM_P (operands[0]) && !REG_P (operands[1]))
operands[1] = force_reg (DImode, operands[1]);
})
(define_expand "movdf"
[(set (match_operand:DF 0 "general_operand" "")
(match_operand:DF 1 "general_operand" ""))]
""
{
/* Need to force register if mem <- !reg. */
if (MEM_P (operands[0]) && !REG_P (operands[1]))
operands[1] = force_reg (DFmode, operands[1]);
})
(define_insn "move_"
[(set (match_operand:DIDF 0 "nonimmediate_operand" "=r, r, r, m")
(match_operand:DIDF 1 "general_operand" " r, i, m, r"))]
""
{
rtx addr;
rtx otherops[5];
switch (which_alternative)
{
case 0:
return "movd44\t%0, %1";
case 1:
/* reg <- const_int, we ask gcc to split instruction. */
return "#";
case 2:
/* Refer to nds32_legitimate_address_p() in nds32.c,
we only allow "reg", "symbol_ref", "const", and "reg + const_int"
as address rtx for DImode/DFmode memory access. */
addr = XEXP (operands[1], 0);
otherops[0] = gen_rtx_REG (SImode, REGNO (operands[0]));
otherops[1] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
otherops[2] = addr;
if (REG_P (addr))
{
/* (reg) <- (mem (reg)) */
output_asm_insn ("lmw.bi\t%0, [%2], %1, 0", otherops);
}
else if (GET_CODE (addr) == PLUS)
{
/* (reg) <- (mem (plus (reg) (const_int))) */
rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
if (REG_P (op0))
{
otherops[2] = op0;
otherops[3] = op1;
otherops[4] = gen_int_mode (INTVAL (op1) + 4, SImode);
}
else
{
otherops[2] = op1;
otherops[3] = op0;
otherops[4] = gen_int_mode (INTVAL (op0) + 4, SImode);
}
/* To avoid base overwrite when REGNO(%0) == REGNO(%2). */
if (REGNO (otherops[0]) != REGNO (otherops[2]))
{
output_asm_insn ("lwi\t%0, [%2 + (%3)]", otherops);
output_asm_insn ("lwi\t%1, [%2 + (%4)]", otherops);
}
else
{
output_asm_insn ("lwi\t%1, [%2 + (%4)]", otherops);
output_asm_insn ("lwi\t%0,[ %2 + (%3)]", otherops);
}
}
else
{
/* (reg) <- (mem (symbol_ref ...))
(reg) <- (mem (const ...)) */
output_asm_insn ("lwi.gp\t%0, [ + %2]", otherops);
output_asm_insn ("lwi.gp\t%1, [ + %2 + 4]", otherops);
}
/* We have already used output_asm_insn() by ourself,
so return an empty string. */
return "";
case 3:
/* Refer to nds32_legitimate_address_p() in nds32.c,
we only allow "reg", "symbol_ref", "const", and "reg + const_int"
as address rtx for DImode/DFmode memory access. */
addr = XEXP (operands[0], 0);
otherops[0] = gen_rtx_REG (SImode, REGNO (operands[1]));
otherops[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
otherops[2] = addr;
if (REG_P (addr))
{
/* (mem (reg)) <- (reg) */
output_asm_insn ("smw.bi\t%0, [%2], %1, 0", otherops);
}
else if (GET_CODE (addr) == PLUS)
{
/* (mem (plus (reg) (const_int))) <- (reg) */
rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
if (REG_P (op0))
{
otherops[2] = op0;
otherops[3] = op1;
otherops[4] = gen_int_mode (INTVAL (op1) + 4, SImode);
}
else
{
otherops[2] = op1;
otherops[3] = op0;
otherops[4] = gen_int_mode (INTVAL (op0) + 4, SImode);
}
/* To avoid base overwrite when REGNO(%0) == REGNO(%2). */
if (REGNO (otherops[0]) != REGNO (otherops[2]))
{
output_asm_insn ("swi\t%0, [%2 + (%3)]", otherops);
output_asm_insn ("swi\t%1, [%2 + (%4)]", otherops);
}
else
{
output_asm_insn ("swi\t%1, [%2 + (%4)]", otherops);
output_asm_insn ("swi\t%0, [%2 + (%3)]", otherops);
}
}
else
{
/* (mem (symbol_ref ...)) <- (reg)
(mem (const ...)) <- (reg) */
output_asm_insn ("swi.gp\t%0, [ + %2]", otherops);
output_asm_insn ("swi.gp\t%1, [ + %2 + 4]", otherops);
}
/* We have already used output_asm_insn() by ourself,
so return an empty string. */
return "";
default:
gcc_unreachable ();
}
}
[(set_attr "type" "move,move,move,move")
(set_attr "length" " 4, 16, 8, 8")])
(define_split
[(set (match_operand:DIDF 0 "register_operand" "")
(match_operand:DIDF 1 "const_double_operand" ""))]
"reload_completed"
[(set (match_dup 2) (match_dup 3))
(set (match_dup 4) (match_dup 5))]
{
/* Construct lowpart rtx. */
operands[2] = gen_lowpart (SImode, operands[0]);
operands[3] = gen_lowpart (SImode, operands[1]);
/* Construct highpart rtx. */
/* Note that operands[1] can be VOIDmode constant,
so we need to use gen_highpart_mode().
Refer to gcc/emit-rtl.c for more information. */
operands[4] = gen_highpart (SImode, operands[0]);
operands[5] = gen_highpart_mode (SImode,
GET_MODE (operands[0]), operands[1]);
/* Actually we would like to create move behavior by ourself.
So that movsi expander could have chance to split large constant. */
emit_move_insn (operands[2], operands[3]);
emit_move_insn (operands[4], operands[5]);
DONE;
})
;; There is 'movd44' instruction for DImode/DFmode movement under V3/V3M ISA.
;; We only need to split it under V2 ISA or none-16-bit code generation.
(define_split
[(set (match_operand:DIDF 0 "register_operand" "")
(match_operand:DIDF 1 "register_operand" ""))]
"reload_completed
&& (TARGET_ISA_V2 || !TARGET_16_BIT)"
[(set (match_dup 0) (match_dup 1))
(set (match_dup 2) (match_dup 3))]
{
operands[2] = gen_highpart (SImode, operands[0]);
operands[3] = gen_highpart (SImode, operands[1]);
operands[0] = gen_lowpart (SImode, operands[0]);
operands[1] = gen_lowpart (SImode, operands[1]);
/* Handle a partial overlap. */
if (rtx_equal_p (operands[0], operands[3]))
{
rtx tmp0 = operands[0];
rtx tmp1 = operands[1];
operands[0] = operands[2];
operands[1] = operands[3];
operands[2] = tmp0;
operands[3] = tmp1;
}
})
;; -------------------------------------------------------------
;; Boolean DImode instructions.
;; -------------------------------------------------------------
;; Nowadays, the generic code is supposed to split the DImode
;; boolean operations and have good code generation.
;; Unless we find out some bad cases, there is no need to
;; define DImode boolean operations by ourself.
;; -------------------------------------------------------------