| /* ste.c -- Implementation File (module.c template V1.0) |
| Copyright (C) 1995, 1996, 2000, 2002, 2003 Free Software Foundation, Inc. |
| Contributed by James Craig Burley. |
| |
| This file is part of GNU Fortran. |
| |
| GNU Fortran 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 Fortran 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 Fortran; see the file COPYING. If not, write to |
| the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| Related Modules: |
| ste.c |
| |
| Description: |
| Implements the various statements and such like. |
| |
| Modifications: |
| */ |
| |
| /* Include files. */ |
| |
| #include "proj.h" |
| #include "rtl.h" |
| #include "toplev.h" |
| #include "ggc.h" |
| #include "ste.h" |
| #include "bld.h" |
| #include "com.h" |
| #include "expr.h" |
| #include "lab.h" |
| #include "lex.h" |
| #include "sta.h" |
| #include "stp.h" |
| #include "str.h" |
| #include "sts.h" |
| #include "stt.h" |
| #include "stv.h" |
| #include "stw.h" |
| #include "symbol.h" |
| |
| /* Externals defined here. */ |
| |
| |
| /* Simple definitions and enumerations. */ |
| |
| typedef enum |
| { |
| FFESTE_stateletSIMPLE_, /* Expecting simple/start. */ |
| FFESTE_stateletATTRIB_, /* Expecting attrib/item/itemstart. */ |
| FFESTE_stateletITEM_, /* Expecting item/itemstart/finish. */ |
| FFESTE_stateletITEMVALS_, /* Expecting itemvalue/itemendvals. */ |
| FFESTE_ |
| } ffesteStatelet_; |
| |
| /* Internal typedefs. */ |
| |
| |
| /* Private include files. */ |
| |
| |
| /* Internal structure definitions. */ |
| |
| |
| /* Static objects accessed by functions in this module. */ |
| |
| static ffesteStatelet_ ffeste_statelet_ = FFESTE_stateletSIMPLE_; |
| static ffelab ffeste_label_formatdef_ = NULL; |
| static tree (*ffeste_io_driver_) (ffebld expr); /* do?io. */ |
| static ffecomGfrt ffeste_io_endgfrt_; /* end function to call. */ |
| static tree ffeste_io_abort_; /* abort-io label or NULL_TREE. */ |
| static bool ffeste_io_abort_is_temp_; /* abort-io label is a temp. */ |
| static tree ffeste_io_end_; /* END= label or NULL_TREE. */ |
| static tree ffeste_io_err_; /* ERR= label or NULL_TREE. */ |
| static tree ffeste_io_iostat_; /* IOSTAT= var or NULL_TREE. */ |
| static bool ffeste_io_iostat_is_temp_; /* IOSTAT= var is a temp. */ |
| |
| /* Static functions (internal). */ |
| |
| static void ffeste_begin_iterdo_ (ffestw block, tree *tvar, tree *tincr, |
| tree *xitersvar, ffebld var, |
| ffebld start, ffelexToken start_token, |
| ffebld end, ffelexToken end_token, |
| ffebld incr, ffelexToken incr_token, |
| const char *msg); |
| static void ffeste_end_iterdo_ (ffestw block, tree tvar, tree tincr, |
| tree itersvar); |
| static void ffeste_io_call_ (tree call, bool do_check); |
| static void ffeste_io_impdo_ (ffebld impdo, ffelexToken impdo_token); |
| static tree ffeste_io_dofio_ (ffebld expr); |
| static tree ffeste_io_dolio_ (ffebld expr); |
| static tree ffeste_io_douio_ (ffebld expr); |
| static tree ffeste_io_ialist_ (bool have_err, ffestvUnit unit, |
| ffebld unit_expr, int unit_dflt); |
| static tree ffeste_io_cilist_ (bool have_err, ffestvUnit unit, |
| ffebld unit_expr, int unit_dflt, |
| bool have_end, ffestvFormat format, |
| ffestpFile *format_spec, bool rec, |
| ffebld rec_expr); |
| static tree ffeste_io_cllist_ (bool have_err, ffebld unit_expr, |
| ffestpFile *stat_spec); |
| static tree ffeste_io_icilist_ (bool have_err, ffebld unit_expr, |
| bool have_end, ffestvFormat format, |
| ffestpFile *format_spec); |
| static tree ffeste_io_inlist_ (bool have_err, |
| ffestpFile *unit_spec, |
| ffestpFile *file_spec, |
| ffestpFile *exist_spec, |
| ffestpFile *open_spec, |
| ffestpFile *number_spec, |
| ffestpFile *named_spec, |
| ffestpFile *name_spec, |
| ffestpFile *access_spec, |
| ffestpFile *sequential_spec, |
| ffestpFile *direct_spec, |
| ffestpFile *form_spec, |
| ffestpFile *formatted_spec, |
| ffestpFile *unformatted_spec, |
| ffestpFile *recl_spec, |
| ffestpFile *nextrec_spec, |
| ffestpFile *blank_spec); |
| static tree ffeste_io_olist_ (bool have_err, ffebld unit_expr, |
| ffestpFile *file_spec, |
| ffestpFile *stat_spec, |
| ffestpFile *access_spec, |
| ffestpFile *form_spec, |
| ffestpFile *recl_spec, |
| ffestpFile *blank_spec); |
| static void ffeste_subr_beru_ (ffestpBeruStmt *info, ffecomGfrt rt); |
| |
| /* Internal macros. */ |
| |
| #define ffeste_emit_line_note_() \ |
| emit_line_note (input_location) |
| #define ffeste_check_simple_() \ |
| assert(ffeste_statelet_ == FFESTE_stateletSIMPLE_) |
| #define ffeste_check_start_() \ |
| assert(ffeste_statelet_ == FFESTE_stateletSIMPLE_); \ |
| ffeste_statelet_ = FFESTE_stateletATTRIB_ |
| #define ffeste_check_attrib_() \ |
| assert(ffeste_statelet_ == FFESTE_stateletATTRIB_) |
| #define ffeste_check_item_() \ |
| assert(ffeste_statelet_ == FFESTE_stateletATTRIB_ \ |
| || ffeste_statelet_ == FFESTE_stateletITEM_); \ |
| ffeste_statelet_ = FFESTE_stateletITEM_ |
| #define ffeste_check_item_startvals_() \ |
| assert(ffeste_statelet_ == FFESTE_stateletATTRIB_ \ |
| || ffeste_statelet_ == FFESTE_stateletITEM_); \ |
| ffeste_statelet_ = FFESTE_stateletITEMVALS_ |
| #define ffeste_check_item_value_() \ |
| assert(ffeste_statelet_ == FFESTE_stateletITEMVALS_) |
| #define ffeste_check_item_endvals_() \ |
| assert(ffeste_statelet_ == FFESTE_stateletITEMVALS_); \ |
| ffeste_statelet_ = FFESTE_stateletITEM_ |
| #define ffeste_check_finish_() \ |
| assert(ffeste_statelet_ == FFESTE_stateletATTRIB_ \ |
| || ffeste_statelet_ == FFESTE_stateletITEM_); \ |
| ffeste_statelet_ = FFESTE_stateletSIMPLE_ |
| |
| #define ffeste_f2c_init_charnolen_(Exp,Init,Spec) \ |
| do \ |
| { \ |
| if ((Spec)->kw_or_val_present) \ |
| Exp = ffecom_arg_ptr_to_const_expr ((Spec)->u.expr, &ignore); \ |
| else \ |
| Exp = null_pointer_node; \ |
| if (Exp) \ |
| Init = Exp; \ |
| else \ |
| { \ |
| Init = null_pointer_node; \ |
| constantp = FALSE; \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_init_char_(Exp,Init,Lenexp,Leninit,Spec) \ |
| do \ |
| { \ |
| if ((Spec)->kw_or_val_present) \ |
| Exp = ffecom_arg_ptr_to_const_expr ((Spec)->u.expr, &Lenexp); \ |
| else \ |
| { \ |
| Exp = null_pointer_node; \ |
| Lenexp = ffecom_f2c_ftnlen_zero_node; \ |
| } \ |
| if (Exp) \ |
| Init = Exp; \ |
| else \ |
| { \ |
| Init = null_pointer_node; \ |
| constantp = FALSE; \ |
| } \ |
| if (Lenexp) \ |
| Leninit = Lenexp; \ |
| else \ |
| { \ |
| Leninit = ffecom_f2c_ftnlen_zero_node; \ |
| constantp = FALSE; \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_init_flag_(Flag,Init) \ |
| do \ |
| { \ |
| Init = convert (ffecom_f2c_flag_type_node, \ |
| (Flag) ? integer_one_node : integer_zero_node); \ |
| } while(0) |
| |
| #define ffeste_f2c_init_format_(Exp,Init,Spec) \ |
| do \ |
| { \ |
| Exp = ffecom_arg_ptr_to_const_expr ((Spec)->u.expr, NULL); \ |
| if (Exp) \ |
| Init = Exp; \ |
| else \ |
| { \ |
| Init = null_pointer_node; \ |
| constantp = FALSE; \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_init_int_(Exp,Init,Spec) \ |
| do \ |
| { \ |
| if ((Spec)->kw_or_val_present) \ |
| Exp = ffecom_const_expr ((Spec)->u.expr); \ |
| else \ |
| Exp = ffecom_integer_zero_node; \ |
| if (Exp) \ |
| Init = Exp; \ |
| else \ |
| { \ |
| Init = ffecom_integer_zero_node; \ |
| constantp = FALSE; \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_init_ptrtoint_(Exp,Init,Spec) \ |
| do \ |
| { \ |
| if ((Spec)->kw_or_val_present) \ |
| Exp = ffecom_ptr_to_const_expr ((Spec)->u.expr); \ |
| else \ |
| Exp = null_pointer_node; \ |
| if (Exp) \ |
| Init = Exp; \ |
| else \ |
| { \ |
| Init = null_pointer_node; \ |
| constantp = FALSE; \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_init_next_(Init) \ |
| do \ |
| { \ |
| TREE_CHAIN (initn) = build_tree_list ((field = TREE_CHAIN (field)), \ |
| (Init)); \ |
| initn = TREE_CHAIN(initn); \ |
| } while(0) |
| |
| #define ffeste_f2c_prepare_charnolen_(Spec,Exp) \ |
| do \ |
| { \ |
| if (! (Exp)) \ |
| ffecom_prepare_arg_ptr_to_expr ((Spec)->u.expr); \ |
| } while(0) |
| |
| #define ffeste_f2c_prepare_char_(Spec,Exp) \ |
| do \ |
| { \ |
| if (! (Exp)) \ |
| ffecom_prepare_arg_ptr_to_expr ((Spec)->u.expr); \ |
| } while(0) |
| |
| #define ffeste_f2c_prepare_format_(Spec,Exp) \ |
| do \ |
| { \ |
| if (! (Exp)) \ |
| ffecom_prepare_arg_ptr_to_expr ((Spec)->u.expr); \ |
| } while(0) |
| |
| #define ffeste_f2c_prepare_int_(Spec,Exp) \ |
| do \ |
| { \ |
| if (! (Exp)) \ |
| ffecom_prepare_expr ((Spec)->u.expr); \ |
| } while(0) |
| |
| #define ffeste_f2c_prepare_ptrtoint_(Spec,Exp) \ |
| do \ |
| { \ |
| if (! (Exp)) \ |
| ffecom_prepare_ptr_to_expr ((Spec)->u.expr); \ |
| } while(0) |
| |
| #define ffeste_f2c_compile_(Field,Exp) \ |
| do \ |
| { \ |
| tree exz; \ |
| if ((Exp)) \ |
| { \ |
| exz = ffecom_modify (void_type_node, \ |
| ffecom_2 (COMPONENT_REF, TREE_TYPE (Field), \ |
| t, (Field)), \ |
| (Exp)); \ |
| expand_expr_stmt (exz); \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_compile_charnolen_(Field,Spec,Exp) \ |
| do \ |
| { \ |
| tree exq; \ |
| if (! (Exp)) \ |
| { \ |
| exq = ffecom_arg_ptr_to_expr ((Spec)->u.expr, &ignore); \ |
| ffeste_f2c_compile_ ((Field), exq); \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_compile_char_(Field,Lenfield,Spec,Exp,Lenexp) \ |
| do \ |
| { \ |
| tree exq = (Exp); \ |
| tree lenexq = (Lenexp); \ |
| int need_exq = (! exq); \ |
| int need_lenexq = (! lenexq); \ |
| if (need_exq || need_lenexq) \ |
| { \ |
| exq = ffecom_arg_ptr_to_expr ((Spec)->u.expr, &lenexq); \ |
| if (need_exq) \ |
| ffeste_f2c_compile_ ((Field), exq); \ |
| if (need_lenexq) \ |
| ffeste_f2c_compile_ ((Lenfield), lenexq); \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_compile_format_(Field,Spec,Exp) \ |
| do \ |
| { \ |
| tree exq; \ |
| if (! (Exp)) \ |
| { \ |
| exq = ffecom_arg_ptr_to_expr ((Spec)->u.expr, NULL); \ |
| ffeste_f2c_compile_ ((Field), exq); \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_compile_int_(Field,Spec,Exp) \ |
| do \ |
| { \ |
| tree exq; \ |
| if (! (Exp)) \ |
| { \ |
| exq = ffecom_expr ((Spec)->u.expr); \ |
| ffeste_f2c_compile_ ((Field), exq); \ |
| } \ |
| } while(0) |
| |
| #define ffeste_f2c_compile_ptrtoint_(Field,Spec,Exp) \ |
| do \ |
| { \ |
| tree exq; \ |
| if (! (Exp)) \ |
| { \ |
| exq = ffecom_ptr_to_expr ((Spec)->u.expr); \ |
| ffeste_f2c_compile_ ((Field), exq); \ |
| } \ |
| } while(0) |
| |
| /* Start a Fortran block. */ |
| |
| #ifdef ENABLE_CHECKING |
| |
| typedef struct gbe_block |
| { |
| struct gbe_block *outer; |
| ffestw block; |
| location_t location; |
| bool is_stmt; |
| } *gbe_block; |
| |
| gbe_block ffeste_top_block_ = NULL; |
| |
| static void |
| ffeste_start_block_ (ffestw block) |
| { |
| gbe_block b = xmalloc (sizeof (*b)); |
| |
| b->outer = ffeste_top_block_; |
| b->block = block; |
| b->location = input_location; |
| b->is_stmt = FALSE; |
| |
| ffeste_top_block_ = b; |
| |
| ffecom_start_compstmt (); |
| } |
| |
| /* End a Fortran block. */ |
| |
| static void |
| ffeste_end_block_ (ffestw block) |
| { |
| gbe_block b = ffeste_top_block_; |
| |
| assert (b); |
| assert (! b->is_stmt); |
| assert (b->block == block); |
| assert (! b->is_stmt); |
| |
| ffeste_top_block_ = b->outer; |
| |
| free (b); |
| |
| ffecom_end_compstmt (); |
| } |
| |
| /* Start a Fortran statement. |
| |
| Starts a back-end block, so temporaries can be managed, clean-ups |
| properly handled, etc. Nesting of statements *is* allowed -- the |
| handling of I/O items, even implied-DO I/O lists, within a READ, |
| PRINT, or WRITE statement is one example. */ |
| |
| static void |
| ffeste_start_stmt_(void) |
| { |
| gbe_block b = xmalloc (sizeof (*b)); |
| |
| b->outer = ffeste_top_block_; |
| b->block = NULL; |
| b->location = input_location; |
| b->is_stmt = TRUE; |
| |
| ffeste_top_block_ = b; |
| |
| ffecom_start_compstmt (); |
| } |
| |
| /* End a Fortran statement. */ |
| |
| static void |
| ffeste_end_stmt_(void) |
| { |
| gbe_block b = ffeste_top_block_; |
| |
| assert (b); |
| assert (b->is_stmt); |
| |
| ffeste_top_block_ = b->outer; |
| |
| free (b); |
| |
| ffecom_end_compstmt (); |
| } |
| |
| #else /* ! defined (ENABLE_CHECKING) */ |
| |
| #define ffeste_start_block_(b) ffecom_start_compstmt () |
| #define ffeste_end_block_(b) \ |
| do \ |
| { \ |
| ffecom_end_compstmt (); \ |
| } while(0) |
| #define ffeste_start_stmt_() ffeste_start_block_(NULL) |
| #define ffeste_end_stmt_() ffeste_end_block_(NULL) |
| |
| #endif /* ! defined (ENABLE_CHECKING) */ |
| |
| /* Begin an iterative DO loop. Pass the block to start if |
| applicable. */ |
| |
| static void |
| ffeste_begin_iterdo_ (ffestw block, tree *xtvar, tree *xtincr, |
| tree *xitersvar, ffebld var, |
| ffebld start, ffelexToken start_token, |
| ffebld end, ffelexToken end_token, |
| ffebld incr, ffelexToken incr_token, |
| const char *msg) |
| { |
| tree tvar; |
| tree expr; |
| tree tstart; |
| tree tend; |
| tree tincr; |
| tree tincr_saved; |
| tree niters; |
| struct nesting *expanded_loop; |
| |
| /* Want to have tvar, tincr, and niters for the whole loop body. */ |
| |
| if (block) |
| ffeste_start_block_ (block); |
| else |
| ffeste_start_stmt_ (); |
| |
| niters = ffecom_make_tempvar (block ? "do" : "impdo", |
| ffecom_integer_type_node, |
| FFETARGET_charactersizeNONE, -1); |
| |
| ffecom_prepare_expr (incr); |
| ffecom_prepare_expr_rw (NULL_TREE, var); |
| |
| ffecom_prepare_end (); |
| |
| tvar = ffecom_expr_rw (NULL_TREE, var); |
| tincr = ffecom_expr (incr); |
| |
| if (TREE_CODE (tvar) == ERROR_MARK |
| || TREE_CODE (tincr) == ERROR_MARK) |
| { |
| if (block) |
| { |
| ffeste_end_block_ (block); |
| ffestw_set_do_tvar (block, error_mark_node); |
| } |
| else |
| { |
| ffeste_end_stmt_ (); |
| *xtvar = error_mark_node; |
| } |
| return; |
| } |
| |
| /* Check whether incr is known to be zero, complain and fix. */ |
| |
| if (integer_zerop (tincr) || real_zerop (tincr)) |
| { |
| ffebad_start (FFEBAD_DO_STEP_ZERO); |
| ffebad_here (0, ffelex_token_where_line (incr_token), |
| ffelex_token_where_column (incr_token)); |
| ffebad_string (msg); |
| ffebad_finish (); |
| tincr = convert (TREE_TYPE (tvar), integer_one_node); |
| } |
| |
| tincr_saved = ffecom_save_tree (tincr); |
| |
| /* Want to have tstart, tend for just this statement. */ |
| |
| ffeste_start_stmt_ (); |
| |
| ffecom_prepare_expr (start); |
| ffecom_prepare_expr (end); |
| |
| ffecom_prepare_end (); |
| |
| tstart = ffecom_expr (start); |
| tend = ffecom_expr (end); |
| |
| if (TREE_CODE (tstart) == ERROR_MARK |
| || TREE_CODE (tend) == ERROR_MARK) |
| { |
| ffeste_end_stmt_ (); |
| |
| if (block) |
| { |
| ffeste_end_block_ (block); |
| ffestw_set_do_tvar (block, error_mark_node); |
| } |
| else |
| { |
| ffeste_end_stmt_ (); |
| *xtvar = error_mark_node; |
| } |
| return; |
| } |
| |
| /* For warnings only, nothing else happens here. */ |
| { |
| tree try; |
| |
| if (! ffe_is_onetrip ()) |
| { |
| try = ffecom_2 (MINUS_EXPR, TREE_TYPE (tvar), |
| tend, |
| tstart); |
| |
| try = ffecom_2 (PLUS_EXPR, TREE_TYPE (tvar), |
| try, |
| tincr); |
| |
| if (TREE_CODE (TREE_TYPE (tvar)) != REAL_TYPE) |
| try = ffecom_2 (TRUNC_DIV_EXPR, integer_type_node, try, |
| tincr); |
| else |
| try = convert (integer_type_node, |
| ffecom_2 (RDIV_EXPR, TREE_TYPE (tvar), |
| try, |
| tincr)); |
| |
| /* Warn if loop never executed, since we've done the evaluation |
| of the unofficial iteration count already. */ |
| |
| try = ffecom_truth_value (ffecom_2 (LE_EXPR, integer_type_node, |
| try, |
| convert (TREE_TYPE (tvar), |
| integer_zero_node))); |
| |
| if (integer_onep (try)) |
| { |
| ffebad_start (FFEBAD_DO_NULL); |
| ffebad_here (0, ffelex_token_where_line (start_token), |
| ffelex_token_where_column (start_token)); |
| ffebad_string (msg); |
| ffebad_finish (); |
| } |
| } |
| |
| /* Warn if end plus incr would overflow. */ |
| |
| try = ffecom_2 (PLUS_EXPR, TREE_TYPE (tvar), |
| tend, |
| tincr); |
| |
| if ((TREE_CODE_CLASS (TREE_CODE (try)) == 'c') |
| && TREE_CONSTANT_OVERFLOW (try)) |
| { |
| ffebad_start (FFEBAD_DO_END_OVERFLOW); |
| ffebad_here (0, ffelex_token_where_line (end_token), |
| ffelex_token_where_column (end_token)); |
| ffebad_string (msg); |
| ffebad_finish (); |
| } |
| } |
| |
| /* Do the initial assignment into the DO var. */ |
| |
| tstart = ffecom_save_tree (tstart); |
| |
| expr = ffecom_2 (MINUS_EXPR, TREE_TYPE (tvar), |
| tend, |
| tstart); |
| |
| if (! ffe_is_onetrip ()) |
| { |
| expr = ffecom_2 (PLUS_EXPR, TREE_TYPE (expr), |
| expr, |
| convert (TREE_TYPE (expr), tincr_saved)); |
| } |
| |
| if (TREE_CODE (TREE_TYPE (tvar)) != REAL_TYPE) |
| expr = ffecom_2 (TRUNC_DIV_EXPR, TREE_TYPE (expr), |
| expr, |
| tincr_saved); |
| else |
| expr = ffecom_2 (RDIV_EXPR, TREE_TYPE (expr), |
| expr, |
| tincr_saved); |
| |
| #if 1 /* New, F90-approved approach: convert to default INTEGER. */ |
| if (TREE_TYPE (tvar) != error_mark_node) |
| expr = convert (ffecom_integer_type_node, expr); |
| #else /* Old approach; convert to INTEGER unless that's a narrowing. */ |
| if ((TREE_TYPE (tvar) != error_mark_node) |
| && ((TREE_CODE (TREE_TYPE (tvar)) != INTEGER_TYPE) |
| || ((TYPE_SIZE (TREE_TYPE (tvar)) != NULL_TREE) |
| && ((TREE_CODE (TYPE_SIZE (TREE_TYPE (tvar))) |
| != INTEGER_CST) |
| || (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (tvar))) |
| <= TREE_INT_CST_LOW (TYPE_SIZE (ffecom_integer_type_node))))))) |
| /* Convert unless promoting INTEGER type of any kind downward to |
| default INTEGER; else leave as, say, INTEGER*8 (long long int). */ |
| expr = convert (ffecom_integer_type_node, expr); |
| #endif |
| |
| assert (TYPE_MAIN_VARIANT (TREE_TYPE (niters)) |
| == TYPE_MAIN_VARIANT (TREE_TYPE (expr))); |
| |
| expr = ffecom_modify (void_type_node, niters, expr); |
| expand_expr_stmt (expr); |
| |
| expr = ffecom_modify (void_type_node, tvar, tstart); |
| expand_expr_stmt (expr); |
| |
| ffeste_end_stmt_ (); |
| |
| expanded_loop = expand_start_loop_continue_elsewhere (!! block); |
| if (block) |
| ffestw_set_do_hook (block, expanded_loop); |
| |
| if (! ffe_is_onetrip ()) |
| { |
| expr = ffecom_truth_value |
| (ffecom_2 (GE_EXPR, integer_type_node, |
| ffecom_2 (PREDECREMENT_EXPR, |
| TREE_TYPE (niters), |
| niters, |
| convert (TREE_TYPE (niters), |
| ffecom_integer_one_node)), |
| convert (TREE_TYPE (niters), |
| ffecom_integer_zero_node))); |
| |
| expand_exit_loop_top_cond (0, expr); |
| } |
| |
| if (block) |
| { |
| ffestw_set_do_tvar (block, tvar); |
| ffestw_set_do_incr_saved (block, tincr_saved); |
| ffestw_set_do_count_var (block, niters); |
| } |
| else |
| { |
| *xtvar = tvar; |
| *xtincr = tincr_saved; |
| *xitersvar = niters; |
| } |
| } |
| |
| /* End an iterative DO loop. Pass the same iteration variable and increment |
| value trees that were generated in the paired _begin_ call. */ |
| |
| static void |
| ffeste_end_iterdo_ (ffestw block, tree tvar, tree tincr, tree itersvar) |
| { |
| tree expr; |
| tree niters = itersvar; |
| |
| if (tvar == error_mark_node) |
| return; |
| |
| expand_loop_continue_here (); |
| |
| ffeste_start_stmt_ (); |
| |
| if (ffe_is_onetrip ()) |
| { |
| expr = ffecom_truth_value |
| (ffecom_2 (GE_EXPR, integer_type_node, |
| ffecom_2 (PREDECREMENT_EXPR, |
| TREE_TYPE (niters), |
| niters, |
| convert (TREE_TYPE (niters), |
| ffecom_integer_one_node)), |
| convert (TREE_TYPE (niters), |
| ffecom_integer_zero_node))); |
| |
| expand_exit_loop_if_false (0, expr); |
| } |
| |
| expr = ffecom_modify (void_type_node, tvar, |
| ffecom_2 (PLUS_EXPR, TREE_TYPE (tvar), |
| tvar, |
| tincr)); |
| expand_expr_stmt (expr); |
| |
| /* Lose the stuff we just built. */ |
| ffeste_end_stmt_ (); |
| |
| expand_end_loop (); |
| |
| /* Lose the tvar and incr_saved trees. */ |
| if (block) |
| ffeste_end_block_ (block); |
| else |
| ffeste_end_stmt_ (); |
| } |
| |
| /* Generate call to run-time I/O routine. */ |
| |
| static void |
| ffeste_io_call_ (tree call, bool do_check) |
| { |
| /* Generate the call and optional assignment into iostat var. */ |
| |
| TREE_SIDE_EFFECTS (call) = 1; |
| if (ffeste_io_iostat_ != NULL_TREE) |
| call = ffecom_modify (do_check ? NULL_TREE : void_type_node, |
| ffeste_io_iostat_, call); |
| expand_expr_stmt (call); |
| |
| if (! do_check |
| || ffeste_io_abort_ == NULL_TREE |
| || TREE_CODE (ffeste_io_abort_) == ERROR_MARK) |
| return; |
| |
| /* Generate optional test. */ |
| |
| expand_start_cond (ffecom_truth_value (ffeste_io_iostat_), 0); |
| expand_goto (ffeste_io_abort_); |
| expand_end_cond (); |
| } |
| |
| /* Handle implied-DO in I/O list. |
| |
| Expands code to start up the DO loop. Then for each item in the |
| DO loop, handles appropriately (possibly including recursively calling |
| itself). Then expands code to end the DO loop. */ |
| |
| static void |
| ffeste_io_impdo_ (ffebld impdo, ffelexToken impdo_token) |
| { |
| ffebld var = ffebld_head (ffebld_right (impdo)); |
| ffebld start = ffebld_head (ffebld_trail (ffebld_right (impdo))); |
| ffebld end = ffebld_head (ffebld_trail (ffebld_trail |
| (ffebld_right (impdo)))); |
| ffebld incr = ffebld_head (ffebld_trail (ffebld_trail |
| (ffebld_trail (ffebld_right (impdo))))); |
| ffebld list; |
| ffebld item; |
| tree tvar; |
| tree tincr; |
| tree titervar; |
| |
| if (incr == NULL) |
| { |
| incr = ffebld_new_conter (ffebld_constant_new_integerdefault_val (1)); |
| ffebld_set_info (incr, ffeinfo_new |
| (FFEINFO_basictypeINTEGER, |
| FFEINFO_kindtypeINTEGERDEFAULT, |
| 0, |
| FFEINFO_kindENTITY, |
| FFEINFO_whereCONSTANT, |
| FFETARGET_charactersizeNONE)); |
| } |
| |
| /* Start the DO loop. */ |
| |
| start = ffeexpr_convert_expr (start, impdo_token, var, impdo_token, |
| FFEEXPR_contextLET); |
| end = ffeexpr_convert_expr (end, impdo_token, var, impdo_token, |
| FFEEXPR_contextLET); |
| incr = ffeexpr_convert_expr (incr, impdo_token, var, impdo_token, |
| FFEEXPR_contextLET); |
| |
| ffeste_begin_iterdo_ (NULL, &tvar, &tincr, &titervar, var, |
| start, impdo_token, |
| end, impdo_token, |
| incr, impdo_token, |
| "Implied DO loop"); |
| |
| /* Handle the list of items. */ |
| |
| for (list = ffebld_left (impdo); list != NULL; list = ffebld_trail (list)) |
| { |
| item = ffebld_head (list); |
| if (item == NULL) |
| continue; |
| |
| /* Strip parens off items such as in "READ *,(A)". This is really a bug |
| in the user's code, but I've been told lots of code does this. */ |
| while (ffebld_op (item) == FFEBLD_opPAREN) |
| item = ffebld_left (item); |
| |
| if (ffebld_op (item) == FFEBLD_opANY) |
| continue; |
| |
| if (ffebld_op (item) == FFEBLD_opIMPDO) |
| ffeste_io_impdo_ (item, impdo_token); |
| else |
| { |
| ffeste_start_stmt_ (); |
| |
| ffecom_prepare_arg_ptr_to_expr (item); |
| |
| ffecom_prepare_end (); |
| |
| ffeste_io_call_ ((*ffeste_io_driver_) (item), TRUE); |
| |
| ffeste_end_stmt_ (); |
| } |
| } |
| |
| /* Generate end of implied-do construct. */ |
| |
| ffeste_end_iterdo_ (NULL, tvar, tincr, titervar); |
| } |
| |
| /* I/O driver for formatted I/O item (do_fio) |
| |
| Returns a tree for a CALL_EXPR to the do_fio function, which handles |
| a formatted I/O list item, along with the appropriate arguments for |
| the function. It is up to the caller to set the TREE_SIDE_EFFECTS flag |
| for the CALL_EXPR, expand (emit) the expression, emit any assignment |
| of the result to an IOSTAT= variable, and emit any checking of the |
| result for errors. */ |
| |
| static tree |
| ffeste_io_dofio_ (ffebld expr) |
| { |
| tree num_elements; |
| tree variable; |
| tree size; |
| tree arglist; |
| ffeinfoBasictype bt; |
| ffeinfoKindtype kt; |
| bool is_complex; |
| |
| bt = ffeinfo_basictype (ffebld_info (expr)); |
| kt = ffeinfo_kindtype (ffebld_info (expr)); |
| |
| if ((bt == FFEINFO_basictypeANY) |
| || (kt == FFEINFO_kindtypeANY)) |
| return error_mark_node; |
| |
| if (bt == FFEINFO_basictypeCOMPLEX) |
| { |
| is_complex = TRUE; |
| bt = FFEINFO_basictypeREAL; |
| } |
| else |
| is_complex = FALSE; |
| |
| variable = ffecom_arg_ptr_to_expr (expr, &size); |
| |
| if ((variable == error_mark_node) |
| || (size == error_mark_node)) |
| return error_mark_node; |
| |
| if (size == NULL_TREE) /* Already filled in for CHARACTER type. */ |
| { /* "(ftnlen) sizeof(type)" */ |
| size = size_binop (CEIL_DIV_EXPR, |
| TYPE_SIZE_UNIT (ffecom_tree_type[bt][kt]), |
| size_int (TYPE_PRECISION (char_type_node) |
| / BITS_PER_UNIT)); |
| #if 0 /* Assume that while it is possible that char * is wider than |
| ftnlen, no object in Fortran space can get big enough for its |
| size to be wider than ftnlen. I really hope nobody wastes |
| time debugging a case where it can! */ |
| assert (TYPE_PRECISION (ffecom_f2c_ftnlen_type_node) |
| >= TYPE_PRECISION (TREE_TYPE (size))); |
| #endif |
| size = convert (ffecom_f2c_ftnlen_type_node, size); |
| } |
| |
| if (ffeinfo_rank (ffebld_info (expr)) == 0 |
| || TREE_CODE (TREE_TYPE (TREE_TYPE (variable))) != ARRAY_TYPE) |
| num_elements |
| = is_complex ? ffecom_f2c_ftnlen_two_node : ffecom_f2c_ftnlen_one_node; |
| else |
| { |
| num_elements |
| = size_binop (CEIL_DIV_EXPR, |
| TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (variable))), |
| convert (sizetype, size)); |
| num_elements = size_binop (CEIL_DIV_EXPR, num_elements, |
| size_int (TYPE_PRECISION (char_type_node) |
| / BITS_PER_UNIT)); |
| num_elements = convert (ffecom_f2c_ftnlen_type_node, |
| num_elements); |
| } |
| |
| num_elements |
| = ffecom_1 (ADDR_EXPR, ffecom_f2c_ptr_to_ftnlen_type_node, |
| num_elements); |
| |
| variable = convert (string_type_node, variable); |
| |
| arglist = build_tree_list (NULL_TREE, num_elements); |
| TREE_CHAIN (arglist) = build_tree_list (NULL_TREE, variable); |
| TREE_CHAIN (TREE_CHAIN (arglist)) = build_tree_list (NULL_TREE, size); |
| |
| return ffecom_call_gfrt (FFECOM_gfrtDOFIO, arglist, NULL_TREE); |
| } |
| |
| /* I/O driver for list-directed I/O item (do_lio) |
| |
| Returns a tree for a CALL_EXPR to the do_lio function, which handles |
| a list-directed I/O list item, along with the appropriate arguments for |
| the function. It is up to the caller to set the TREE_SIDE_EFFECTS flag |
| for the CALL_EXPR, expand (emit) the expression, emit any assignment |
| of the result to an IOSTAT= variable, and emit any checking of the |
| result for errors. */ |
| |
| static tree |
| ffeste_io_dolio_ (ffebld expr) |
| { |
| tree type_id; |
| tree num_elements; |
| tree variable; |
| tree size; |
| tree arglist; |
| ffeinfoBasictype bt; |
| ffeinfoKindtype kt; |
| int tc; |
| |
| bt = ffeinfo_basictype (ffebld_info (expr)); |
| kt = ffeinfo_kindtype (ffebld_info (expr)); |
| |
| if ((bt == FFEINFO_basictypeANY) |
| || (kt == FFEINFO_kindtypeANY)) |
| return error_mark_node; |
| |
| tc = ffecom_f2c_typecode (bt, kt); |
| assert (tc != -1); |
| type_id = build_int_2 (tc, 0); |
| |
| type_id |
| = ffecom_1 (ADDR_EXPR, ffecom_f2c_ptr_to_ftnint_type_node, |
| convert (ffecom_f2c_ftnint_type_node, |
| type_id)); |
| |
| variable = ffecom_arg_ptr_to_expr (expr, &size); |
| |
| if ((type_id == error_mark_node) |
| || (variable == error_mark_node) |
| || (size == error_mark_node)) |
| return error_mark_node; |
| |
| if (size == NULL_TREE) /* Already filled in for CHARACTER type. */ |
| { /* "(ftnlen) sizeof(type)" */ |
| size = size_binop (CEIL_DIV_EXPR, |
| TYPE_SIZE_UNIT (ffecom_tree_type[bt][kt]), |
| size_int (TYPE_PRECISION (char_type_node) |
| / BITS_PER_UNIT)); |
| #if 0 /* Assume that while it is possible that char * is wider than |
| ftnlen, no object in Fortran space can get big enough for its |
| size to be wider than ftnlen. I really hope nobody wastes |
| time debugging a case where it can! */ |
| assert (TYPE_PRECISION (ffecom_f2c_ftnlen_type_node) |
| >= TYPE_PRECISION (TREE_TYPE (size))); |
| #endif |
| size = convert (ffecom_f2c_ftnlen_type_node, size); |
| } |
| |
| if (ffeinfo_rank (ffebld_info (expr)) == 0 |
| || TREE_CODE (TREE_TYPE (TREE_TYPE (variable))) != ARRAY_TYPE) |
| num_elements = ffecom_integer_one_node; |
| else |
| { |
| num_elements |
| = size_binop (CEIL_DIV_EXPR, |
| TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (variable))), |
| convert (sizetype, size)); |
| num_elements = size_binop (CEIL_DIV_EXPR, num_elements, |
| size_int (TYPE_PRECISION (char_type_node) |
| / BITS_PER_UNIT)); |
| num_elements = convert (ffecom_f2c_ftnlen_type_node, |
| num_elements); |
| } |
| |
| num_elements |
| = ffecom_1 (ADDR_EXPR, ffecom_f2c_ptr_to_ftnlen_type_node, |
| num_elements); |
| |
| variable = convert (string_type_node, variable); |
| |
| arglist = build_tree_list (NULL_TREE, type_id); |
| TREE_CHAIN (arglist) = build_tree_list (NULL_TREE, num_elements); |
| TREE_CHAIN (TREE_CHAIN (arglist)) = build_tree_list (NULL_TREE, variable); |
| TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))) |
| = build_tree_list (NULL_TREE, size); |
| |
| return ffecom_call_gfrt (FFECOM_gfrtDOLIO, arglist, NULL_TREE); |
| } |
| |
| /* I/O driver for unformatted I/O item (do_uio) |
| |
| Returns a tree for a CALL_EXPR to the do_uio function, which handles |
| an unformatted I/O list item, along with the appropriate arguments for |
| the function. It is up to the caller to set the TREE_SIDE_EFFECTS flag |
| for the CALL_EXPR, expand (emit) the expression, emit any assignment |
| of the result to an IOSTAT= variable, and emit any checking of the |
| result for errors. */ |
| |
| static tree |
| ffeste_io_douio_ (ffebld expr) |
| { |
| tree num_elements; |
| tree variable; |
| tree size; |
| tree arglist; |
| ffeinfoBasictype bt; |
| ffeinfoKindtype kt; |
| bool is_complex; |
| |
| bt = ffeinfo_basictype (ffebld_info (expr)); |
| kt = ffeinfo_kindtype (ffebld_info (expr)); |
| |
| if ((bt == FFEINFO_basictypeANY) |
| || (kt == FFEINFO_kindtypeANY)) |
| return error_mark_node; |
| |
| if (bt == FFEINFO_basictypeCOMPLEX) |
| { |
| is_complex = TRUE; |
| bt = FFEINFO_basictypeREAL; |
| } |
| else |
| is_complex = FALSE; |
| |
| variable = ffecom_arg_ptr_to_expr (expr, &size); |
| |
| if ((variable == error_mark_node) |
| || (size == error_mark_node)) |
| return error_mark_node; |
| |
| if (size == NULL_TREE) /* Already filled in for CHARACTER type. */ |
| { /* "(ftnlen) sizeof(type)" */ |
| size = size_binop (CEIL_DIV_EXPR, |
| TYPE_SIZE_UNIT (ffecom_tree_type[bt][kt]), |
| size_int (TYPE_PRECISION (char_type_node) |
| / BITS_PER_UNIT)); |
| #if 0 /* Assume that while it is possible that char * is wider than |
| ftnlen, no object in Fortran space can get big enough for its |
| size to be wider than ftnlen. I really hope nobody wastes |
| time debugging a case where it can! */ |
| assert (TYPE_PRECISION (ffecom_f2c_ftnlen_type_node) |
| >= TYPE_PRECISION (TREE_TYPE (size))); |
| #endif |
| size = convert (ffecom_f2c_ftnlen_type_node, size); |
| } |
| |
| if (ffeinfo_rank (ffebld_info (expr)) == 0 |
| || TREE_CODE (TREE_TYPE (TREE_TYPE (variable))) != ARRAY_TYPE) |
| num_elements |
| = is_complex ? ffecom_f2c_ftnlen_two_node : ffecom_f2c_ftnlen_one_node; |
| else |
| { |
| num_elements |
| = size_binop (CEIL_DIV_EXPR, |
| TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (variable))), |
| convert (sizetype, size)); |
| num_elements = size_binop (CEIL_DIV_EXPR, num_elements, |
| size_int (TYPE_PRECISION (char_type_node) |
| / BITS_PER_UNIT)); |
| num_elements = convert (ffecom_f2c_ftnlen_type_node, |
| num_elements); |
| } |
| |
| num_elements |
| = ffecom_1 (ADDR_EXPR, ffecom_f2c_ptr_to_ftnlen_type_node, |
| num_elements); |
| |
| variable = convert (string_type_node, variable); |
| |
| arglist = build_tree_list (NULL_TREE, num_elements); |
| TREE_CHAIN (arglist) = build_tree_list (NULL_TREE, variable); |
| TREE_CHAIN (TREE_CHAIN (arglist)) = build_tree_list (NULL_TREE, size); |
| |
| return ffecom_call_gfrt (FFECOM_gfrtDOUIO, arglist, NULL_TREE); |
| } |
| |
| /* Make arglist with ptr to BACKSPACE/ENDFILE/REWIND control list. |
| |
| Returns a tree suitable as an argument list containing a pointer to |
| a BACKSPACE/ENDFILE/REWIND control list. First, generates that control |
| list, if necessary, along with any static and run-time initializations |
| that are needed as specified by the arguments to this function. |
| |
| Must ensure that all expressions are prepared before being evaluated, |
| for any whose evaluation might result in the generation of temporaries. |
| |
| Note that this means this function causes a transition, within the |
| current block being code-generated via the back end, from the |
| declaration of variables (temporaries) to the expanding of expressions, |
| statements, etc. */ |
| |
| static GTY(()) tree f2c_alist_struct; |
| static tree |
| ffeste_io_ialist_ (bool have_err, |
| ffestvUnit unit, |
| ffebld unit_expr, |
| int unit_dflt) |
| { |
| tree t; |
| tree ttype; |
| tree field; |
| tree inits, initn; |
| bool constantp = TRUE; |
| static tree errfield, unitfield; |
| tree errinit, unitinit; |
| tree unitexp; |
| static int mynumber = 0; |
| |
| if (f2c_alist_struct == NULL_TREE) |
| { |
| tree ref; |
| |
| ref = make_node (RECORD_TYPE); |
| |
| errfield = ffecom_decl_field (ref, NULL_TREE, "err", |
| ffecom_f2c_flag_type_node); |
| unitfield = ffecom_decl_field (ref, errfield, "unit", |
| ffecom_f2c_ftnint_type_node); |
| |
| TYPE_FIELDS (ref) = errfield; |
| layout_type (ref); |
| |
| f2c_alist_struct = ref; |
| } |
| |
| /* Try to do as much compile-time initialization of the structure |
| as possible, to save run time. */ |
| |
| ffeste_f2c_init_flag_ (have_err, errinit); |
| |
| switch (unit) |
| { |
| case FFESTV_unitNONE: |
| case FFESTV_unitASTERISK: |
| unitinit = build_int_2 (unit_dflt, 0); |
| unitexp = unitinit; |
| break; |
| |
| case FFESTV_unitINTEXPR: |
| unitexp = ffecom_const_expr (unit_expr); |
| if (unitexp) |
| unitinit = unitexp; |
| else |
| { |
| unitinit = ffecom_integer_zero_node; |
| constantp = FALSE; |
| } |
| break; |
| |
| default: |
| assert ("bad unit spec" == NULL); |
| unitinit = ffecom_integer_zero_node; |
| unitexp = unitinit; |
| break; |
| } |
| |
| inits = build_tree_list ((field = TYPE_FIELDS (f2c_alist_struct)), errinit); |
| initn = inits; |
| ffeste_f2c_init_next_ (unitinit); |
| |
| inits = build_constructor (f2c_alist_struct, inits); |
| TREE_CONSTANT (inits) = constantp ? 1 : 0; |
| TREE_STATIC (inits) = 1; |
| |
| t = build_decl (VAR_DECL, |
| ffecom_get_invented_identifier ("__g77_alist_%d", |
| mynumber++), |
| f2c_alist_struct); |
| TREE_STATIC (t) = 1; |
| t = ffecom_start_decl (t, 1); |
| ffecom_finish_decl (t, inits, 0); |
| |
| /* Prepare run-time expressions. */ |
| |
| if (! unitexp) |
| ffecom_prepare_expr (unit_expr); |
| |
| ffecom_prepare_end (); |
| |
| /* Now evaluate run-time expressions as needed. */ |
| |
| if (! unitexp) |
| { |
| unitexp = ffecom_expr (unit_expr); |
| ffeste_f2c_compile_ (unitfield, unitexp); |
| } |
| |
| ttype = build_pointer_type (TREE_TYPE (t)); |
| t = ffecom_1 (ADDR_EXPR, ttype, t); |
| |
| t = build_tree_list (NULL_TREE, t); |
| |
| return t; |
| } |
| |
| /* Make arglist with ptr to external-I/O control list. |
| |
| Returns a tree suitable as an argument list containing a pointer to |
| an external-I/O control list. First, generates that control |
| list, if necessary, along with any static and run-time initializations |
| that are needed as specified by the arguments to this function. |
| |
| Must ensure that all expressions are prepared before being evaluated, |
| for any whose evaluation might result in the generation of temporaries. |
| |
| Note that this means this function causes a transition, within the |
| current block being code-generated via the back end, from the |
| declaration of variables (temporaries) to the expanding of expressions, |
| statements, etc. */ |
| |
| static GTY(()) tree f2c_cilist_struct; |
| static tree |
| ffeste_io_cilist_ (bool have_err, |
| ffestvUnit unit, |
| ffebld unit_expr, |
| int unit_dflt, |
| bool have_end, |
| ffestvFormat format, |
| ffestpFile *format_spec, |
| bool rec, |
| ffebld rec_expr) |
| { |
| tree t; |
| tree ttype; |
| tree field; |
| tree inits, initn; |
| bool constantp = TRUE; |
| static tree errfield, unitfield, endfield, formatfield, recfield; |
| tree errinit, unitinit, endinit, formatinit, recinit; |
| tree unitexp, formatexp, recexp; |
| static int mynumber = 0; |
| |
| if (f2c_cilist_struct == NULL_TREE) |
| { |
| tree ref; |
| |
| ref = make_node (RECORD_TYPE); |
| |
| errfield = ffecom_decl_field (ref, NULL_TREE, "err", |
| ffecom_f2c_flag_type_node); |
| unitfield = ffecom_decl_field (ref, errfield, "unit", |
| ffecom_f2c_ftnint_type_node); |
| endfield = ffecom_decl_field (ref, unitfield, "end", |
| ffecom_f2c_flag_type_node); |
| formatfield = ffecom_decl_field (ref, endfield, "format", |
| string_type_node); |
| recfield = ffecom_decl_field (ref, formatfield, "rec", |
| ffecom_f2c_ftnint_type_node); |
| |
| TYPE_FIELDS (ref) = errfield; |
| layout_type (ref); |
| |
| f2c_cilist_struct = ref; |
| } |
| |
| /* Try to do as much compile-time initialization of the structure |
| as possible, to save run time. */ |
| |
| ffeste_f2c_init_flag_ (have_err, errinit); |
| |
| switch (unit) |
| { |
| case FFESTV_unitNONE: |
| case FFESTV_unitASTERISK: |
| unitinit = build_int_2 (unit_dflt, 0); |
| unitexp = unitinit; |
| break; |
| |
| case FFESTV_unitINTEXPR: |
| unitexp = ffecom_const_expr (unit_expr); |
| if (unitexp) |
| unitinit = unitexp; |
| else |
| { |
| unitinit = ffecom_integer_zero_node; |
| constantp = FALSE; |
| } |
| break; |
| |
| default: |
| assert ("bad unit spec" == NULL); |
| unitinit = ffecom_integer_zero_node; |
| unitexp = unitinit; |
| break; |
| } |
| |
| switch (format) |
| { |
| case FFESTV_formatNONE: |
| formatinit = null_pointer_node; |
| formatexp = formatinit; |
| break; |
| |
| case FFESTV_formatLABEL: |
| formatexp = error_mark_node; |
| formatinit = ffecom_lookup_label (format_spec->u.label); |
| if ((formatinit == NULL_TREE) |
| || (TREE_CODE (formatinit) == ERROR_MARK)) |
| break; |
| formatinit = ffecom_1 (ADDR_EXPR, |
| build_pointer_type (void_type_node), |
| formatinit); |
| TREE_CONSTANT (formatinit) = 1; |
| break; |
| |
| case FFESTV_formatCHAREXPR: |
| formatexp = ffecom_arg_ptr_to_const_expr (format_spec->u.expr, NULL); |
| if (formatexp) |
| formatinit = formatexp; |
| else |
| { |
| formatinit = null_pointer_node; |
| constantp = FALSE; |
| } |
| break; |
| |
| case FFESTV_formatASTERISK: |
| formatinit = null_pointer_node; |
| formatexp = formatinit; |
| break; |
| |
| case FFESTV_formatINTEXPR: |
| formatinit = null_pointer_node; |
| formatexp = ffecom_expr_assign (format_spec->u.expr); |
| if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (formatexp))) |
| < GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (null_pointer_node)))) |
| error ("ASSIGNed FORMAT specifier is too small"); |
| formatexp = convert (string_type_node, formatexp); |
| break; |
| |
| case FFESTV_formatNAMELIST: |
| formatinit = ffecom_expr (format_spec->u.expr); |
| formatexp = formatinit; |
| break; |
| |
| default: |
| assert ("bad format spec" == NULL); |
| formatinit = integer_zero_node; |
| formatexp = formatinit; |
| break; |
| } |
| |
| ffeste_f2c_init_flag_ (have_end, endinit); |
| |
| if (rec) |
| recexp = ffecom_const_expr (rec_expr); |
| else |
| recexp = ffecom_integer_zero_node; |
| if (recexp) |
| recinit = recexp; |
| else |
| { |
| recinit = ffecom_integer_zero_node; |
| constantp = FALSE; |
| } |
| |
| inits = build_tree_list ((field = TYPE_FIELDS (f2c_cilist_struct)), errinit); |
| initn = inits; |
| ffeste_f2c_init_next_ (unitinit); |
| ffeste_f2c_init_next_ (endinit); |
| ffeste_f2c_init_next_ (formatinit); |
| ffeste_f2c_init_next_ (recinit); |
| |
| inits = build_constructor (f2c_cilist_struct, inits); |
| TREE_CONSTANT (inits) = constantp ? 1 : 0; |
| TREE_STATIC (inits) = 1; |
| |
| t = build_decl (VAR_DECL, |
| ffecom_get_invented_identifier ("__g77_cilist_%d", |
| mynumber++), |
| f2c_cilist_struct); |
| TREE_STATIC (t) = 1; |
| t = ffecom_start_decl (t, 1); |
| ffecom_finish_decl (t, inits, 0); |
| |
| /* Prepare run-time expressions. */ |
| |
| if (! unitexp) |
| ffecom_prepare_expr (unit_expr); |
| |
| if (! formatexp) |
| ffecom_prepare_arg_ptr_to_expr (format_spec->u.expr); |
| |
| if (! recexp) |
| ffecom_prepare_expr (rec_expr); |
| |
| ffecom_prepare_end (); |
| |
| /* Now evaluate run-time expressions as needed. */ |
| |
| if (! unitexp) |
| { |
| unitexp = ffecom_expr (unit_expr); |
| ffeste_f2c_compile_ (unitfield, unitexp); |
| } |
| |
| if (! formatexp) |
| { |
| formatexp = ffecom_arg_ptr_to_expr (format_spec->u.expr, NULL); |
| ffeste_f2c_compile_ (formatfield, formatexp); |
| } |
| else if (format == FFESTV_formatINTEXPR) |
| ffeste_f2c_compile_ (formatfield, formatexp); |
| |
| if (! recexp) |
| { |
| recexp = ffecom_expr (rec_expr); |
| ffeste_f2c_compile_ (recfield, recexp); |
| } |
| |
| ttype = build_pointer_type (TREE_TYPE (t)); |
| t = ffecom_1 (ADDR_EXPR, ttype, t); |
| |
| t = build_tree_list (NULL_TREE, t); |
| |
| return t; |
| } |
| |
| /* Make arglist with ptr to CLOSE control list. |
| |
| Returns a tree suitable as an argument list containing a pointer to |
| a CLOSE-statement control list. First, generates that control |
| list, if necessary, along with any static and run-time initializations |
| that are needed as specified by the arguments to this function. |
| |
| Must ensure that all expressions are prepared before being evaluated, |
| for any whose evaluation might result in the generation of temporaries. |
| |
| Note that this means this function causes a transition, within the |
| current block being code-generated via the back end, from the |
| declaration of variables (temporaries) to the expanding of expressions, |
| statements, etc. */ |
| |
| static GTY(()) tree f2c_close_struct; |
| static tree |
| ffeste_io_cllist_ (bool have_err, |
| ffebld unit_expr, |
| ffestpFile *stat_spec) |
| { |
| tree t; |
| tree ttype; |
| tree field; |
| tree inits, initn; |
| tree ignore; /* Ignore length info for certain fields. */ |
| bool constantp = TRUE; |
| static tree errfield, unitfield, statfield; |
| tree errinit, unitinit, statinit; |
| tree unitexp, statexp; |
| static int mynumber = 0; |
| |
| if (f2c_close_struct == NULL_TREE) |
| { |
| tree ref; |
| |
| ref = make_node (RECORD_TYPE); |
| |
| errfield = ffecom_decl_field (ref, NULL_TREE, "err", |
| ffecom_f2c_flag_type_node); |
| unitfield = ffecom_decl_field (ref, errfield, "unit", |
| ffecom_f2c_ftnint_type_node); |
| statfield = ffecom_decl_field (ref, unitfield, "stat", |
| string_type_node); |
| |
| TYPE_FIELDS (ref) = errfield; |
| layout_type (ref); |
| |
| f2c_close_struct = ref; |
| } |
| |
| /* Try to do as much compile-time initialization of the structure |
| as possible, to save run time. */ |
| |
| ffeste_f2c_init_flag_ (have_err, errinit); |
| |
| unitexp = ffecom_const_expr (unit_expr); |
| if (unitexp) |
| unitinit = unitexp; |
| else |
| { |
| unitinit = ffecom_integer_zero_node; |
| constantp = FALSE; |
| } |
| |
| ffeste_f2c_init_charnolen_ (statexp, statinit, stat_spec); |
| |
| inits = build_tree_list ((field = TYPE_FIELDS (f2c_close_struct)), errinit); |
| initn = inits; |
| ffeste_f2c_init_next_ (unitinit); |
| ffeste_f2c_init_next_ (statinit); |
| |
| inits = build_constructor (f2c_close_struct, inits); |
| TREE_CONSTANT (inits) = constantp ? 1 : 0; |
| TREE_STATIC (inits) = 1; |
| |
| t = build_decl (VAR_DECL, |
| ffecom_get_invented_identifier ("__g77_cllist_%d", |
| mynumber++), |
| f2c_close_struct); |
| TREE_STATIC (t) = 1; |
| t = ffecom_start_decl (t, 1); |
| ffecom_finish_decl (t, inits, 0); |
| |
| /* Prepare run-time expressions. */ |
| |
| if (! unitexp) |
| ffecom_prepare_expr (unit_expr); |
| |
| if (! statexp) |
| ffecom_prepare_arg_ptr_to_expr (stat_spec->u.expr); |
| |
| ffecom_prepare_end (); |
| |
| /* Now evaluate run-time expressions as needed. */ |
| |
| if (! unitexp) |
| { |
| unitexp = ffecom_expr (unit_expr); |
| ffeste_f2c_compile_ (unitfield, unitexp); |
| } |
| |
| ffeste_f2c_compile_charnolen_ (statfield, stat_spec, statexp); |
| |
| ttype = build_pointer_type (TREE_TYPE (t)); |
| t = ffecom_1 (ADDR_EXPR, ttype, t); |
| |
| t = build_tree_list (NULL_TREE, t); |
| |
| return t; |
| } |
| |
| /* Make arglist with ptr to internal-I/O control list. |
| |
| Returns a tree suitable as an argument list containing a pointer to |
| an internal-I/O control list. First, generates that control |
| list, if necessary, along with any static and run-time initializations |
| that are needed as specified by the arguments to this function. |
| |
| Must ensure that all expressions are prepared before being evaluated, |
| for any whose evaluation might result in the generation of temporaries. |
| |
| Note that this means this function causes a transition, within the |
| current block being code-generated via the back end, from the |
| declaration of variables (temporaries) to the expanding of expressions, |
| statements, etc. */ |
| |
| static GTY(()) tree f2c_icilist_struct; |
| static tree |
| ffeste_io_icilist_ (bool have_err, |
| ffebld unit_expr, |
| bool have_end, |
| ffestvFormat format, |
| ffestpFile *format_spec) |
| { |
| tree t; |
| tree ttype; |
| tree field; |
| tree inits, initn; |
| bool constantp = TRUE; |
| static tree errfield, unitfield, endfield, formatfield, unitlenfield, |
| unitnumfield; |
| tree errinit, unitinit, endinit, formatinit, unitleninit, unitnuminit; |
| tree unitexp, formatexp, unitlenexp, unitnumexp; |
| static int mynumber = 0; |
| |
| if (f2c_icilist_struct == NULL_TREE) |
| { |
| tree ref; |
| |
| ref = make_node (RECORD_TYPE); |
| |
| errfield = ffecom_decl_field (ref, NULL_TREE, "err", |
| ffecom_f2c_flag_type_node); |
| unitfield = ffecom_decl_field (ref, errfield, "unit", |
| string_type_node); |
| endfield = ffecom_decl_field (ref, unitfield, "end", |
| ffecom_f2c_flag_type_node); |
| formatfield = ffecom_decl_field (ref, endfield, "format", |
| string_type_node); |
| unitlenfield = ffecom_decl_field (ref, formatfield, "unitlen", |
| ffecom_f2c_ftnint_type_node); |
| unitnumfield = ffecom_decl_field (ref, unitlenfield, "unitnum", |
| ffecom_f2c_ftnint_type_node); |
| |
| TYPE_FIELDS (ref) = errfield; |
| layout_type (ref); |
| |
| f2c_icilist_struct = ref; |
| } |
| |
| /* Try to do as much compile-time initialization of the structure |
| as possible, to save run time. */ |
| |
| ffeste_f2c_init_flag_ (have_err, errinit); |
| |
| unitexp = ffecom_arg_ptr_to_const_expr (unit_expr, &unitlenexp); |
| if (unitexp) |
| unitinit = unitexp; |
| else |
| { |
| unitinit = null_pointer_node; |
| constantp = FALSE; |
| } |
| if (unitlenexp) |
| unitleninit = unitlenexp; |
| else |
| { |
| unitleninit = ffecom_integer_zero_node; |
| constantp = FALSE; |
| } |
| |
| /* Now see if we can fully initialize the number of elements, or |
| if we have to compute that at run time. */ |
| if (ffeinfo_rank (ffebld_info (unit_expr)) == 0 |
| || (unitexp |
| && TREE_CODE (TREE_TYPE (TREE_TYPE (unitexp))) != ARRAY_TYPE)) |
| { |
| /* Not an array, so just one element. */ |
| unitnuminit = ffecom_integer_one_node; |
| unitnumexp = unitnuminit; |
| } |
| else if (unitexp && unitlenexp) |
| { |
| /* An array, but all the info is constant, so compute now. */ |
| unitnuminit |
| = size_binop (CEIL_DIV_EXPR, |
| TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (unitexp))), |
| convert (sizetype, unitlenexp)); |
| unitnuminit = size_binop (CEIL_DIV_EXPR, unitnuminit, |
| size_int (TYPE_PRECISION (char_type_node) |
| / BITS_PER_UNIT)); |
| unitnumexp = unitnuminit; |
| } |
| else |
| { |
| /* Put off computing until run time. */ |
| unitnuminit = ffecom_integer_zero_node; |
| unitnumexp = NULL_TREE; |
| constantp = FALSE; |
| } |
| |
| switch (format) |
| { |
| case FFESTV_formatNONE: |
| formatinit = null_pointer_node; |
| formatexp = formatinit; |
| break; |
| |
| case FFESTV_formatLABEL: |
| formatexp = error_mark_node; |
| formatinit = ffecom_lookup_label (format_spec->u.label); |
| if ((formatinit == NULL_TREE) |
| || (TREE_CODE (formatinit) == ERROR_MARK)) |
| break; |
| formatinit = ffecom_1 (ADDR_EXPR, |
| build_pointer_type (void_type_node), |
| formatinit); |
| TREE_CONSTANT (formatinit) = 1; |
| break; |
| |
| case FFESTV_formatCHAREXPR: |
| ffeste_f2c_init_format_ (formatexp, formatinit, format_spec); |
| break; |
| |
| case FFESTV_formatASTERISK: |
| formatinit = null_pointer_node; |
| formatexp = formatinit; |
| break; |
| |
| case FFESTV_formatINTEXPR: |
| formatinit = null_pointer_node; |
| formatexp = ffecom_expr_assign (format_spec->u.expr); |
| if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (formatexp))) |
| < GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (null_pointer_node)))) |
| error ("ASSIGNed FORMAT specifier is too small"); |
| formatexp = convert (string_type_node, formatexp); |
| break; |
| |
| default: |
| assert ("bad format spec" == NULL); |
| formatinit = ffecom_integer_zero_node; |
| formatexp = formatinit; |
| break; |
| } |
| |
| ffeste_f2c_init_flag_ (have_end, endinit); |
| |
| inits = build_tree_list ((field = TYPE_FIELDS (f2c_icilist_struct)), |
| errinit); |
| initn = inits; |
| ffeste_f2c_init_next_ (unitinit); |
| ffeste_f2c_init_next_ (endinit); |
| ffeste_f2c_init_next_ (formatinit); |
| ffeste_f2c_init_next_ (unitleninit); |
| ffeste_f2c_init_next_ (unitnuminit); |
| |
| inits = build_constructor (f2c_icilist_struct, inits); |
| TREE_CONSTANT (inits) = constantp ? 1 : 0; |
| TREE_STATIC (inits) = 1; |
| |
| t = build_decl (VAR_DECL, |
| ffecom_get_invented_identifier ("__g77_icilist_%d", |
| mynumber++), |
| f2c_icilist_struct); |
| TREE_STATIC (t) = 1; |
| t = ffecom_start_decl (t, 1); |
| ffecom_finish_decl (t, inits, 0); |
| |
| /* Prepare run-time expressions. */ |
| |
| if (! unitexp) |
| ffecom_prepare_arg_ptr_to_expr (unit_expr); |
| |
| ffeste_f2c_prepare_format_ (format_spec, formatexp); |
| |
| ffecom_prepare_end (); |
| |
| /* Now evaluate run-time expressions as needed. */ |
| |
| if (! unitexp || ! unitlenexp) |
| { |
| int need_unitexp = (! unitexp); |
| int need_unitlenexp = (! unitlenexp); |
| |
| unitexp = ffecom_arg_ptr_to_expr (unit_expr, &unitlenexp); |
| if (need_unitexp) |
| ffeste_f2c_compile_ (unitfield, unitexp); |
| if (need_unitlenexp) |
| ffeste_f2c_compile_ (unitlenfield, unitlenexp); |
| } |
| |
| if (! unitnumexp |
| && unitexp != error_mark_node |
| && unitlenexp != error_mark_node) |
| { |
| unitnumexp |
| = size_binop (CEIL_DIV_EXPR, |
| TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (unitexp))), |
| convert (sizetype, unitlenexp)); |
| unitnumexp = size_binop (CEIL_DIV_EXPR, unitnumexp, |
| size_int (TYPE_PRECISION (char_type_node) |
| / BITS_PER_UNIT)); |
| ffeste_f2c_compile_ (unitnumfield, unitnumexp); |
| } |
| |
| if (format == FFESTV_formatINTEXPR) |
| ffeste_f2c_compile_ (formatfield, formatexp); |
| else |
| ffeste_f2c_compile_format_ (formatfield, format_spec, formatexp); |
| |
| ttype = build_pointer_type (TREE_TYPE (t)); |
| t = ffecom_1 (ADDR_EXPR, ttype, t); |
| |
| t = build_tree_list (NULL_TREE, t); |
| |
| return t; |
| } |
| |
| /* Make arglist with ptr to INQUIRE control list |
| |
| Returns a tree suitable as an argument list containing a pointer to |
| an INQUIRE-statement control list. First, generates that control |
| list, if necessary, along with any static and run-time initializations |
| that are needed as specified by the arguments to this function. |
| |
| Must ensure that all expressions are prepared before being evaluated, |
| for any whose evaluation might result in the generation of temporaries. |
| |
| Note that this means this function causes a transition, within the |
| current block being code-generated via the back end, from the |
| declaration of variables (temporaries) to the expanding of expressions, |
| statements, etc. */ |
| |
| static GTY(()) tree f2c_inquire_struct; |
| static tree |
| ffeste_io_inlist_ (bool have_err, |
| ffestpFile *unit_spec, |
| ffestpFile *file_spec, |
| ffestpFile *exist_spec, |
| ffestpFile *open_spec, |
| ffestpFile *number_spec, |
| ffestpFile *named_spec, |
| ffestpFile *name_spec, |
| ffestpFile *access_spec, |
| ffestpFile *sequential_spec, |
| ffestpFile *direct_spec, |
| ffestpFile *form_spec, |
| ffestpFile *formatted_spec, |
| ffestpFile *unformatted_spec, |
| ffestpFile *recl_spec, |
| ffestpFile *nextrec_spec, |
| ffestpFile *blank_spec) |
| { |
| tree t; |
| tree ttype; |
| tree field; |
| tree inits, initn; |
| bool constantp = TRUE; |
| static tree errfield, unitfield, filefield, filelenfield, existfield, |
| openfield, numberfield, namedfield, namefield, namelenfield, accessfield, |
| accesslenfield, sequentialfield, sequentiallenfield, directfield, directlenfield, |
| formfield, formlenfield, formattedfield, formattedlenfield, unformattedfield, |
| unformattedlenfield, reclfield, nextrecfield, blankfield, blanklenfield; |
| tree errinit, unitinit, fileinit, fileleninit, existinit, openinit, numberinit, |
| namedinit, nameinit, nameleninit, accessinit, accessleninit, sequentialinit, |
| sequentialleninit, directinit, directleninit, forminit, formleninit, |
| formattedinit, formattedleninit, unformattedinit, unformattedleninit, |
| reclinit, nextrecinit, blankinit, blankleninit; |
| tree |
| unitexp, fileexp, filelenexp, existexp, openexp, numberexp, namedexp, |
| nameexp, namelenexp, accessexp, accesslenexp, sequentialexp, sequentiallenexp, |
| directexp, directlenexp, formexp, formlenexp, formattedexp, formattedlenexp, |
| unformattedexp, unformattedlenexp, reclexp, nextrecexp, blankexp, blanklenexp; |
| static int mynumber = 0; |
| |
| if (f2c_inquire_struct == NULL_TREE) |
| { |
| tree ref; |
| |
| ref = make_node (RECORD_TYPE); |
| |
| errfield = ffecom_decl_field (ref, NULL_TREE, "err", |
| ffecom_f2c_flag_type_node); |
| unitfield = ffecom_decl_field (ref, errfield, "unit", |
| ffecom_f2c_ftnint_type_node); |
| filefield = ffecom_decl_field (ref, unitfield, "file", |
| string_type_node); |
| filelenfield = ffecom_decl_field (ref, filefield, "filelen", |
| ffecom_f2c_ftnlen_type_node); |
| existfield = ffecom_decl_field (ref, filelenfield, "exist", |
| ffecom_f2c_ptr_to_ftnint_type_node); |
| openfield = ffecom_decl_field (ref, existfield, "open", |
| ffecom_f2c_ptr_to_ftnint_type_node); |
| numberfield = ffecom_decl_field (ref, openfield, "number", |
| ffecom_f2c_ptr_to_ftnint_type_node); |
| namedfield = ffecom_decl_field (ref, numberfield, "named", |
| ffecom_f2c_ptr_to_ftnint_type_node); |
| namefield = ffecom_decl_field (ref, namedfield, "name", |
| string_type_node); |
| namelenfield = ffecom_decl_field (ref, namefield, "namelen", |
| ffecom_f2c_ftnlen_type_node); |
| accessfield = ffecom_decl_field (ref, namelenfield, "access", |
| string_type_node); |
| accesslenfield = ffecom_decl_field (ref, accessfield, "accesslen", |
| ffecom_f2c_ftnlen_type_node); |
| sequentialfield = ffecom_decl_field (ref, accesslenfield, "sequential", |
| string_type_node); |
| sequentiallenfield = ffecom_decl_field (ref, sequentialfield, |
| "sequentiallen", |
| ffecom_f2c_ftnlen_type_node); |
| directfield = ffecom_decl_field (ref, sequentiallenfield, "direct", |
| string_type_node); |
| directlenfield = ffecom_decl_field (ref, directfield, "directlen", |
| ffecom_f2c_ftnlen_type_node); |
| formfield = ffecom_decl_field (ref, directlenfield, "form", |
| string_type_node); |
| formlenfield = ffecom_decl_field (ref, formfield, "formlen", |
| ffecom_f2c_ftnlen_type_node); |
| formattedfield = ffecom_decl_field (ref, formlenfield, "formatted", |
| string_type_node); |
| formattedlenfield = ffecom_decl_field (ref, formattedfield, |
| "formattedlen", |
| ffecom_f2c_ftnlen_type_node); |
| unformattedfield = ffecom_decl_field (ref, formattedlenfield, |
| "unformatted", |
| string_type_node); |
| unformattedlenfield = ffecom_decl_field (ref, unformattedfield, |
| "unformattedlen", |
| ffecom_f2c_ftnlen_type_node); |
| reclfield = ffecom_decl_field (ref, unformattedlenfield, "recl", |
| ffecom_f2c_ptr_to_ftnint_type_node); |
| nextrecfield = ffecom_decl_field (ref, reclfield, "nextrec", |
| ffecom_f2c_ptr_to_ftnint_type_node); |
| blankfield = ffecom_decl_field (ref, nextrecfield, "blank", |
| string_type_node); |
| blanklenfield = ffecom_decl_field (ref, blankfield, "blanklen", |
| ffecom_f2c_ftnlen_type_node); |
| |
| TYPE_FIELDS (ref) = errfield; |
| layout_type (ref); |
| |
| f2c_inquire_struct = ref; |
| } |
| |
| /* Try to do as much compile-time initialization of the structure |
| as possible, to save run time. */ |
| |
| ffeste_f2c_init_flag_ (have_err, errinit); |
| ffeste_f2c_init_int_ (unitexp, unitinit, unit_spec); |
| ffeste_f2c_init_char_ (fileexp, fileinit, filelenexp, fileleninit, |
| file_spec); |
| ffeste_f2c_init_ptrtoint_ (existexp, existinit, exist_spec); |
| ffeste_f2c_init_ptrtoint_ (openexp, openinit, open_spec); |
| ffeste_f2c_init_ptrtoint_ (numberexp, numberinit, number_spec); |
| ffeste_f2c_init_ptrtoint_ (namedexp, namedinit, named_spec); |
| ffeste_f2c_init_char_ (nameexp, nameinit, namelenexp, nameleninit, |
| name_spec); |
| ffeste_f2c_init_char_ (accessexp, accessinit, accesslenexp, |
| accessleninit, access_spec); |
| ffeste_f2c_init_char_ (sequentialexp, sequentialinit, sequentiallenexp, |
| sequentialleninit, sequential_spec); |
| ffeste_f2c_init_char_ (directexp, directinit, directlenexp, |
| directleninit, direct_spec); |
| ffeste_f2c_init_char_ (formexp, forminit, formlenexp, formleninit, |
| form_spec); |
| ffeste_f2c_init_char_ (formattedexp, formattedinit, |
| formattedlenexp, formattedleninit, formatted_spec); |
| ffeste_f2c_init_char_ (unformattedexp, unformattedinit, unformattedlenexp, |
| unformattedleninit, unformatted_spec); |
| ffeste_f2c_init_ptrtoint_ (reclexp, reclinit, recl_spec); |
| ffeste_f2c_init_ptrtoint_ (nextrecexp, nextrecinit, nextrec_spec); |
| ffeste_f2c_init_char_ (blankexp, blankinit, blanklenexp, |
| blankleninit, blank_spec); |
| |
| inits = build_tree_list ((field = TYPE_FIELDS (f2c_inquire_struct)), |
| errinit); |
| initn = inits; |
| ffeste_f2c_init_next_ (unitinit); |
| ffeste_f2c_init_next_ (fileinit); |
| ffeste_f2c_init_next_ (fileleninit); |
| ffeste_f2c_init_next_ (existinit); |
| ffeste_f2c_init_next_ (openinit); |
| ffeste_f2c_init_next_ (numberinit); |
| ffeste_f2c_init_next_ (namedinit); |
| ffeste_f2c_init_next_ (nameinit); |
| ffeste_f2c_init_next_ (nameleninit); |
| ffeste_f2c_init_next_ (accessinit); |
| ffeste_f2c_init_next_ (accessleninit); |
| ffeste_f2c_init_next_ (sequentialinit); |
| ffeste_f2c_init_next_ (sequentialleninit); |
| ffeste_f2c_init_next_ (directinit); |
| ffeste_f2c_init_next_ (directleninit); |
| ffeste_f2c_init_next_ (forminit); |
| ffeste_f2c_init_next_ (formleninit); |
| ffeste_f2c_init_next_ (formattedinit); |
| ffeste_f2c_init_next_ (formattedleninit); |
| ffeste_f2c_init_next_ (unformattedinit); |
| ffeste_f2c_init_next_ (unformattedleninit); |
| ffeste_f2c_init_next_ (reclinit); |
| ffeste_f2c_init_next_ (nextrecinit); |
| ffeste_f2c_init_next_ (blankinit); |
| ffeste_f2c_init_next_ (blankleninit); |
| |
| inits = build_constructor (f2c_inquire_struct, inits); |
| TREE_CONSTANT (inits) = constantp ? 1 : 0; |
| TREE_STATIC (inits) = 1; |
| |
| t = build_decl (VAR_DECL, |
| ffecom_get_invented_identifier ("__g77_inlist_%d", |
| mynumber++), |
| f2c_inquire_struct); |
| TREE_STATIC (t) = 1; |
| t = ffecom_start_decl (t, 1); |
| ffecom_finish_decl (t, inits, 0); |
| |
| /* Prepare run-time expressions. */ |
| |
| ffeste_f2c_prepare_int_ (unit_spec, unitexp); |
| ffeste_f2c_prepare_char_ (file_spec, fileexp); |
| ffeste_f2c_prepare_ptrtoint_ (exist_spec, existexp); |
| ffeste_f2c_prepare_ptrtoint_ (open_spec, openexp); |
| ffeste_f2c_prepare_ptrtoint_ (number_spec, numberexp); |
| ffeste_f2c_prepare_ptrtoint_ (named_spec, namedexp); |
| ffeste_f2c_prepare_char_ (name_spec, nameexp); |
| ffeste_f2c_prepare_char_ (access_spec, accessexp); |
| ffeste_f2c_prepare_char_ (sequential_spec, sequentialexp); |
| ffeste_f2c_prepare_char_ (direct_spec, directexp); |
| ffeste_f2c_prepare_char_ (form_spec, formexp); |
| ffeste_f2c_prepare_char_ (formatted_spec, formattedexp); |
| ffeste_f2c_prepare_char_ (unformatted_spec, unformattedexp); |
| ffeste_f2c_prepare_ptrtoint_ (recl_spec, reclexp); |
| ffeste_f2c_prepare_ptrtoint_ (nextrec_spec, nextrecexp); |
| ffeste_f2c_prepare_char_ (blank_spec, blankexp); |
| |
| ffecom_prepare_end (); |
| |
| /* Now evaluate run-time expressions as needed. */ |
| |
| ffeste_f2c_compile_int_ (unitfield, unit_spec, unitexp); |
| ffeste_f2c_compile_char_ (filefield, filelenfield, file_spec, |
| fileexp, filelenexp); |
| ffeste_f2c_compile_ptrtoint_ (existfield, exist_spec, existexp); |
| ffeste_f2c_compile_ptrtoint_ (openfield, open_spec, openexp); |
| ffeste_f2c_compile_ptrtoint_ (numberfield, number_spec, numberexp); |
| ffeste_f2c_compile_ptrtoint_ (namedfield, named_spec, namedexp); |
| ffeste_f2c_compile_char_ (namefield, namelenfield, name_spec, nameexp, |
| namelenexp); |
| ffeste_f2c_compile_char_ (accessfield, accesslenfield, access_spec, |
| accessexp, accesslenexp); |
| ffeste_f2c_compile_char_ (sequentialfield, sequentiallenfield, |
| sequential_spec, sequentialexp, |
| sequentiallenexp); |
| ffeste_f2c_compile_char_ (directfield, directlenfield, direct_spec, |
| directexp, directlenexp); |
| ffeste_f2c_compile_char_ (formfield, formlenfield, form_spec, formexp, |
| formlenexp); |
| ffeste_f2c_compile_char_ (formattedfield, formattedlenfield, formatted_spec, |
| formattedexp, formattedlenexp); |
| ffeste_f2c_compile_char_ (unformattedfield, unformattedlenfield, |
| unformatted_spec, unformattedexp, |
| unformattedlenexp); |
| ffeste_f2c_compile_ptrtoint_ (reclfield, recl_spec, reclexp); |
| ffeste_f2c_compile_ptrtoint_ (nextrecfield, nextrec_spec, nextrecexp); |
| ffeste_f2c_compile_char_ (blankfield, blanklenfield, blank_spec, blankexp, |
| blanklenexp); |
| |
| ttype = build_pointer_type (TREE_TYPE (t)); |
| t = ffecom_1 (ADDR_EXPR, ttype, t); |
| |
| t = build_tree_list (NULL_TREE, t); |
| |
| return t; |
| } |
| |
| /* Make arglist with ptr to OPEN control list |
| |
| Returns a tree suitable as an argument list containing a pointer to |
| an OPEN-statement control list. First, generates that control |
| list, if necessary, along with any static and run-time initializations |
| that are needed as specified by the arguments to this function. |
| |
| Must ensure that all expressions are prepared before being evaluated, |
| for any whose evaluation might result in the generation of temporaries. |
| |
| Note that this means this function causes a transition, within the |
| current block being code-generated via the back end, from the |
| declaration of variables (temporaries) to the expanding of expressions, |
| statements, etc. */ |
| |
| static GTY(()) tree f2c_open_struct; |
| static tree |
| ffeste_io_olist_ (bool have_err, |
| ffebld unit_expr, |
| ffestpFile *file_spec, |
| ffestpFile *stat_spec, |
| ffestpFile *access_spec, |
| ffestpFile *form_spec, |
| ffestpFile *recl_spec, |
| ffestpFile *blank_spec) |
| { |
| tree t; |
| tree ttype; |
| tree field; |
| tree inits, initn; |
| tree ignore; /* Ignore length info for certain fields. */ |
| bool constantp = TRUE; |
| static tree errfield, unitfield, filefield, filelenfield, statfield, |
| accessfield, formfield, reclfield, blankfield; |
| tree errinit, unitinit, fileinit, fileleninit, statinit, accessinit, |
| forminit, reclinit, blankinit; |
| tree |
| unitexp, fileexp, filelenexp, statexp, accessexp, formexp, reclexp, |
| blankexp; |
| static int mynumber = 0; |
| |
| if (f2c_open_struct == NULL_TREE) |
| { |
| tree ref; |
| |
| ref = make_node (RECORD_TYPE); |
| |
| errfield = ffecom_decl_field (ref, NULL_TREE, "err", |
| ffecom_f2c_flag_type_node); |
| unitfield = ffecom_decl_field (ref, errfield, "unit", |
| ffecom_f2c_ftnint_type_node); |
| filefield = ffecom_decl_field (ref, unitfield, "file", |
| string_type_node); |
| filelenfield = ffecom_decl_field (ref, filefield, "filelen", |
| ffecom_f2c_ftnlen_type_node); |
| statfield = ffecom_decl_field (ref, filelenfield, "stat", |
| string_type_node); |
| accessfield = ffecom_decl_field (ref, statfield, "access", |
| string_type_node); |
| formfield = ffecom_decl_field (ref, accessfield, "form", |
| string_type_node); |
| reclfield = ffecom_decl_field (ref, formfield, "recl", |
| ffecom_f2c_ftnint_type_node); |
| blankfield = ffecom_decl_field (ref, reclfield, "blank", |
| string_type_node); |
| |
| TYPE_FIELDS (ref) = errfield; |
| layout_type (ref); |
| |
| f2c_open_struct = ref; |
| } |
| |
| /* Try to do as much compile-time initialization of the structure |
| as possible, to save run time. */ |
| |
| ffeste_f2c_init_flag_ (have_err, errinit); |
| |
| unitexp = ffecom_const_expr (unit_expr); |
| if (unitexp) |
| unitinit = unitexp; |
| else |
| { |
| unitinit = ffecom_integer_zero_node; |
| constantp = FALSE; |
| } |
| |
| ffeste_f2c_init_char_ (fileexp, fileinit, filelenexp, fileleninit, |
| file_spec); |
| ffeste_f2c_init_charnolen_ (statexp, statinit, stat_spec); |
| ffeste_f2c_init_charnolen_ (accessexp, accessinit, access_spec); |
| ffeste_f2c_init_charnolen_ (formexp, forminit, form_spec); |
| ffeste_f2c_init_int_ (reclexp, reclinit, recl_spec); |
| ffeste_f2c_init_charnolen_ (blankexp, blankinit, blank_spec); |
| |
| inits = build_tree_list ((field = TYPE_FIELDS (f2c_open_struct)), errinit); |
| initn = inits; |
| ffeste_f2c_init_next_ (unitinit); |
| ffeste_f2c_init_next_ (fileinit); |
| ffeste_f2c_init_next_ (fileleninit); |
| ffeste_f2c_init_next_ (statinit); |
| ffeste_f2c_init_next_ (accessinit); |
| ffeste_f2c_init_next_ (forminit); |
| ffeste_f2c_init_next_ (reclinit); |
| ffeste_f2c_init_next_ (blankinit); |
| |
| inits = build_constructor (f2c_open_struct, inits); |
| TREE_CONSTANT (inits) = constantp ? 1 : 0; |
| TREE_STATIC (inits) = 1; |
| |
| t = build_decl (VAR_DECL, |
| ffecom_get_invented_identifier ("__g77_olist_%d", |
| mynumber++), |
| f2c_open_struct); |
| TREE_STATIC (t) = 1; |
| t = ffecom_start_decl (t, 1); |
| ffecom_finish_decl (t, inits, 0); |
| |
| /* Prepare run-time expressions. */ |
| |
| if (! unitexp) |
| ffecom_prepare_expr (unit_expr); |
| |
| ffeste_f2c_prepare_char_ (file_spec, fileexp); |
| ffeste_f2c_prepare_charnolen_ (stat_spec, statexp); |
| ffeste_f2c_prepare_charnolen_ (access_spec, accessexp); |
| ffeste_f2c_prepare_charnolen_ (form_spec, formexp); |
| ffeste_f2c_prepare_int_ (recl_spec, reclexp); |
| ffeste_f2c_prepare_charnolen_ (blank_spec, blankexp); |
| |
| ffecom_prepare_end (); |
| |
| /* Now evaluate run-time expressions as needed. */ |
| |
| if (! unitexp) |
| { |
| unitexp = ffecom_expr (unit_expr); |
| ffeste_f2c_compile_ (unitfield, unitexp); |
| } |
| |
| ffeste_f2c_compile_char_ (filefield, filelenfield, file_spec, fileexp, |
| filelenexp); |
| ffeste_f2c_compile_charnolen_ (statfield, stat_spec, statexp); |
| ffeste_f2c_compile_charnolen_ (accessfield, access_spec, accessexp); |
| ffeste_f2c_compile_charnolen_ (formfield, form_spec, formexp); |
| ffeste_f2c_compile_int_ (reclfield, recl_spec, reclexp); |
| ffeste_f2c_compile_charnolen_ (blankfield, blank_spec, blankexp); |
| |
| ttype = build_pointer_type (TREE_TYPE (t)); |
| t = ffecom_1 (ADDR_EXPR, ttype, t); |
| |
| t = build_tree_list (NULL_TREE, t); |
| |
| return t; |
| } |
| |
| /* Generate code for BACKSPACE/ENDFILE/REWIND. */ |
| |
| static void |
| ffeste_subr_beru_ (ffestpBeruStmt *info, ffecomGfrt rt) |
| { |
| tree alist; |
| bool iostat; |
| bool errl; |
| |
| ffeste_emit_line_note_ (); |
| |
| #define specified(something) (info->beru_spec[something].kw_or_val_present) |
| |
| iostat = specified (FFESTP_beruixIOSTAT); |
| errl = specified (FFESTP_beruixERR); |
| |
| #undef specified |
| |
| /* ~~For now, we assume the unit number is specified and is not ASTERISK, |
| because the FFE doesn't support BACKSPACE(*) and rejects a BACKSPACE |
| without any unit specifier. f2c, however, supports the former |
| construct. When it is time to add this feature to the FFE, which |
| probably is fairly easy, ffestc_R919 and company will want to pass an |
| ffestvUnit indicator of FFESTV_unitINTEXPR or _unitASTERISK to |
| ffeste_R919 and company, and they will want to pass that same value to |
| this function, and that argument will replace the constant _unitINTEXPR_ |
| in the call below. Right now, the default unit number, 6, is ignored. */ |
| |
| ffeste_start_stmt_ (); |
| |
| if (errl) |
| { |
| /* Have ERR= specification. */ |
| |
| ffeste_io_err_ |
| = ffeste_io_abort_ |
| = ffecom_lookup_label |
| (info->beru_spec[FFESTP_beruixERR].u.label); |
| ffeste_io_abort_is_temp_ = FALSE; |
| } |
| else |
| { |
| /* No ERR= specification. */ |
| |
| ffeste_io_err_ = NULL_TREE; |
| |
| if ((ffeste_io_abort_is_temp_ = iostat)) |
| ffeste_io_abort_ = ffecom_temp_label (); |
| else |
| ffeste_io_abort_ = NULL_TREE; |
| } |
| |
| if (iostat) |
| { |
| /* Have IOSTAT= specification. */ |
| |
| ffeste_io_iostat_is_temp_ = FALSE; |
| ffeste_io_iostat_ = ffecom_expr |
| (info->beru_spec[FFESTP_beruixIOSTAT].u.expr); |
| } |
| else if (ffeste_io_abort_ != NULL_TREE) |
| { |
| /* Have no IOSTAT= but have ERR=. */ |
| |
| ffeste_io_iostat_is_temp_ = TRUE; |
| ffeste_io_iostat_ |
| = ffecom_make_tempvar ("beru", ffecom_integer_type_node, |
| FFETARGET_charactersizeNONE, -1); |
| } |
| else |
| { |
| /* No IOSTAT= or ERR= specification. */ |
| |
| ffeste_io_iostat_is_temp_ = FALSE; |
| ffeste_io_iostat_ = NULL_TREE; |
| } |
| |
| /* Now prescan, then convert, all the arguments. */ |
| |
| alist = ffeste_io_ialist_ (errl || iostat, FFESTV_unitINTEXPR, |
| info->beru_spec[FFESTP_beruixUNIT].u.expr, 6); |
| |
| /* Don't generate "if (iostat != 0) goto label;" if label is temp abort |
| label, since we're gonna fall through to there anyway. */ |
| |
| ffeste_io_call_ (ffecom_call_gfrt (rt, alist, NULL_TREE), |
| ! ffeste_io_abort_is_temp_); |
| |
| /* If we've got a temp label, generate its code here. */ |
| |
| if (ffeste_io_abort_is_temp_) |
| { |
| DECL_INITIAL (ffeste_io_abort_) = error_mark_node; |
| emit_nop (); |
| expand_label (ffeste_io_abort_); |
| |
| assert (ffeste_io_err_ == NULL_TREE); |
| } |
| |
| ffeste_end_stmt_ (); |
| } |
| |
| /* END DO statement |
| |
| Also invoked by _labeldef_branch_finish_ (or, in cases |
| of errors, other _labeldef_ functions) when the label definition is |
| for a DO-target (LOOPEND) label, once per matching/outstanding DO |
| block on the stack. */ |
| |
| void |
| ffeste_do (ffestw block) |
| { |
| ffeste_emit_line_note_ (); |
| |
| if (ffestw_do_tvar (block) == 0) |
| { |
| expand_end_loop (); /* DO WHILE and just DO. */ |
| |
| ffeste_end_block_ (block); |
| } |
| else |
| ffeste_end_iterdo_ (block, |
| ffestw_do_tvar (block), |
| ffestw_do_incr_saved (block), |
| ffestw_do_count_var (block)); |
| } |
| |
| /* End of statement following logical IF. |
| |
| Applies to *only* logical IF, not to IF-THEN. */ |
| |
| void |
| ffeste_end_R807 (void) |
| { |
| ffeste_emit_line_note_ (); |
| |
| expand_end_cond (); |
| |
| ffeste_end_block_ (NULL); |
| } |
| |
| /* Generate "code" for branch label definition. */ |
| |
| void |
| ffeste_labeldef_branch (ffelab label) |
| { |
| tree glabel; |
| |
| glabel = ffecom_lookup_label (label); |
| assert (glabel != NULL_TREE); |
| if (TREE_CODE (glabel) == ERROR_MARK) |
| return; |
| |
| assert (DECL_INITIAL (glabel) == NULL_TREE); |
| |
| DECL_INITIAL (glabel) = error_mark_node; |
| DECL_SOURCE_FILE (glabel) = ffelab_definition_filename (label); |
| DECL_SOURCE_LINE (glabel) = ffelab_definition_filelinenum (label); |
| |
| emit_nop (); |
| |
| expand_label (glabel); |
| } |
| |
| /* Generate "code" for FORMAT label definition. */ |
| |
| void |
| ffeste_labeldef_format (ffelab label) |
| { |
| ffeste_label_formatdef_ = label; |
| } |
| |
| /* Assignment statement (outside of WHERE). */ |
| |
| void |
| ffeste_R737A (ffebld dest, ffebld source) |
| { |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| ffeste_start_stmt_ (); |
| |
| ffecom_expand_let_stmt (dest, source); |
| |
| ffeste_end_stmt_ (); |
| } |
| |
| /* Block IF (IF-THEN) statement. */ |
| |
| void |
| ffeste_R803 (ffestw block, ffebld expr) |
| { |
| tree temp; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| ffeste_start_block_ (block); |
| |
| temp = ffecom_make_tempvar ("ifthen", integer_type_node, |
| FFETARGET_charactersizeNONE, -1); |
| |
| ffeste_start_stmt_ (); |
| |
| ffecom_prepare_expr (expr); |
| |
| if (ffecom_prepare_end ()) |
| { |
| tree result; |
| |
| result = ffecom_modify (void_type_node, |
| temp, |
| ffecom_truth_value (ffecom_expr (expr))); |
| |
| expand_expr_stmt (result); |
| |
| ffeste_end_stmt_ (); |
| } |
| else |
| { |
| ffeste_end_stmt_ (); |
| |
| temp = ffecom_truth_value (ffecom_expr (expr)); |
| } |
| |
| expand_start_cond (temp, 0); |
| |
| /* No fake `else' constructs introduced (yet). */ |
| ffestw_set_ifthen_fake_else (block, 0); |
| } |
| |
| /* ELSE IF statement. */ |
| |
| void |
| ffeste_R804 (ffestw block, ffebld expr) |
| { |
| tree temp; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| /* Since ELSEIF(expr) might require preparations for expr, |
| implement as ELSE; prepare-expr; IF (expr) THEN ...; ENDIF. */ |
| |
| expand_start_else (); |
| |
| ffeste_start_block_ (block); |
| |
| temp = ffecom_make_tempvar ("elseif", integer_type_node, |
| FFETARGET_charactersizeNONE, -1); |
| |
| ffeste_start_stmt_ (); |
| |
| ffecom_prepare_expr (expr); |
| |
| if (ffecom_prepare_end ()) |
| { |
| tree result; |
| |
| result = ffecom_modify (void_type_node, |
| temp, |
| ffecom_truth_value (ffecom_expr (expr))); |
| |
| expand_expr_stmt (result); |
| |
| ffeste_end_stmt_ (); |
| } |
| else |
| { |
| /* In this case, we could probably have used expand_start_elseif |
| instead, saving the need for a fake `else' construct. But, |
| until it's clear that'd improve performance, it's easier this |
| way, since we have to expand_start_else before we get to this |
| test, given the current design. */ |
| |
| ffeste_end_stmt_ (); |
| |
| temp = ffecom_truth_value (ffecom_expr (expr)); |
| } |
| |
| expand_start_cond (temp, 0); |
| |
| /* Increment number of fake `else' constructs introduced. */ |
| ffestw_set_ifthen_fake_else (block, |
| ffestw_ifthen_fake_else (block) + 1); |
| } |
| |
| /* ELSE statement. */ |
| |
| void |
| ffeste_R805 (ffestw block UNUSED) |
| { |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| expand_start_else (); |
| } |
| |
| /* END IF statement. */ |
| |
| void |
| ffeste_R806 (ffestw block) |
| { |
| int i = ffestw_ifthen_fake_else (block) + 1; |
| |
| ffeste_emit_line_note_ (); |
| |
| for (; i; --i) |
| { |
| expand_end_cond (); |
| |
| ffeste_end_block_ (block); |
| } |
| } |
| |
| /* Logical IF statement. */ |
| |
| void |
| ffeste_R807 (ffebld expr) |
| { |
| tree temp; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| ffeste_start_block_ (NULL); |
| |
| temp = ffecom_make_tempvar ("if", integer_type_node, |
| FFETARGET_charactersizeNONE, -1); |
| |
| ffeste_start_stmt_ (); |
| |
| ffecom_prepare_expr (expr); |
| |
| if (ffecom_prepare_end ()) |
| { |
| tree result; |
| |
| result = ffecom_modify (void_type_node, |
| temp, |
| ffecom_truth_value (ffecom_expr (expr))); |
| |
| expand_expr_stmt (result); |
| |
| ffeste_end_stmt_ (); |
| } |
| else |
| { |
| ffeste_end_stmt_ (); |
| |
| temp = ffecom_truth_value (ffecom_expr (expr)); |
| } |
| |
| expand_start_cond (temp, 0); |
| } |
| |
| /* SELECT CASE statement. */ |
| |
| void |
| ffeste_R809 (ffestw block, ffebld expr) |
| { |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| ffeste_start_block_ (block); |
| |
| if ((expr == NULL) |
| || (ffeinfo_basictype (ffebld_info (expr)) |
| == FFEINFO_basictypeANY)) |
| ffestw_set_select_texpr (block, error_mark_node); |
| else if (ffeinfo_basictype (ffebld_info (expr)) |
| == FFEINFO_basictypeCHARACTER) |
| { |
| /* ~~~Someday handle CHARACTER*1, CHARACTER*N */ |
| |
| /* xgettext:no-c-format */ |
| ffebad_start_msg ("SELECT CASE on CHARACTER type (at %0) not supported -- sorry", |
| FFEBAD_severityFATAL); |
| ffebad_here (0, ffestw_line (block), ffestw_col (block)); |
| ffebad_finish (); |
| ffestw_set_select_texpr (block, error_mark_node); |
| } |
| else |
| { |
| tree result; |
| tree texpr; |
| |
| result = ffecom_make_tempvar ("select", ffecom_type_expr (expr), |
| ffeinfo_size (ffebld_info (expr)), |
| -1); |
| |
| ffeste_start_stmt_ (); |
| |
| ffecom_prepare_expr (expr); |
| |
| ffecom_prepare_end (); |
| |
| texpr = ffecom_expr (expr); |
| |
| assert (TYPE_MAIN_VARIANT (TREE_TYPE (texpr)) |
| == TYPE_MAIN_VARIANT (TREE_TYPE (result))); |
| |
| texpr = ffecom_modify (void_type_node, |
| result, |
| texpr); |
| expand_expr_stmt (texpr); |
| |
| ffeste_end_stmt_ (); |
| |
| expand_start_case (1, result, TREE_TYPE (result), |
| "SELECT CASE statement"); |
| ffestw_set_select_texpr (block, texpr); |
| ffestw_set_select_break (block, FALSE); |
| } |
| } |
| |
| /* CASE statement. |
| |
| If casenum is 0, it's CASE DEFAULT. Else it's the case ranges at |
| the start of the first_stmt list in the select object at the top of |
| the stack that match casenum. */ |
| |
| void |
| ffeste_R810 (ffestw block, unsigned long casenum) |
| { |
| ffestwSelect s = ffestw_select (block); |
| ffestwCase c; |
| tree texprlow; |
| tree texprhigh; |
| tree tlabel; |
| int pushok; |
| tree duplicate; |
| |
| ffeste_check_simple_ (); |
| |
| if (s->first_stmt == (ffestwCase) &s->first_rel) |
| c = NULL; |
| else |
| c = s->first_stmt; |
| |
| ffeste_emit_line_note_ (); |
| |
| if (ffestw_select_texpr (block) == error_mark_node) |
| return; |
| |
| /* ~~~Someday handle CHARACTER*1, CHARACTER*N */ |
| |
| tlabel = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); |
| |
| if (ffestw_select_break (block)) |
| expand_exit_something (); |
| else |
| ffestw_set_select_break (block, TRUE); |
| |
| if ((c == NULL) || (casenum != c->casenum)) |
| { |
| if (casenum == 0) /* Intentional CASE DEFAULT. */ |
| { |
| pushok = pushcase (NULL_TREE, 0, tlabel, &duplicate); |
| assert (pushok == 0); |
| } |
| } |
| else |
| do |
| { |
| texprlow = (c->low == NULL) ? NULL_TREE |
| : ffecom_constantunion_with_type (&ffebld_constant_union (c->low), |
| ffecom_tree_type[s->type][s->kindtype],c->low->consttype); |
| if (c->low != c->high) |
| { |
| texprhigh = (c->high == NULL) ? NULL_TREE |
| : ffecom_constantunion_with_type (&ffebld_constant_union (c->high), |
| ffecom_tree_type[s->type][s->kindtype],c->high->consttype); |
| pushok = pushcase_range (texprlow, texprhigh, convert, |
| tlabel, &duplicate); |
| } |
| else |
| pushok = pushcase (texprlow, convert, tlabel, &duplicate); |
| if (pushok == 2) |
| { |
| ffebad_start_msg ("SELECT (at %0) has duplicate cases -- check integer overflow of CASE(s)", |
| FFEBAD_severityFATAL); |
| ffebad_here (0, ffestw_line (block), ffestw_col (block)); |
| ffebad_finish (); |
| ffestw_set_select_texpr (block, error_mark_node); |
| } |
| c = c->next_stmt; |
| /* Unlink prev. */ |
| c->previous_stmt->previous_stmt->next_stmt = c; |
| c->previous_stmt = c->previous_stmt->previous_stmt; |
| } |
| while ((c != (ffestwCase) &s->first_rel) && (casenum == c->casenum)); |
| } |
| |
| /* END SELECT statement. */ |
| |
| void |
| ffeste_R811 (ffestw block) |
| { |
| ffeste_emit_line_note_ (); |
| |
| /* ~~~Someday handle CHARACTER*1, CHARACTER*N */ |
| |
| if (TREE_CODE (ffestw_select_texpr (block)) != ERROR_MARK) |
| expand_end_case (ffestw_select_texpr (block)); |
| |
| ffeste_end_block_ (block); |
| } |
| |
| /* Iterative DO statement. */ |
| |
| void |
| ffeste_R819A (ffestw block, ffelab label UNUSED, ffebld var, |
| ffebld start, ffelexToken start_token, |
| ffebld end, ffelexToken end_token, |
| ffebld incr, ffelexToken incr_token) |
| { |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| ffeste_begin_iterdo_ (block, NULL, NULL, NULL, |
| var, |
| start, start_token, |
| end, end_token, |
| incr, incr_token, |
| "Iterative DO loop"); |
| } |
| |
| /* DO WHILE statement. */ |
| |
| void |
| ffeste_R819B (ffestw block, ffelab label UNUSED, ffebld expr) |
| { |
| tree result; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| ffeste_start_block_ (block); |
| |
| if (expr) |
| { |
| struct nesting *loop; |
| tree mod; |
| |
| result = ffecom_make_tempvar ("dowhile", integer_type_node, |
| FFETARGET_charactersizeNONE, -1); |
| loop = expand_start_loop (1); |
| |
| ffeste_start_stmt_ (); |
| |
| ffecom_prepare_expr (expr); |
| |
| ffecom_prepare_end (); |
| |
| mod = ffecom_modify (void_type_node, |
| result, |
| ffecom_truth_value (ffecom_expr (expr))); |
| expand_expr_stmt (mod); |
| |
| ffeste_end_stmt_ (); |
| |
| ffestw_set_do_hook (block, loop); |
| expand_exit_loop_top_cond (0, result); |
| } |
| else |
| ffestw_set_do_hook (block, expand_start_loop (1)); |
| |
| ffestw_set_do_tvar (block, NULL_TREE); |
| } |
| |
| /* END DO statement. |
| |
| This is the MIL-STD 1753 END DO. It's syntactic sugar, similar to |
| CONTINUE (except that it has to have a label that is the target of |
| one or more iterative DO statement), not the Fortran-90 structured |
| END DO, which is handled elsewhere, as is the actual mechanism of |
| ending an iterative DO statement, even one that ends at a label. */ |
| |
| void |
| ffeste_R825 (void) |
| { |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| emit_nop (); |
| } |
| |
| /* CYCLE statement. */ |
| |
| void |
| ffeste_R834 (ffestw block) |
| { |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| expand_continue_loop (ffestw_do_hook (block)); |
| } |
| |
| /* EXIT statement. */ |
| |
| void |
| ffeste_R835 (ffestw block) |
| { |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| expand_exit_loop (ffestw_do_hook (block)); |
| } |
| |
| /* GOTO statement. */ |
| |
| void |
| ffeste_R836 (ffelab label) |
| { |
| tree glabel; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| glabel = ffecom_lookup_label (label); |
| if ((glabel != NULL_TREE) |
| && (TREE_CODE (glabel) != ERROR_MARK)) |
| { |
| expand_goto (glabel); |
| TREE_USED (glabel) = 1; |
| } |
| } |
| |
| /* Computed GOTO statement. */ |
| |
| void |
| ffeste_R837 (ffelab *labels, int count, ffebld expr) |
| { |
| int i; |
| tree texpr; |
| tree value; |
| tree tlabel; |
| int pushok; |
| tree duplicate; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| ffeste_start_stmt_ (); |
| |
| ffecom_prepare_expr (expr); |
| |
| ffecom_prepare_end (); |
| |
| texpr = ffecom_expr (expr); |
| |
| expand_start_case (0, texpr, TREE_TYPE (texpr), "computed GOTO statement"); |
| |
| for (i = 0; i < count; ++i) |
| { |
| value = build_int_2 (i + 1, 0); |
| tlabel = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); |
| |
| pushok = pushcase (value, convert, tlabel, &duplicate); |
| assert (pushok == 0); |
| |
| tlabel = ffecom_lookup_label (labels[i]); |
| if ((tlabel == NULL_TREE) |
| || (TREE_CODE (tlabel) == ERROR_MARK)) |
| continue; |
| |
| expand_goto (tlabel); |
| TREE_USED (tlabel) = 1; |
| } |
| expand_end_case (texpr); |
| |
| ffeste_end_stmt_ (); |
| } |
| |
| /* ASSIGN statement. */ |
| |
| void |
| ffeste_R838 (ffelab label, ffebld target) |
| { |
| tree expr_tree; |
| tree label_tree; |
| tree target_tree; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| /* No need to call ffeste_start_stmt_(), as the sorts of expressions |
| seen here should never require use of temporaries. */ |
| |
| label_tree = ffecom_lookup_label (label); |
| if ((label_tree != NULL_TREE) |
| && (TREE_CODE (label_tree) != ERROR_MARK)) |
| { |
| label_tree = ffecom_1 (ADDR_EXPR, |
| build_pointer_type (void_type_node), |
| label_tree); |
| TREE_CONSTANT (label_tree) = 1; |
| |
| target_tree = ffecom_expr_assign_w (target); |
| if (TREE_CODE (target_tree) != ERROR_MARK) |
| { |
| if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (target_tree))) |
| < GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (label_tree)))) |
| error ("ASSIGN to variable that is too small"); |
| |
| label_tree = convert (TREE_TYPE (target_tree), label_tree); |
| |
| expr_tree = ffecom_modify (void_type_node, |
| target_tree, |
| label_tree); |
| expand_expr_stmt (expr_tree); |
| } |
| } |
| } |
| |
| /* Assigned GOTO statement. */ |
| |
| void |
| ffeste_R839 (ffebld target) |
| { |
| tree t; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| /* No need to call ffeste_start_stmt_(), as the sorts of expressions |
| seen here should never require use of temporaries. */ |
| |
| t = ffecom_expr_assign (target); |
| |
| if (TREE_CODE (t) != ERROR_MARK) |
| { |
| if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (t))) |
| < GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (null_pointer_node)))) |
| error ("ASSIGNed GOTO target variable is too small"); |
| |
| expand_computed_goto (convert (TREE_TYPE (null_pointer_node), t)); |
| } |
| } |
| |
| /* Arithmetic IF statement. */ |
| |
| void |
| ffeste_R840 (ffebld expr, ffelab neg, ffelab zero, ffelab pos) |
| { |
| tree gneg = ffecom_lookup_label (neg); |
| tree gzero = ffecom_lookup_label (zero); |
| tree gpos = ffecom_lookup_label (pos); |
| tree texpr; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| if ((gneg == NULL_TREE) || (gzero == NULL_TREE) || (gpos == NULL_TREE)) |
| return; |
| if ((TREE_CODE (gneg) == ERROR_MARK) |
| || (TREE_CODE (gzero) == ERROR_MARK) |
| || (TREE_CODE (gpos) == ERROR_MARK)) |
| return; |
| |
| ffeste_start_stmt_ (); |
| |
| ffecom_prepare_expr (expr); |
| |
| ffecom_prepare_end (); |
| |
| if (neg == zero) |
| { |
| if (neg == pos) |
| expand_goto (gzero); |
| else |
| { |
| /* IF (expr.LE.0) THEN GOTO neg/zero ELSE GOTO pos. */ |
| texpr = ffecom_expr (expr); |
| texpr = ffecom_2 (LE_EXPR, integer_type_node, |
| texpr, |
| convert (TREE_TYPE (texpr), |
| integer_zero_node)); |
| expand_start_cond (ffecom_truth_value (texpr), 0); |
| expand_goto (gzero); |
| expand_start_else (); |
| expand_goto (gpos); |
| expand_end_cond (); |
| } |
| } |
| else if (neg == pos) |
| { |
| /* IF (expr.NE.0) THEN GOTO neg/pos ELSE GOTO zero. */ |
| texpr = ffecom_expr (expr); |
| texpr = ffecom_2 (NE_EXPR, integer_type_node, |
| texpr, |
| convert (TREE_TYPE (texpr), |
| integer_zero_node)); |
| expand_start_cond (ffecom_truth_value (texpr), 0); |
| expand_goto (gneg); |
| expand_start_else (); |
| expand_goto (gzero); |
| expand_end_cond (); |
| } |
| else if (zero == pos) |
| { |
| /* IF (expr.GE.0) THEN GOTO zero/pos ELSE GOTO neg. */ |
| texpr = ffecom_expr (expr); |
| texpr = ffecom_2 (GE_EXPR, integer_type_node, |
| texpr, |
| convert (TREE_TYPE (texpr), |
| integer_zero_node)); |
| expand_start_cond (ffecom_truth_value (texpr), 0); |
| expand_goto (gzero); |
| expand_start_else (); |
| expand_goto (gneg); |
| expand_end_cond (); |
| } |
| else |
| { |
| /* Use a SAVE_EXPR in combo with: |
| IF (expr.LT.0) THEN GOTO neg |
| ELSEIF (expr.GT.0) THEN GOTO pos |
| ELSE GOTO zero. */ |
| tree expr_saved = ffecom_save_tree (ffecom_expr (expr)); |
| |
| texpr = ffecom_2 (LT_EXPR, integer_type_node, |
| expr_saved, |
| convert (TREE_TYPE (expr_saved), |
| integer_zero_node)); |
| expand_start_cond (ffecom_truth_value (texpr), 0); |
| expand_goto (gneg); |
| texpr = ffecom_2 (GT_EXPR, integer_type_node, |
| expr_saved, |
| convert (TREE_TYPE (expr_saved), |
| integer_zero_node)); |
| expand_start_elseif (ffecom_truth_value (texpr)); |
| expand_goto (gpos); |
| expand_start_else (); |
| expand_goto (gzero); |
| expand_end_cond (); |
| } |
| |
| ffeste_end_stmt_ (); |
| } |
| |
| /* CONTINUE statement. */ |
| |
| void |
| ffeste_R841 (void) |
| { |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| emit_nop (); |
| } |
| |
| /* STOP statement. */ |
| |
| void |
| ffeste_R842 (ffebld expr) |
| { |
| tree callit; |
| ffelexToken msg; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| if ((expr == NULL) |
| || (ffeinfo_basictype (ffebld_info (expr)) |
| == FFEINFO_basictypeANY)) |
| { |
| msg = ffelex_token_new_character ("", |
| ffelex_token_where_line (ffesta_tokens[0]), |
| ffelex_token_where_column (ffesta_tokens[0])); |
| expr = ffebld_new_conter (ffebld_constant_new_characterdefault |
| (msg)); |
| ffelex_token_kill (msg); |
| ffebld_set_info (expr, ffeinfo_new (FFEINFO_basictypeCHARACTER, |
| FFEINFO_kindtypeCHARACTERDEFAULT, |
| 0, FFEINFO_kindENTITY, |
| FFEINFO_whereCONSTANT, 0)); |
| } |
| else if (ffeinfo_basictype (ffebld_info (expr)) |
| == FFEINFO_basictypeINTEGER) |
| { |
| char num[50]; |
| |
| assert (ffebld_op (expr) == FFEBLD_opCONTER); |
| assert (ffeinfo_kindtype (ffebld_info (expr)) |
| == FFEINFO_kindtypeINTEGERDEFAULT); |
| sprintf (num, "%" ffetargetIntegerDefault_f "d", |
| ffebld_constant_integer1 (ffebld_conter (expr))); |
| msg = ffelex_token_new_character (num, |
| ffelex_token_where_line (ffesta_tokens[0]), |
| ffelex_token_where_column (ffesta_tokens[0])); |
| expr = ffebld_new_conter (ffebld_constant_new_characterdefault (msg)); |
| ffelex_token_kill (msg); |
| ffebld_set_info (expr, ffeinfo_new (FFEINFO_basictypeCHARACTER, |
| FFEINFO_kindtypeCHARACTERDEFAULT, |
| 0, FFEINFO_kindENTITY, |
| FFEINFO_whereCONSTANT, 0)); |
| } |
| else |
| { |
| assert (ffeinfo_basictype (ffebld_info (expr)) |
| == FFEINFO_basictypeCHARACTER); |
| assert (ffebld_op (expr) == FFEBLD_opCONTER); |
| assert (ffeinfo_kindtype (ffebld_info (expr)) |
| == FFEINFO_kindtypeCHARACTERDEFAULT); |
| } |
| |
| /* No need to call ffeste_start_stmt_(), as the sorts of expressions |
| seen here should never require use of temporaries. */ |
| |
| callit = ffecom_call_gfrt (FFECOM_gfrtSTOP, |
| ffecom_list_ptr_to_expr (ffebld_new_item (expr, NULL)), |
| NULL_TREE); |
| TREE_SIDE_EFFECTS (callit) = 1; |
| |
| expand_expr_stmt (callit); |
| } |
| |
| /* PAUSE statement. */ |
| |
| void |
| ffeste_R843 (ffebld expr) |
| { |
| tree callit; |
| ffelexToken msg; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| if ((expr == NULL) |
| || (ffeinfo_basictype (ffebld_info (expr)) |
| == FFEINFO_basictypeANY)) |
| { |
| msg = ffelex_token_new_character ("", |
| ffelex_token_where_line (ffesta_tokens[0]), |
| ffelex_token_where_column (ffesta_tokens[0])); |
| expr = ffebld_new_conter (ffebld_constant_new_characterdefault (msg)); |
| ffelex_token_kill (msg); |
| ffebld_set_info (expr, ffeinfo_new (FFEINFO_basictypeCHARACTER, |
| FFEINFO_kindtypeCHARACTERDEFAULT, |
| 0, FFEINFO_kindENTITY, |
| FFEINFO_whereCONSTANT, 0)); |
| } |
| else if (ffeinfo_basictype (ffebld_info (expr)) == FFEINFO_basictypeINTEGER) |
| { |
| char num[50]; |
| |
| assert (ffebld_op (expr) == FFEBLD_opCONTER); |
| assert (ffeinfo_kindtype (ffebld_info (expr)) |
| == FFEINFO_kindtypeINTEGERDEFAULT); |
| sprintf (num, "%" ffetargetIntegerDefault_f "d", |
| ffebld_constant_integer1 (ffebld_conter (expr))); |
| msg = ffelex_token_new_character (num, ffelex_token_where_line (ffesta_tokens[0]), |
| ffelex_token_where_column (ffesta_tokens[0])); |
| expr = ffebld_new_conter (ffebld_constant_new_characterdefault (msg)); |
| ffelex_token_kill (msg); |
| ffebld_set_info (expr, ffeinfo_new (FFEINFO_basictypeCHARACTER, |
| FFEINFO_kindtypeCHARACTERDEFAULT, |
| 0, FFEINFO_kindENTITY, |
| FFEINFO_whereCONSTANT, 0)); |
| } |
| else |
| { |
| assert (ffeinfo_basictype (ffebld_info (expr)) |
| == FFEINFO_basictypeCHARACTER); |
| assert (ffebld_op (expr) == FFEBLD_opCONTER); |
| assert (ffeinfo_kindtype (ffebld_info (expr)) |
| == FFEINFO_kindtypeCHARACTERDEFAULT); |
| } |
| |
| /* No need to call ffeste_start_stmt_(), as the sorts of expressions |
| seen here should never require use of temporaries. */ |
| |
| callit = ffecom_call_gfrt (FFECOM_gfrtPAUSE, |
| ffecom_list_ptr_to_expr (ffebld_new_item (expr, NULL)), |
| NULL_TREE); |
| TREE_SIDE_EFFECTS (callit) = 1; |
| |
| expand_expr_stmt (callit); |
| } |
| |
| /* OPEN statement. */ |
| |
| void |
| ffeste_R904 (ffestpOpenStmt *info) |
| { |
| tree args; |
| bool iostat; |
| bool errl; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| #define specified(something) (info->open_spec[something].kw_or_val_present) |
| |
| iostat = specified (FFESTP_openixIOSTAT); |
| errl = specified (FFESTP_openixERR); |
| |
| #undef specified |
| |
| ffeste_start_stmt_ (); |
| |
| if (errl) |
| { |
| ffeste_io_err_ |
| = ffeste_io_abort_ |
| = ffecom_lookup_label |
| (info->open_spec[FFESTP_openixERR].u.label); |
| ffeste_io_abort_is_temp_ = FALSE; |
| } |
| else |
| { |
| ffeste_io_err_ = NULL_TREE; |
| |
| if ((ffeste_io_abort_is_temp_ = iostat)) |
| ffeste_io_abort_ = ffecom_temp_label (); |
| else |
| ffeste_io_abort_ = NULL_TREE; |
| } |
| |
| if (iostat) |
| { |
| /* Have IOSTAT= specification. */ |
| |
| ffeste_io_iostat_is_temp_ = FALSE; |
| ffeste_io_iostat_ = ffecom_expr |
| (info->open_spec[FFESTP_openixIOSTAT].u.expr); |
| } |
| else if (ffeste_io_abort_ != NULL_TREE) |
| { |
| /* Have no IOSTAT= but have ERR=. */ |
| |
| ffeste_io_iostat_is_temp_ = TRUE; |
| ffeste_io_iostat_ |
| = ffecom_make_tempvar ("open", ffecom_integer_type_node, |
| FFETARGET_charactersizeNONE, -1); |
| } |
| else |
| { |
| /* No IOSTAT= or ERR= specification. */ |
| |
| ffeste_io_iostat_is_temp_ = FALSE; |
| ffeste_io_iostat_ = NULL_TREE; |
| } |
| |
| /* Now prescan, then convert, all the arguments. */ |
| |
| args = ffeste_io_olist_ (errl || iostat, |
| info->open_spec[FFESTP_openixUNIT].u.expr, |
| &info->open_spec[FFESTP_openixFILE], |
| &info->open_spec[FFESTP_openixSTATUS], |
| &info->open_spec[FFESTP_openixACCESS], |
| &info->open_spec[FFESTP_openixFORM], |
| &info->open_spec[FFESTP_openixRECL], |
| &info->open_spec[FFESTP_openixBLANK]); |
| |
| /* Don't generate "if (iostat != 0) goto label;" if label is temp abort |
| label, since we're gonna fall through to there anyway. */ |
| |
| ffeste_io_call_ (ffecom_call_gfrt (FFECOM_gfrtFOPEN, args, NULL_TREE), |
| ! ffeste_io_abort_is_temp_); |
| |
| /* If we've got a temp label, generate its code here. */ |
| |
| if (ffeste_io_abort_is_temp_) |
| { |
| DECL_INITIAL (ffeste_io_abort_) = error_mark_node; |
| emit_nop (); |
| expand_label (ffeste_io_abort_); |
| |
| assert (ffeste_io_err_ == NULL_TREE); |
| } |
| |
| ffeste_end_stmt_ (); |
| } |
| |
| /* CLOSE statement. */ |
| |
| void |
| ffeste_R907 (ffestpCloseStmt *info) |
| { |
| tree args; |
| bool iostat; |
| bool errl; |
| |
| ffeste_check_simple_ (); |
| |
| ffeste_emit_line_note_ (); |
| |
| #define specified(something) (info->close_spec[something].kw_or_val_present) |
| |
| iostat = specified (FFESTP_closeixIOSTAT); |
| errl = specified (FFESTP_closeixERR); |
| |
| #undef specified |
| |
| ffeste_start_stmt_ (); |
| |
| if (errl) |
| { |
| ffeste_io_err_ |
| = ffeste_io_abort_ |
| = ffecom_lookup_label |
| (info->close_spec[FFESTP_closeixERR].u.label); |
| ffeste_io_abort_is_temp_ = FALSE; |
| } |
| else |
| { |
| ffeste_io_err_ = NULL_TREE; |
| |
| if ((ffeste_io_abort_is_temp_ = iostat)) |
| ffeste_io_abort_ = ffecom_temp_label (); |
| else |
| |