home *** CD-ROM | disk | FTP | other *** search
- /* Subroutines for insn-output.c for Hitachi H8/300.
- Copyright (C) 1992,1993 Free Software Foundation, Inc.
-
- Contributed by Steve Chamberlain (sac@cygnus.com) and
- Jim Wilson (wilson@cygnus.com).
-
- This file is part of GNU CC.
-
- GNU CC 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 2, or (at your option)
- any later version.
-
- GNU CC 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 GNU CC; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include <stdio.h>
- #include "config.h"
- #include "rtl.h"
- #include "regs.h"
- #include "hard-reg-set.h"
- #include "real.h"
- #include "insn-config.h"
- #include "conditions.h"
- #include "insn-flags.h"
- #include "output.h"
- #include "insn-attr.h"
- #include "flags.h"
- #include "recog.h"
- #include "expr.h"
- #include "tree.h"
-
- /* Forward declarations. */
- void print_operand_address ();
- char *index ();
-
- /* True if a #pragma interrupt has been seen for the current function. */
- int pragma_interrupt;
-
- /* True if a #pragma saveall has been seen for the current function. */
- int pragma_saveall;
-
- char *names_big[]
- = {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"};
-
- char *
- byte_reg (x, b)
- rtx x;
- int b;
- {
- static char *names_small[]
- = {"r0l", "r0h", "r1l", "r1h", "r2l", "r2h", "r3l", "r3h",
- "r4l", "r4h", "r5l", "r5h", "r6l", "r6h", "r7lBAD", "r7hBAD"};
-
- return names_small[REGNO (x) * 2 + b];
- }
-
- /* REGNO must be saved/restored across calls if this macro is true. */
- static int
- word_reg_used (regno)
- int regno;
- {
- if (regno < 7
- && (pragma_interrupt || pragma_saveall
- || (regno == FRAME_POINTER_REGNUM && regs_ever_live[regno])
- || (regs_ever_live[regno] & ! call_used_regs[regno])))
- return 1;
- return 0;
- }
-
- /* Output assembly language to FILE for the operation OP with operand size
- SIZE. */
- static void
- dosize (file, op, size, fped)
- FILE *file;
- char *op;
- unsigned int size;
- int fped;
- {
- switch (size)
- {
- case 4:
- case 3:
- fprintf (file, "\t%ss\t#%d,sp\n", op, 2);
- size -= 2;
- /* Fall through... */
- case 2:
- case 1:
- fprintf (file, "\t%ss\t#%d,sp\n", op, size);
- size = 0;
- break;
- case 0:
- break;
- default:
- fprintf (file, "\tmov.w\t#%d,r5\n\t%s.w\tr5,sp\n", size, op);
- size = 0;
- break;
- }
- }
-
- /* Output assembly language code for the function prologue. */
- static int push_order[FIRST_PSEUDO_REGISTER]
- = {6, 5, 4, 3, 2, 1, 0, -1, -1};
- static int pop_order[FIRST_PSEUDO_REGISTER]
- = {0, 1, 2, 3, 4, 5, 6, -1, -1};
-
- /* This is what the stack looks like after the prolog of
- a function with a frame has been set up:
-
- <pushed args>
- return pc
- fp-> old fp
- <locals>
- <saved register-0>
- <saved register-1>
- sp-> <saved register-n>
-
-
- This is what the stack looks like after the prolog of
- a function which doesn't have a frame:
-
- <pushed args>
- return pc
- <locals>
- <saved register-0>
- sp-> <saved register-n>
- */
-
- void
- function_prologue (file, size)
- FILE *file;
- int size;
- {
- register int mask = 0;
- int fsize = (size + 1) & -2;
- int idx;
-
- if (frame_pointer_needed)
- {
- /* Push the fp. */
- fprintf (file, "\tpush\t%s\n", names_big[FRAME_POINTER_REGNUM]);
- fprintf (file, "\tmov.w\tr7,r6\n");
-
- /* Leave room for the locals. */
- dosize (file, "sub", fsize, 1);
-
- /* Push the rest of the registers. */
- for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
- {
- int regno = push_order[idx];
-
- if (regno >= 0 && word_reg_used (regno)
- && regno != FRAME_POINTER_REGNUM)
- fprintf (file, "\tpush\t%s\n", names_big[regno]);
- }
- }
- else
- {
- dosize (file, "sub", fsize, 0);
- for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
- {
- int regno = push_order[idx];
-
- if (regno >= 0 && word_reg_used (regno))
- fprintf (file, "\tpush\t%s\n", names_big[regno]);
- }
- }
- }
-
- /* Output assembly language code for the function epilogue. */
-
- void
- function_epilogue (file, size)
- FILE *file;
- int size;
- {
- register int regno;
- register int mask = 0;
- int fsize = (size + 1) & -2;
- int nregs;
- int offset;
- int idx;
- rtx insn = get_last_insn ();
-
- /* If the last insn was a BARRIER, we don't have to write any code. */
- if (GET_CODE (insn) == NOTE)
- insn = prev_nonnote_insn (insn);
- if (insn && GET_CODE (insn) == BARRIER)
- return;
-
- nregs = 0;
-
- if (frame_pointer_needed)
- {
- /* Pop saved registers. */
- for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
- {
- regno = pop_order[idx];
- if (regno >= 0 && regno != FRAME_POINTER_REGNUM
- && word_reg_used (regno))
- fprintf (file, "\tpop\t%s\n", names_big[regno]);
- }
- /* Deallocate locals. */
- dosize (file, "add", fsize, 1);
- /* Pop frame pointer. */
- fprintf (file, "\tpop\t%s\n", names_big[FRAME_POINTER_REGNUM]);
- }
- else
- {
- /* Deallocate locals and pop saved registers. */
- for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
- {
- regno = pop_order[idx];
- if (regno >= 0 && word_reg_used (regno))
- fprintf (file, "\tpop\t%s\n", names_big[regno]);
- }
- dosize (file, "add", fsize, 0);
- }
- if (pragma_interrupt)
- fprintf (file, "\trte\n");
- else
- fprintf (file, "\trts\n");
-
- pragma_interrupt = 0;
- pragma_saveall = 0;
- }
-
- /* Return true if VALUE is a valid constant for constraint 'P'. */
-
- int
- potl8 (value)
- {
- switch (value)
- {
- case 1:
- case 2:
- case 4:
- case 8:
- case 16:
- case 32:
- case 64:
- case 128:
- return 1;
- }
- return 0;
- }
-
- /* Return true if VALUE is a valid constant for constraint 'O'. */
- int
- potg8 (value)
- int value;
- {
- switch (value)
- {
- case 256:
- case 512:
- case 1024:
- case 2048:
- case 4096:
- case 8192:
- case 16384:
- case 32768:
- return 1;
- }
- return 0;
- }
-
- /* Return true is OP is a valid source operand for an integer move
- instruction. */
- int
- general_operand_src (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- /* We can't have a pre-dec as a source. */
- if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == PRE_DEC)
- return 0;
- return general_operand (op, mode);
- }
-
- /* Return true if OP is a valid destination operand for an integer move
- instruction. */
- int
- general_operand_dst (op, mode)
- rtx op;
- enum machine_mode mode;
- {
- /* We can't have a post-inc as a dest. */
- if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == POST_INC)
- return 0;
- return general_operand (op, mode);
- }
-
- /* Handle machine specific pragmas for compatibility with existing
- compilers for the H8/300
-
- pragma saveall generates prolog/epilog code which saves and
- restores all the registers on function entry.
-
- pragma interrupt saves and restores all registers, and exits with
- an rte instruction rather than an rts. A pointer to a function
- with this attribute may be safely used in an interrupt vector. */
- int
- handle_pragma (file)
- FILE *file;
- {
- int c;
- char pbuf[20];
- int psize;
-
- c = getc (file);
- while (c == ' ' || c == '\t')
- c = getc (file);
-
- if (c == '\n' || c == EOF)
- return c;
-
- for (psize = 0; psize < sizeof (pbuf) - 1 && isalpha (c); psize++)
- {
- pbuf[psize] = c;
- c = getc (file);
- }
-
- pbuf[psize] = 0;
-
- if (strcmp (pbuf, "interrupt") == 0)
- pragma_interrupt = 1;
-
- if (strcmp (pbuf, "saveall") == 0)
- pragma_saveall = 1;
-
- return c;
- }
-
- /* If the next arg with MODE and TYPE is to be passed in a register, return
- the rtx to represent where it is passed. CUM represents the state after
- the last argument. NAMED is not used. */
-
- rtx
- function_arg (cum, mode, type, named)
- CUMULATIVE_ARGS *cum;
- enum machine_mode mode;
- tree type;
- int named;
- {
- rtx result = 0;
- int libcall = 0;
-
- /* Right now reload has a problem with reg passing with small reg
- classes. */
-
- if (cum->libcall || (named && TARGET_QUICKCALL))
- libcall = 1;
-
- if (TARGET_NOQUICK)
- libcall = 0;
-
- if (mode != VOIDmode && libcall && mode != DFmode && mode != SFmode)
- {
- switch (cum->nbytes)
- {
- case 0:
- result = gen_rtx (REG, mode, 0);
- break;
- case 2:
- result = gen_rtx (REG, mode, 1);
- break;
- case 4:
- result = gen_rtx (REG, mode, 4);
- break;
- case 6:
- result = gen_rtx (REG, mode, 5);
- break;
- default:
- return 0;
- }
- }
- return result;
- }
-
- /* Documentation for the machine specific operand escapes:
-
- 'C' print (operand - 2).
- 'E' low byte of reg or -ve lsb of constant
- 'F' high byte of reg of -ve msb of constant
-
- 'G' negate constant
- 'L' fake label, changed after used twice.
- 'M' turn a 'M' constant into its negative mod 2.
- 'T' print operand as a word
- 'V' print log2 of constant - used for bset instructions
- 'X' 8 bit register or other operand
-
- 'Y' print either l or h depending on whether last 'Z' operand < 8 or >= 8.
- 'Z' print int & 7
-
- 'e' first word of 32 bit value
- 'f' second word of 32 bit value
-
- 'j' print operand as condition code.
- 'k' print operand as reverse condition code.
-
- 's' low byte of 16 bit value
- 't' high byte of 16 bit value
-
- 'w' 1st byte of 32 bit value zzzzzzzz yyyyyyyy xxxxxxxx wwwwwwww
- 'x' 2nd byte of 32 bit value
- 'y' 3rd byte of 32 bit value
- 'z' 4th byte of 32 bit value
-
- */
-
- /* Return assembly language string which identifies a comparison type. */
-
- char *
- cond_string (code)
- enum rtx_code code;
- {
- switch (code)
- {
- case NE:
- return "ne";
- case EQ:
- return "eq";
- case GE:
- return "ge";
- case GT:
- return "gt";
- case LE:
- return "le";
- case LT:
- return "lt";
- case GEU:
- return "hs";
- case GTU:
- return "hi";
- case LEU:
- return "ls";
- case LTU:
- return "lo";
- default:
- abort ();
- }
- }
-
- /* Print operand X using operand code CODE to assembly language output file
- FILE. */
-
- void
- print_operand (file, x, code)
- FILE *file;
- rtx x;
- int code;
- {
- /* This is used to general unique labels for the 'L' code. */
- static int lab = 1000;
-
- /* This is used for communication between the 'P' and 'U' codes. */
- static char *last_p;
-
- /* This is used for communication between the 'Z' and 'Y' codes. */
- static int bitint;
-
- switch (code)
- {
- case 'L':
- /* 'L' must always be used twice in a single pattern. It generates
- the same lable twice, and then will generate a unique label the
- next time it is used. */
- asm_fprintf (file, "tl%d", (lab++) / 2);
- break;
-
- case 'X':
- if (GET_CODE (x) == REG)
- fprintf (file, "%s", byte_reg (x, 0));
- else
- goto def;
- break;
-
- case 'G':
- if (GET_CODE (x) != CONST_INT)
- abort ();
- fprintf (file, "#%d", 0xff & (-INTVAL (x)));
- break;
-
- case 'T':
- if (GET_CODE (x) == REG)
- fprintf (file, "%s", names_big[REGNO (x)]);
- else
- goto def;
- break;
-
- case 'w':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", INTVAL (x) & 0xff);
- else
- fprintf (file, "%s", byte_reg (x, 2));
- break;
-
- case 'x':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
- else
- fprintf (file, "%s", byte_reg (x, 3));
- break;
-
- case 'y':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x) >> 16) & 0xff);
- else
- fprintf (file, "%s", byte_reg (x, 0));
- break;
-
- case 'z':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x) >> 24) & 0xff);
- else
- fprintf (file, "%s", byte_reg (x, 1));
- break;
-
- /* FOR 16 bits. */
- case 't':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
- else
- fprintf (file, "%s", byte_reg (x, 1));
- break;
-
- case 's':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x)) & 0xff);
- else
- fprintf (file, "%s", byte_reg (x, 0));
- break;
-
- case 'u':
- if (GET_CODE (x) != CONST_INT)
- abort ();
- fprintf (file, "%d", INTVAL (x));
- break;
-
- case 'Z':
- bitint = INTVAL (x);
- fprintf (file, "#%d", bitint & 7);
- break;
-
- case 'Y':
- fprintf (file, "%c", bitint > 7 ? 'h' : 'l');
- break;
-
- case 'O':
- bitint = exact_log2 ((~INTVAL (x)) & 0xff);
- if (bitint == -1)
- abort ();
- fprintf (file, "#%d", bitint & 7);
- break;
-
- case 'V':
- bitint = exact_log2 (INTVAL (x));
- if (bitint == -1)
- abort ();
- fprintf (file, "#%d", bitint & 7);
- break;
-
- case 'P':
- if (REGNO (XEXP (XEXP (x, 0), 0)) == STACK_POINTER_REGNUM)
- {
- last_p = "";
- fprintf (file, ".w");
- }
- else
- {
- last_p = "l";
- fprintf (file, ".b");
- }
- break;
-
- case 'U':
- fprintf (file, "%s%s", names_big[REGNO (x)], last_p);
- break;
-
- case 'M':
- /* For -4 and -2, the other 2 is handled separately. */
- switch (INTVAL (x))
- {
- case -2:
- case -4:
- fprintf (file, "#2");
- break;
- case -1:
- case -3:
- fprintf (file, "#1");
- break;
-
- default:
- abort ();
- }
- break;
-
- case 'e':
- switch (GET_CODE (x))
- {
- case REG:
- fprintf (file, "%s", names_big[REGNO (x)]);
- break;
- case MEM:
- x = adj_offsettable_operand (x, 0);
- print_operand (file, x, 0);
- break;
- case CONST_INT:
- fprintf (file, "#%d", ((INTVAL (x) >> 16) & 0xffff));
- break;
- default:
- abort ();
- break;
- }
- break;
-
- case 'f':
- switch (GET_CODE (x))
- {
- case REG:
- fprintf (file, "%s", names_big[REGNO (x) + 1]);
- break;
-
- case MEM:
- x = adj_offsettable_operand (x, 2);
- print_operand (file, x, 0);
- break;
-
- case CONST_INT:
- fprintf (file, "#%d", INTVAL (x) & 0xffff);
- break;
-
- default:
- abort ();
- }
- break;
-
- case 'C':
- fprintf (file, "#%d", INTVAL (x) - 2);
- break;
-
- case 'E':
- switch (GET_CODE (x))
- {
- case REG:
- fprintf (file, "%sl", names_big[REGNO (x)]);
- break;
-
- case CONST_INT:
- fprintf (file, "#%d", (-INTVAL (x)) & 0xff);
- break;
-
- default:
- abort ();
- }
- break;
-
- case 'F':
- switch (GET_CODE (x))
- {
- case REG:
- fprintf (file, "%sh", names_big[REGNO (x)]);
- break;
-
- case CONST_INT:
- fprintf (file, "#%d", ((-INTVAL (x)) & 0xff00) >> 8);
- break;
-
- default:
- abort ();
- }
- break;
-
- case 'j':
- asm_fprintf (file, cond_string (GET_CODE (x)));
- break;
-
- case 'k':
- asm_fprintf (file, cond_string (reverse_condition (GET_CODE (x))));
- break;
- def: ;
- default:
- switch (GET_CODE (x))
- {
- case REG:
- fprintf (file, "%s", names_big[REGNO (x)]);
- break;
-
- case MEM:
- fprintf (file, "@");
- output_address (XEXP (x, 0));
- break;
-
- case CONST_INT:
- case SYMBOL_REF:
- case CONST:
- case LABEL_REF:
- fprintf (file, "#");
- print_operand_address (file, x);
- break;
- }
- }
- }
-
- /* Output assembly language output for the address ADDR to FILE. */
-
- void
- print_operand_address (file, addr)
- FILE *file;
- rtx addr;
- {
- switch (GET_CODE (addr))
- {
- case REG:
- fprintf (file, "%s", names_big[REGNO (addr)]);
- break;
-
- case PRE_DEC:
- fprintf (file, "-%s", names_big[REGNO (XEXP (addr, 0))]);
- break;
-
- case POST_INC:
- fprintf (file, "%s+", names_big[REGNO (XEXP (addr, 0))]);
- break;
-
- case PLUS:
- fprintf (file, "(");
- if (GET_CODE (XEXP (addr, 0)) == REG)
- {
- /* reg,foo */
- print_operand_address (file, XEXP (addr, 1));
- fprintf (file, ",");
- print_operand_address (file, XEXP (addr, 0));
- }
- else
- {
- /* foo+k */
- print_operand_address (file, XEXP (addr, 0));
- fprintf (file, "+");
- print_operand_address (file, XEXP (addr, 1));
- }
- fprintf (file, ")");
- break;
-
- case CONST_INT:
- if (INTVAL (addr) < 0)
- {
- int v = -INTVAL (addr);
-
- fprintf (file, "-%d", v);
- }
- else
- fprintf (file, "%d", INTVAL (addr));
- break;
-
- default:
- output_addr_const (file, addr);
- break;
- }
- }
-
- /* Output to FILE a reference to the user-level label NAME.
- Strip off the section name if any. It is separated from the label name
- by a space. */
-
- void
- asm_output_labelref (file, name)
- FILE *file;
- char *name;
- {
- char *p;
-
- fputc ('_', file);
-
- for (p = name; *p; p++)
- {
- if (*p == ' ')
- {
- /* If we found a space in the name, then we've skipped over the
- section encoding. */
- fputs (p + 1, file);
- return;
- }
- }
-
- /* No space, so no section. */
- fputs (name, file);
- }
-
- /* Output all insn addresses and their sizes into the assembly language
- output file. This is helpful for debugging whether the length attributes
- in the md file are correct. This is not meant to be a user selectable
- option. */
-
- void
- final_prescan_insn (insn, operand, num_operands)
- rtx insn, *operand;
- int num_operands;
- {
- /* This holds the last insn address. */
- static int last_insn_address = 0;
-
- int uid = INSN_UID (insn);
-
- if (TARGET_ADDRESSES)
- {
- fprintf (asm_out_file, "; %d %d\n", insn_addresses[uid],
- insn_addresses[uid] - last_insn_address);
- last_insn_address = insn_addresses[uid];
- }
- }
-
- static void
- shift_n_bits (lval, operand, f, notzero)
- rtx lval;
- rtx operand;
- rtx (*f) ();
- int notzero;
- {
- rtx label = gen_label_rtx ();
- rtx bot = gen_label_rtx ();
-
- if (! notzero)
- {
- /* Have to put a zero test at the top. */
- emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, lval));
- emit_jump_insn (gen_beq (bot));
- }
- emit_label (label);
- f (operand);
- emit_insn (gen_rtx (SET, QImode, lval,
- gen_rtx (MINUS, QImode, lval, const1_rtx)));
- emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, lval));
- emit_jump_insn (gen_bne (label));
-
- emit_label (bot);
- /* We can't end an expand with a label. */
- emit_move_insn (operand, operand);
- }
-
- int
- can_shift (code, operands, f, limit, fby_eight)
- int code;
- rtx operands[];
- rtx (*f) ();
- int limit;
- rtx (*fby_eight) ();
- {
- extern int rtx_equal_function_value_matters;
-
- emit_move_insn (operands[0], operands[1]);
-
- if (GET_CODE (operands[2]) != CONST_INT)
- {
- rtx lval;
-
- /* Can't define expand a loop after rtl generation. */
- if (! rtx_equal_function_value_matters)
- return 0;
-
- lval = gen_reg_rtx (QImode);
-
- convert_move (lval, operands[2], 1);
- shift_n_bits (lval, operands[0], f, 0);
- }
- else
- {
- int i;
-
- i = INTVAL (operands[2]);
- if (i >= 8 && fby_eight)
- {
- fby_eight (operands[0]);
- i -= 8;
- }
- if (i > limit)
- {
- rtx lval;
-
- /* Can't define expand a loop after rtl generation. */
- if (! rtx_equal_function_value_matters)
- return 0;
- lval = gen_reg_rtx (QImode);
- emit_move_insn (lval, gen_rtx (CONST_INT, VOIDmode, i));
- shift_n_bits (lval, operands[0], f, 1);
- }
- else
- {
- while (i--)
- f (operands[0]);
- }
- }
- return 1;
- }
-
- int
- domovsi (operands)
- rtx operands[];
- {
- rtx src = operands[1];
- rtx dst = operands[0];
-
- if (push_operand (dst, GET_MODE (dst)))
- {
- /* Source must be a reg. */
- if (! REG_P (src))
- {
- rtx tmp = gen_reg_rtx (GET_MODE (dst));
-
- emit_move_insn (tmp, src);
- operands[1] = tmp;
- }
- }
- return 0;
- }
-
- int
- io (FROM, TO)
- {
- int OFFSET = 0;
-
- if ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM)
- (OFFSET) = 2 + frame_pointer_needed * 2;
- else
- {
- int regno;
- int offset = 0;
-
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if ((regs_ever_live[regno]
- && (! call_used_regs[regno] || regno == FRAME_POINTER_REGNUM)))
- offset += 2;
-
- (OFFSET) = offset + get_frame_size ();
-
- if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM)
- (OFFSET) += 2; /* Skip saved PC. */
- }
- return OFFSET;
- }
-