| /* Subroutines for insn-output.c for System/370. |
| Copyright (C) 1989, 1993, 1995 Free Software Foundation, Inc. |
| Contributed by Jan Stein (jan@cd.chalmers.se). |
| Modified for MVS C/370 by Dave Pitts (dpitts@nyx.cs.du.edu) |
| |
| 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, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.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" |
| |
| |
| /* Label node, this structure is used to keep track of labels on the |
| current page. */ |
| typedef struct label_node |
| { |
| struct label_node *label_next; |
| int label_id; |
| int label_page; |
| } |
| label_node_t; |
| |
| /* Is 1 when a label has been generated and the base register must be |
| reloaded. */ |
| int mvs_label_emitted = 0; |
| |
| /* Current function starting base page. */ |
| int function_base_page; |
| |
| /* Length of the current page code. */ |
| int mvs_page_code; |
| |
| /* Length of the current page literals. */ |
| int mvs_page_lit; |
| |
| /* Current function name. */ |
| char *mvs_function_name = 0; |
| |
| /* Current function name length. */ |
| int mvs_function_name_length = 0; |
| |
| /* Page number for multi-page functions. */ |
| int mvs_page_num = 0; |
| |
| /* Label node list anchor. */ |
| static label_node_t *label_anchor = 0; |
| |
| /* Label node free list anchor. */ |
| static label_node_t *free_anchor = 0; |
| |
| /* Assembler source file descriptor. */ |
| static FILE *assembler_source = 0; |
| |
| /* Define the length of the internal MVS function table. */ |
| #define MVS_FUNCTION_TABLE_LENGTH 32 |
| |
| /* C/370 internal function table. These functions use non-standard linkage |
| and must handled in a special manner. */ |
| static char *mvs_function_table[MVS_FUNCTION_TABLE_LENGTH] = |
| { |
| "ceil", "edc_acos", "edc_asin", "edc_ata2", "edc_atan", "edc_cos", |
| "edc_cosh", "edc_erf", "edc_erfc", "edc_exp", "edc_gamm", "edc_lg10", |
| "edc_log", "edc_sin", "edc_sinh", "edc_sqrt", "edc_tan", "edc_tanh", |
| "fabs", "floor", "fmod", "frexp", "hypot", "j0", |
| "j1", "jn", "ldexp", "modf", "pow", "y0", |
| "y1", "yn" |
| }; |
| |
| /* ASCII to EBCDIC conversion table. */ |
| #if defined(TARGET_EBCDIC) && !defined(HOST_EBCDIC) |
| static unsigned char ascebc[256] = |
| { |
| /*00 NL SH SX EX ET NQ AK BL */ |
| 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, |
| /*08 BS HT LF VT FF CR SO SI */ |
| 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, |
| /*10 DL D1 D2 D3 D4 NK SN EB */ |
| 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, |
| /*18 CN EM SB EC FS GS RS US */ |
| 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, |
| /*20 SP ! " # $ % & ' */ |
| 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, |
| /*28 ( ) * + , - . / */ |
| 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, |
| /*30 0 1 2 3 4 5 6 7 */ |
| 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, |
| /*38 8 9 : ; < = > ? */ |
| 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, |
| /*40 @ A B C D E F G */ |
| 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, |
| /*48 H I J K L M N O */ |
| 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, |
| /*50 P Q R S T U V W */ |
| 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, |
| /*58 X Y Z [ \ ] ^ _ */ |
| 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, |
| /*60 ` a b c d e f g */ |
| 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, |
| /*68 h i j k l m n o */ |
| 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, |
| /*70 p q r s t u v w */ |
| 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, |
| /*78 x y z { | } ~ DL */ |
| 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, |
| 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF |
| }; |
| #endif |
| |
| /* EBCDIC to ASCII conversion table. */ |
| #if defined(HOST_EBCDIC) && !defined(TARGET_EBCDIC) |
| unsigned char ebcasc[256] = |
| { |
| /*00 NU SH SX EX PF HT LC DL */ |
| 0x00, 0x01, 0x02, 0x03, 0x00, 0x09, 0x00, 0x7F, |
| /*08 SM VT FF CR SO SI */ |
| 0x00, 0x00, 0x00, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, |
| /*10 DE D1 D2 TM RS NL BS IL */ |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x0A, 0x08, 0x00, |
| /*18 CN EM CC C1 FS GS RS US */ |
| 0x18, 0x19, 0x00, 0x00, 0x1C, 0x1D, 0x1E, 0x1F, |
| /*20 DS SS FS BP LF EB EC */ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x17, 0x1B, |
| /*28 SM C2 EQ AK BL */ |
| 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, 0x00, |
| /*30 SY PN RS UC ET */ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, |
| /*38 C3 D4 NK SU */ |
| 0x00, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x1A, |
| /*40 SP */ |
| 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| /*48 . < ( + | */ |
| 0x00, 0x00, 0x00, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, |
| /*50 & */ |
| 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| /*58 ! $ * ) ; ^ */ |
| 0x00, 0x00, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E, |
| /*60 - / */ |
| 0x2D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| /*68 , % _ > ? */ |
| 0x00, 0x00, 0x00, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, |
| /*70 */ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| /*78 ` : # @ ' = " */ |
| 0x00, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, |
| /*80 a b c d e f g */ |
| 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, |
| /*88 h i { */ |
| 0x68, 0x69, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00, |
| /*90 j k l m n o p */ |
| 0x00, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, |
| /*98 q r } */ |
| 0x71, 0x72, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00, |
| /*A0 ~ s t u v w x */ |
| 0x00, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, |
| /*A8 y z [ */ |
| 0x79, 0x7A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, |
| /*B0 */ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| /*B8 ] */ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, |
| /*C0 { A B C D E F G */ |
| 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, |
| /*C8 H I */ |
| 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| /*D0 } J K L M N O P */ |
| 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, |
| /*D8 Q R */ |
| 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| /*E0 \ S T U V W X */ |
| 0x5C, 0x00, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, |
| /*E8 Y Z */ |
| 0x59, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| /*F0 0 1 2 3 4 5 6 7 */ |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
| /*F8 8 9 */ |
| 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF |
| }; |
| #endif |
| |
| /* Map characters from one character set to another. |
| C is the character to be translated. */ |
| |
| char |
| mvs_map_char (c) |
| char c; |
| { |
| #if defined(TARGET_EBCDIC) && !defined(HOST_EBCDIC) |
| return ascebc[c]; |
| #else |
| #if defined(HOST_EBCDIC) && !defined(TARGET_EBCDIC) |
| return ebcasc[c]; |
| #else |
| return c; |
| #endif |
| #endif |
| } |
| |
| /* Emit reload of base register if indicated. This is to eliminate multiple |
| reloads when several labels are generated pointing to the same place |
| in the code. */ |
| |
| int |
| check_label_emit (void) |
| { |
| if (mvs_label_emitted) |
| { |
| mvs_label_emitted = 0; |
| mvs_page_code += 4; |
| fprintf (assembler_source, "\tL\t%d,%d(,%d)\n", |
| BASE_REGISTER, (mvs_page_num - function_base_page) * 4, |
| PAGE_REGISTER); |
| } |
| } |
| |
| /* Add the label to the current page label list. If a free element is available |
| it will be used for the new label. Otherwise, a label element will be |
| allocated from memory. |
| ID is the label number of the label being added to the list. */ |
| |
| int |
| mvs_add_label (id) |
| int id; |
| { |
| label_node_t *lp; |
| |
| if (free_anchor) |
| { |
| lp = free_anchor; |
| free_anchor = lp->label_next; |
| } |
| else |
| { |
| lp = (label_node_t *) malloc (sizeof (label_node_t)); |
| if (lp == 0) |
| { |
| fatal ("virtual memory exhausted\n"); |
| abort (); |
| } |
| } |
| lp->label_id = id; |
| lp->label_page = mvs_page_num; |
| lp->label_next = label_anchor; |
| label_anchor = lp; |
| } |
| |
| /* Check to see if the label is in the list. If 1 is returned then a load |
| and branch on register must be generated. |
| ID is the label number of the label being checked. */ |
| |
| int |
| mvs_check_label (id) |
| int id; |
| { |
| label_node_t *lp; |
| |
| for (lp = label_anchor; lp; lp = lp->label_next) |
| { |
| if (lp->label_id == id) |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* The label list for the current page freed by linking the list onto the free |
| label element chain. */ |
| |
| int |
| mvs_free_label (void) |
| { |
| if (label_anchor) |
| { |
| if (free_anchor) |
| label_anchor->label_next = free_anchor; |
| free_anchor = label_anchor; |
| } |
| label_anchor = 0; |
| } |
| |
| /* If the page size limit is reached a new code page is started, and the base |
| register is set to it. This page break point is counted conservatively, |
| most literals that have the same value are collapsed by the assembler. |
| True is returned when a new page is started. |
| FILE is the assembler output file descriptor. |
| CODE is the length, in bytes, of the instruction to be emitted. |
| LIT is the length of the literal to be emitted. */ |
| |
| int |
| mvs_check_page (file, code, lit) |
| FILE *file; |
| int code, lit; |
| { |
| if (file) |
| assembler_source = file; |
| |
| if (mvs_page_code + code + mvs_page_lit + lit > MAX_MVS_PAGE_LENGTH) |
| { |
| fprintf (assembler_source, "\tB\tPGE%d\n", mvs_page_num); |
| fprintf (assembler_source, "\tDS\t0F\n"); |
| fprintf (assembler_source, "\tLTORG\n"); |
| fprintf (assembler_source, "\tDS\t0F\n"); |
| fprintf (assembler_source, "PGE%d\tEQU\t*\n", mvs_page_num); |
| fprintf (assembler_source, "\tDROP\t%d\n", BASE_REGISTER); |
| mvs_page_num++; |
| fprintf (assembler_source, "\tBALR\t%d,0\n", BASE_REGISTER); |
| fprintf (assembler_source, "PG%d\tEQU\t*\n", mvs_page_num); |
| fprintf (assembler_source, "\tUSING\t*,%d\n", BASE_REGISTER); |
| mvs_free_label (); |
| mvs_page_code = code; |
| mvs_page_lit = lit; |
| return 1; |
| } |
| mvs_page_code += code; |
| mvs_page_lit += lit; |
| return 0; |
| } |
| |
| /* Check for C/370 runtime function, they don't use standard calling |
| conventions. True is returned if the function is in the table. |
| NAME is the name of the current function. */ |
| |
| int |
| mvs_function_check (name) |
| char *name; |
| { |
| int lower, middle, upper; |
| int i; |
| |
| lower = 0; |
| upper = MVS_FUNCTION_TABLE_LENGTH - 1; |
| while (lower <= upper) |
| { |
| middle = (lower + upper) / 2; |
| i = strcmp (name, mvs_function_table[middle]); |
| if (i == 0) |
| return 1; |
| if (i < 0) |
| upper = middle - 1; |
| else |
| lower = middle + 1; |
| } |
| return 0; |
| } |
| |
| |
| /* Return 1 if OP is a valid S operand for an RS, SI or SS type instruction. |
| OP is the current operation. |
| MODE is the current operation mode. */ |
| |
| int |
| s_operand (op, mode) |
| register rtx op; |
| enum machine_mode mode; |
| { |
| extern int volatile_ok; |
| register enum rtx_code code = GET_CODE (op); |
| |
| if (CONSTANT_ADDRESS_P (op)) |
| return 1; |
| if (mode == VOIDmode || GET_MODE (op) != mode) |
| return 0; |
| if (code == MEM) |
| { |
| register rtx x = XEXP (op, 0); |
| |
| if (!volatile_ok && op->volatil) |
| return 0; |
| if (REG_P (x) && REG_OK_FOR_BASE_P (x)) |
| return 1; |
| if (GET_CODE (x) == PLUS |
| && REG_P (XEXP (x, 0)) && REG_OK_FOR_BASE_P (XEXP (x, 0)) |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && (unsigned) INTVAL (XEXP (x, 1)) < 4096) |
| return 1; |
| } |
| return 0; |
| } |
| |
| |
| /* Return 1 if OP is a valid R or S operand for an RS, SI or SS type |
| instruction. |
| OP is the current operation. |
| MODE is the current operation mode. */ |
| |
| int |
| r_or_s_operand (op, mode) |
| register rtx op; |
| enum machine_mode mode; |
| { |
| extern int volatile_ok; |
| register enum rtx_code code = GET_CODE (op); |
| |
| if (CONSTANT_ADDRESS_P (op)) |
| return 1; |
| if (mode == VOIDmode || GET_MODE (op) != mode) |
| return 0; |
| if (code == REG) |
| return 1; |
| else if (code == MEM) |
| { |
| register rtx x = XEXP (op, 0); |
| |
| if (!volatile_ok && op->volatil) |
| return 0; |
| if (REG_P (x) && REG_OK_FOR_BASE_P (x)) |
| return 1; |
| if (GET_CODE (x) == PLUS |
| && REG_P (XEXP (x, 0)) && REG_OK_FOR_BASE_P (XEXP (x, 0)) |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && (unsigned) INTVAL (XEXP (x, 1)) < 4096) |
| return 1; |
| } |
| return 0; |
| } |
| |
| |
| /* Return 1 if the next instruction is an unsigned jump instruction. |
| INSN is the current instruction. */ |
| |
| unsigned_jump_follows_p (insn) |
| register rtx insn; |
| { |
| insn = NEXT_INSN (insn); |
| if (GET_CODE (insn) != JUMP_INSN) |
| return 0; |
| |
| insn = XEXP (insn, 3); |
| if (GET_CODE (insn) != SET) |
| return 0; |
| |
| if (GET_CODE (XEXP (insn, 0)) != PC) |
| return 0; |
| |
| insn = XEXP (insn, 1); |
| if (GET_CODE (insn) != IF_THEN_ELSE) |
| return 0; |
| |
| insn = XEXP (insn, 0); |
| return GET_CODE (insn) != GE && GET_CODE (insn) != GT |
| && GET_CODE (insn) != LE && GET_CODE (insn) != LT; |
| } |