1998-08-27 22:51:39 +02:00
|
|
|
|
/* Implement looping actions for CHILL.
|
2000-12-07 02:55:52 +01:00
|
|
|
|
Copyright (C) 1992, 1993, 1994, 2000
|
|
|
|
|
Free Software Foundation, Inc.
|
1998-08-27 22:51:39 +02:00
|
|
|
|
|
|
|
|
|
This file is part of GNU CC.
|
|
|
|
|
|
|
|
|
|
GNU CC is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
|
|
GNU CC is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with GNU CC; see the file COPYING. If not, write to
|
1999-01-11 14:17:38 +01:00
|
|
|
|
the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
|
|
|
Boston, MA 02111-1307, USA. */
|
1998-08-27 22:51:39 +02:00
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "tree.h"
|
|
|
|
|
#include "ch-tree.h"
|
|
|
|
|
#include "lex.h"
|
|
|
|
|
#include "flags.h"
|
|
|
|
|
#include "actions.h"
|
|
|
|
|
#include "input.h"
|
|
|
|
|
#include "obstack.h"
|
|
|
|
|
#include "assert.h"
|
|
|
|
|
#include "rtl.h"
|
|
|
|
|
|
|
|
|
|
/* if the user codes '-flocal-loop-counter' on the command line,
|
|
|
|
|
ch-actions.c (lang_decode_option) will set this flag. */
|
|
|
|
|
int flag_local_loop_counter = 0;
|
|
|
|
|
|
2000-01-17 15:51:05 +01:00
|
|
|
|
extern tree chill_truthvalue_conversion PARAMS ((tree));
|
|
|
|
|
extern rtx emit_line_note PARAMS ((char *, int));
|
|
|
|
|
extern void error PARAMS ((char *, ...));
|
|
|
|
|
extern rtx expand_assignment PARAMS ((tree, tree, int, int));
|
|
|
|
|
extern void save_expr_under_name PARAMS ((tree, tree));
|
|
|
|
|
extern void stamp_nesting_label PARAMS ((tree));
|
|
|
|
|
extern int int_fits_type_p PARAMS ((tree, tree));
|
|
|
|
|
extern void warning PARAMS ((char *, ...));
|
1998-08-27 22:51:39 +02:00
|
|
|
|
|
|
|
|
|
/* forward declarations */
|
2000-01-17 15:51:05 +01:00
|
|
|
|
static int classify_loop PARAMS ((void));
|
|
|
|
|
static int declare_temps PARAMS ((void));
|
|
|
|
|
static int initialize_iter_var PARAMS ((void));
|
|
|
|
|
static int maybe_skip_loop PARAMS ((void));
|
|
|
|
|
static int top_loop_end_check PARAMS ((void));
|
|
|
|
|
static int bottom_loop_end_check PARAMS ((void));
|
|
|
|
|
static int increment_temps PARAMS ((void));
|
|
|
|
|
static tree build_temporary_variable PARAMS ((char *, tree));
|
|
|
|
|
static tree maybe_make_for_temp PARAMS ((tree, char *, tree));
|
|
|
|
|
static tree chill_unsigned_type PARAMS ((tree));
|
1998-08-27 22:51:39 +02:00
|
|
|
|
|
|
|
|
|
/* In terms of the parameters passed to build_loop_iterator,
|
|
|
|
|
* there are several types of loops. They are encoded by
|
|
|
|
|
* the ITER_TYPE enumeration.
|
|
|
|
|
*
|
|
|
|
|
* 1) DO FOR EVER; ... OD
|
|
|
|
|
* indicated by a NULL_TREE start_exp, step_exp and end_exp,
|
|
|
|
|
* condition == NULL, in_flag = 0, and ever_flag == 1 in the
|
|
|
|
|
* first ITERATOR.
|
|
|
|
|
*
|
|
|
|
|
* 2) DO WHILE cond; ... OD
|
|
|
|
|
* indicated by NULL_TREE start_exp, step_exp and end_exp,
|
|
|
|
|
* in_flag = 0, and condition != NULL.
|
|
|
|
|
*
|
|
|
|
|
* 3) DO; ... OD
|
|
|
|
|
* indicated by NULL_TREEs in start_exp, step_exp and end_exp,
|
|
|
|
|
* condition != NULL, in_flag == 0 and ever_flag == 0. This
|
|
|
|
|
* is not really a loop, but a compound statement.
|
|
|
|
|
*
|
|
|
|
|
* 4) DO FOR user_var := start_exp
|
|
|
|
|
* [DOWN] TO end_exp BY step_exp; ... DO
|
|
|
|
|
* indicated by non-NULL_TREE start_exp, step_exp and end_exp.
|
|
|
|
|
*
|
|
|
|
|
* 5) DO FOR user_var [DOWN] IN discrete_mode; ... OD
|
|
|
|
|
* indicated by in_flag == 1. start_exp is a non-NULL_TREE
|
|
|
|
|
* discrete mode, with an optional down_flag.
|
|
|
|
|
*
|
|
|
|
|
* 6) DO FOR user_var [DOWN] IN powerset_expr; ... OD
|
|
|
|
|
* indicated by in_flag == 1. start_exp is a non-NULL_TREE
|
|
|
|
|
* powerset mode, with an optional down_flag.
|
|
|
|
|
*
|
|
|
|
|
* 7) DO FOR user_var [DOWN] IN location; ... OD
|
|
|
|
|
* indicated by in_flag == 1. start_exp is a non-NULL_TREE
|
|
|
|
|
* location mode, with an optional down_flag.
|
|
|
|
|
*/
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
DO_UNUSED,
|
|
|
|
|
DO_FOREVER,
|
|
|
|
|
DO_WHILE,
|
|
|
|
|
DO_OD,
|
|
|
|
|
DO_STEP,
|
|
|
|
|
DO_RANGE,
|
|
|
|
|
DO_POWERSET,
|
|
|
|
|
DO_LOC,
|
|
|
|
|
DO_LOC_VARYING
|
|
|
|
|
} ITER_TYPE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct iterator
|
|
|
|
|
{
|
|
|
|
|
/* These variables only have meaning in the first ITERATOR structure. */
|
|
|
|
|
ITER_TYPE itype; /* type of this iterator */
|
|
|
|
|
int error_flag; /* TRUE if no loop was started due to
|
|
|
|
|
user error */
|
|
|
|
|
tree condition; /* WHILE condition expression */
|
|
|
|
|
int down_flag; /* TRUE if DOWN was coded */
|
|
|
|
|
|
|
|
|
|
/* These variables have meaning in every ITERATOR structure. */
|
|
|
|
|
tree user_var; /* user's explicit iteration variable */
|
|
|
|
|
tree start_exp; /* user's start expression
|
|
|
|
|
or IN expression of a FOR .. IN*/
|
|
|
|
|
tree step_exp; /* user's step expression */
|
|
|
|
|
tree end_exp; /* user's end expression */
|
|
|
|
|
tree start_temp; /* temp holding evaluated start_exp */
|
|
|
|
|
tree end_temp; /* temp holding evaluated end_exp */
|
|
|
|
|
tree step_temp; /* temp holding evaluated step_exp */
|
|
|
|
|
tree powerset_temp; /* temp holding user's initial powerset expression */
|
|
|
|
|
tree loc_ptr_temp; /* temp holding count for LOC enumeration ptr */
|
|
|
|
|
tree iter_var; /* hidden variable for the loop */
|
|
|
|
|
tree iter_type; /* hidden variable's type */
|
|
|
|
|
tree base_type; /* LOC enumeration base type */
|
|
|
|
|
struct iterator *next; /* ptr to next iterator for this loop */
|
|
|
|
|
} ITERATOR;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* There's an entry like this for each nested DO loop.
|
|
|
|
|
* The list is maintained by push_loop_block
|
|
|
|
|
* and pop_loop_block.
|
|
|
|
|
*/
|
|
|
|
|
typedef struct loop {
|
|
|
|
|
struct loop *nxt_level; /* pointer to enclosing loop */
|
|
|
|
|
ITERATOR *iter_list; /* iterators for the current loop */
|
|
|
|
|
} LOOP;
|
|
|
|
|
|
|
|
|
|
static LOOP *loop_stack = (LOOP *)0;
|
|
|
|
|
|
2001-10-30 05:27:17 +01:00
|
|
|
|
/*
|
1998-08-27 22:51:39 +02:00
|
|
|
|
|
|
|
|
|
Here is a CHILL DO FOR statement:
|
|
|
|
|
|
|
|
|
|
DO FOR user_var := start_exp BY step_exp [DOWN] TO end_exp
|
|
|
|
|
WHILE condition;
|
|
|
|
|
|
|
|
|
|
For this loop to be 'safe', like a Pascal FOR loop, the start,
|
|
|
|
|
end, and increment expressions are computed once, before the
|
|
|
|
|
assignment to the iteration variable and saved in temporaries,
|
|
|
|
|
before the first assignment of the iteration variable, so the
|
|
|
|
|
following works:
|
|
|
|
|
|
|
|
|
|
FOR i := (i+1) TO (i+10) DO
|
|
|
|
|
|
|
|
|
|
To prevent changes to the start/end/step expressions from
|
2001-10-30 05:27:17 +01:00
|
|
|
|
effecting the loop's termination, and to make the loop end-check
|
1998-08-27 22:51:39 +02:00
|
|
|
|
as simple as possible, we evaluate the step expression into
|
|
|
|
|
a temporary and compute a hidden iteration count before entering
|
2001-10-30 05:27:17 +01:00
|
|
|
|
the loop's body. User code cannot effect the counter, and the
|
1998-08-27 22:51:39 +02:00
|
|
|
|
end-loop check simply decrements the counter and checks for zero.
|
|
|
|
|
|
|
|
|
|
The whole phrase FOR iter := ... TO end_exp can be repeated
|
|
|
|
|
multiple times, with different user-iteration variables. This
|
|
|
|
|
is discussed later.
|
|
|
|
|
|
|
|
|
|
The loop counter calculations need careful design since a loop
|
|
|
|
|
from MININT TO MAXINT must work, in the precision of integers.
|
|
|
|
|
|
2001-10-30 05:27:17 +01:00
|
|
|
|
Here's how it works, in C:
|
1998-08-27 22:51:39 +02:00
|
|
|
|
|
|
|
|
|
0) The DO ... OD loop is simply a block with
|
|
|
|
|
its own scope.
|
|
|
|
|
|
|
|
|
|
1) The DO FOR EVER is simply implemented:
|
|
|
|
|
|
|
|
|
|
loop_top:
|
|
|
|
|
.
|
|
|
|
|
. body of loop
|
|
|
|
|
.
|
|
|
|
|
goto loop_top
|
|
|
|
|
end_loop:
|
|
|
|
|
|
|
|
|
|
2) The DO WHILE is also simple:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
loop_top:
|
|
|
|
|
if (!condition) goto end_loop
|
|
|
|
|
.
|
|
|
|
|
. body of loop
|
|
|
|
|
.
|
|
|
|
|
goto loop_top
|
|
|
|
|
end_loop:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3) The DO FOR [while condition] loop (no DOWN)
|
|
|
|
|
|
|
|
|
|
push a new scope,
|
|
|
|
|
decl iter_var
|
|
|
|
|
|
|
|
|
|
step_temp = step_exp
|
|
|
|
|
start_temp = start_exp
|
|
|
|
|
end_temp = end_exp
|
|
|
|
|
if (end_exp < start_exp) goto end_loop
|
2001-10-30 05:27:17 +01:00
|
|
|
|
// following line is all unsigned arithmetic
|
1998-08-27 22:51:39 +02:00
|
|
|
|
iter_var = (end_exp - start_exp + step_exp) / step_exp
|
|
|
|
|
user_var = start_temp
|
|
|
|
|
loop_top:
|
|
|
|
|
if (!condition) goto end_loop
|
|
|
|
|
.
|
|
|
|
|
. body of loop
|
|
|
|
|
.
|
|
|
|
|
iter_var--
|
|
|
|
|
if (iter_var == 0) goto end_loop
|
|
|
|
|
user_var += step_temp
|
|
|
|
|
goto loop_top
|
|
|
|
|
end_loop:
|
|
|
|
|
pop scope
|
|
|
|
|
|
|
|
|
|
4) The proposed CHILL for [while condition] loop (with DOWN)
|
|
|
|
|
|
|
|
|
|
push a new scope,
|
|
|
|
|
decl iter
|
|
|
|
|
step_temp = step_exp
|
|
|
|
|
start_temp = start_exp
|
|
|
|
|
end_temp = end_exp
|
|
|
|
|
if (end_exp > start_exp) goto end_loop
|
2001-10-30 05:27:17 +01:00
|
|
|
|
// following line is all unsigned arithmetic
|
1998-08-27 22:51:39 +02:00
|
|
|
|
iter_var = (start_exp - end_exp + step_exp) / step_exp
|
|
|
|
|
user_var = start_temp
|
|
|
|
|
loop_top:
|
|
|
|
|
if (!condition) goto end_loop
|
|
|
|
|
.
|
|
|
|
|
. body of loop
|
|
|
|
|
.
|
|
|
|
|
iter_var--
|
|
|
|
|
if (iter_var == 0) goto end_loop
|
|
|
|
|
user_var -= step_temp
|
|
|
|
|
goto loop_top
|
|
|
|
|
end_loop:
|
|
|
|
|
pop scope
|
|
|
|
|
|
|
|
|
|
|
2001-10-30 05:27:17 +01:00
|
|
|
|
5) The range loop, which iterates over a mode's possible
|
1998-08-27 22:51:39 +02:00
|
|
|
|
values, works just like the above step loops, but with
|
2001-10-30 05:27:17 +01:00
|
|
|
|
the start and end values taken from the mode's lower
|
1998-08-27 22:51:39 +02:00
|
|
|
|
and upper domain values.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6) The FOR IN loop, where a location enumeration is
|
|
|
|
|
specified (see spec on page 81 of Z.200, bottom
|
|
|
|
|
of page 186):
|
|
|
|
|
|
|
|
|
|
push a new scope,
|
|
|
|
|
decl iter_var as an unsigned integer
|
|
|
|
|
loc_ptr_temp as pointer to a composite base type
|
|
|
|
|
|
|
|
|
|
if array is varying
|
2001-10-30 05:27:17 +01:00
|
|
|
|
iter_var = array's length field
|
1998-08-27 22:51:39 +02:00
|
|
|
|
else
|
|
|
|
|
iter_var = sizeof array / sizeof base_type
|
|
|
|
|
loc_ptr_temp = &of highest or lowest indexable entry
|
|
|
|
|
loop_top:
|
|
|
|
|
if (!condition) goto end_loop
|
|
|
|
|
.
|
|
|
|
|
. body of loop
|
|
|
|
|
.
|
|
|
|
|
iter_var--
|
|
|
|
|
if (iter_var == 0) goto end_loop
|
|
|
|
|
loc_ptr_temp +/-= sizeof array base_type
|
|
|
|
|
goto loop_top
|
|
|
|
|
end_loop:
|
|
|
|
|
pop scope
|
|
|
|
|
|
|
|
|
|
7) The DO FOR (DOWN) IN powerset_exp
|
|
|
|
|
|
|
|
|
|
push a new scope,
|
|
|
|
|
decl powerset_temp
|
|
|
|
|
decl iterator as basetype of powerset
|
|
|
|
|
|
|
|
|
|
powerset_temp := start_exp
|
|
|
|
|
loop_top:
|
2001-10-30 05:27:17 +01:00
|
|
|
|
// if DOWN
|
1998-08-27 22:51:39 +02:00
|
|
|
|
if (__flsetclrpowerset () == 0) goto end_loop;
|
2001-10-30 05:27:17 +01:00
|
|
|
|
// not DOWN
|
1998-08-27 22:51:39 +02:00
|
|
|
|
if (__ffsetclrpowerset () == 0) goto end_loop;
|
|
|
|
|
if (!condition) goto end_loop
|
|
|
|
|
.
|
|
|
|
|
. body of loop
|
|
|
|
|
.
|
|
|
|
|
goto loop_top
|
|
|
|
|
end_loop:
|
|
|
|
|
pop scope
|
|
|
|
|
|
|
|
|
|
|
2001-10-30 05:27:17 +01:00
|
|
|
|
So, here's the general DO FOR schema, as implemented here:
|
1998-08-27 22:51:39 +02:00
|
|
|
|
|
|
|
|
|
classify_loop -- what type of loop have we?
|
|
|
|
|
-- build_iterator does some of this, also
|
2001-10-30 05:27:17 +01:00
|
|
|
|
expand_start_loop -- start the loop's control scope
|
1998-08-27 22:51:39 +02:00
|
|
|
|
-- start scope for synthesized loop variables
|
|
|
|
|
declare_temps -- create, initialize temporary variables
|
|
|
|
|
maybe_skip_loop -- skip loop if end conditions unsatisfiable
|
|
|
|
|
initialize_iter_var -- initialize the iteration counter
|
2001-10-30 05:27:17 +01:00
|
|
|
|
-- initialize user's loop variable
|
1998-08-27 22:51:39 +02:00
|
|
|
|
expand_start_loop -- generate top-of-loop label
|
|
|
|
|
top_loop_end_check -- generate while code and/or
|
|
|
|
|
powerset find-a-bit function call
|
|
|
|
|
.
|
|
|
|
|
.
|
2001-10-30 05:27:17 +01:00
|
|
|
|
. user's loop body code
|
1998-08-27 22:51:39 +02:00
|
|
|
|
.
|
|
|
|
|
.
|
|
|
|
|
bottom_loop_end_check -- exit if counter has become zero
|
|
|
|
|
increment_temps -- update temps for next iteration
|
|
|
|
|
expand_end_loop -- generate jump back to top of loop
|
|
|
|
|
expand_end_cond -- generate label for end of conditional
|
|
|
|
|
-- end of scope for synthesized loop variables
|
|
|
|
|
free_iterators -- free up iterator space
|
|
|
|
|
|
|
|
|
|
When there are two or more iterator phrases, each of the
|
|
|
|
|
above loop steps must act upon all iterators. For example,
|
|
|
|
|
the 'increment_temps' step must increment all temporaries
|
|
|
|
|
(associated with all iterators).
|
|
|
|
|
|
|
|
|
|
NOTE: Z.200, section 10.1 says that a block is ...
|
|
|
|
|
"the actions statement list in a do action, including any
|
|
|
|
|
loop counter and while control". This means that an exp-
|
|
|
|
|
ression in a WHILE control can include references to the
|
2001-10-30 05:27:17 +01:00
|
|
|
|
loop counters created for the loop's exclusive use.
|
1998-08-27 22:51:39 +02:00
|
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
DCL a (1:10) INT;
|
|
|
|
|
DCL j INT;
|
|
|
|
|
DO FOR j IN a WHILE j > 0;
|
|
|
|
|
...
|
|
|
|
|
OD;
|
|
|
|
|
The 'j' referenced in the while is the loc-identity 'j'
|
2001-10-30 05:27:17 +01:00
|
|
|
|
created inside the loop's scope, and NOT the 'j' declared
|
1998-08-27 22:51:39 +02:00
|
|
|
|
before the loop.
|
2001-10-30 05:27:17 +01:00
|
|
|
|
*/
|
1998-08-27 22:51:39 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The following routines are called directly by the
|
|
|
|
|
* CHILL parser.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
push_loop_block ()
|
|
|
|
|
{
|
|
|
|
|
LOOP *temp = (LOOP *)xmalloc (sizeof (LOOP));
|
|
|
|
|
|
|
|
|
|
/* push a new loop onto the stack */
|
|
|
|
|
temp->nxt_level = loop_stack;
|
|
|
|
|
temp->iter_list = (ITERATOR *)0;
|
|
|
|
|
loop_stack = temp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
pop_loop_block ()
|
|
|
|
|
{
|
|
|
|
|
LOOP *do_temp = loop_stack;
|
|
|
|
|
ITERATOR *ip;
|
|
|
|
|
|
|
|
|
|
/* pop loop block off the list */
|
|
|
|
|
loop_stack = do_temp->nxt_level;
|
|
|
|
|
|
|
|
|
|
/* free the loop's iterator blocks */
|
|
|
|
|
ip = do_temp->iter_list;
|
|
|
|
|
while (ip != NULL)
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *temp = ip->next;
|
|
|
|
|
free (ip);
|
|
|
|
|
ip = temp;
|
|
|
|
|
}
|
|
|
|
|
free (do_temp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
begin_loop_scope ()
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *firstp = loop_stack->iter_list;
|
|
|
|
|
|
|
|
|
|
if (pass < 2)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We need to classify the loop and declare its temporaries
|
|
|
|
|
* here, so as to define them before the WHILE condition
|
|
|
|
|
* (if any) is parsed. The WHILE expression may refer to
|
|
|
|
|
* a temporary.
|
|
|
|
|
*/
|
|
|
|
|
if (classify_loop ())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (firstp->itype != DO_OD)
|
|
|
|
|
declare_temps ();
|
|
|
|
|
|
|
|
|
|
clear_last_expr ();
|
|
|
|
|
push_momentary ();
|
|
|
|
|
expand_start_bindings (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
end_loop_scope (opt_label)
|
|
|
|
|
tree opt_label;
|
|
|
|
|
{
|
|
|
|
|
if (opt_label)
|
|
|
|
|
possibly_define_exit_label (opt_label);
|
|
|
|
|
poplevel (0, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (pass < 2)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
expand_end_bindings (getdecls (), kept_level_p (), 0);
|
|
|
|
|
pop_momentary ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The iterator structure records all aspects of a
|
|
|
|
|
* 'FOR i := start [DOWN] TO end' clause or
|
|
|
|
|
* 'FOR i IN modename' or 'FOR i IN powerset' clause.
|
|
|
|
|
* It's saved on the iter_list of the current LOOP.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
build_loop_iterator (user_var, start_exp, step_exp, end_exp,
|
|
|
|
|
down_flag, in_flag, ever_flag)
|
|
|
|
|
tree user_var, start_exp, step_exp, end_exp;
|
|
|
|
|
int down_flag, in_flag, ever_flag;
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *ip = (ITERATOR *)xmalloc (sizeof (ITERATOR));
|
|
|
|
|
|
|
|
|
|
/* chain this iterator onto the current loop */
|
|
|
|
|
if (loop_stack->iter_list == NULL)
|
|
|
|
|
loop_stack->iter_list = ip;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *temp = loop_stack->iter_list;
|
|
|
|
|
while (temp->next != NULL)
|
|
|
|
|
temp = temp->next;
|
|
|
|
|
temp->next = ip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ip->itype = DO_UNUSED;
|
|
|
|
|
ip->user_var = user_var;
|
|
|
|
|
ip->start_exp = start_exp;
|
|
|
|
|
ip->step_exp = step_exp;
|
|
|
|
|
ip->end_exp = end_exp;
|
|
|
|
|
ip->condition = NULL_TREE;
|
|
|
|
|
ip->start_temp = NULL_TREE;
|
|
|
|
|
ip->end_temp = NULL_TREE;
|
|
|
|
|
ip->step_temp = NULL_TREE;
|
|
|
|
|
ip->down_flag = down_flag;
|
|
|
|
|
ip->powerset_temp = NULL_TREE;
|
|
|
|
|
ip->iter_var = NULL_TREE;
|
|
|
|
|
ip->iter_type = NULL_TREE;
|
|
|
|
|
ip->loc_ptr_temp = NULL_TREE;
|
|
|
|
|
ip->error_flag = 1; /* assume error will be found */
|
|
|
|
|
ip->next = (ITERATOR *)0;
|
|
|
|
|
|
|
|
|
|
if (ever_flag)
|
|
|
|
|
ip->itype = DO_FOREVER;
|
|
|
|
|
else if (in_flag && start_exp != NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
if (TREE_CODE (start_exp) == ERROR_MARK)
|
|
|
|
|
return;
|
|
|
|
|
if (TREE_CODE (TREE_TYPE (start_exp)) == SET_TYPE)
|
|
|
|
|
ip->itype = DO_POWERSET;
|
|
|
|
|
else if (discrete_type_p (TREE_TYPE (ip->start_exp)))
|
|
|
|
|
ip->itype = DO_RANGE;
|
|
|
|
|
else if (TREE_CODE (TREE_TYPE (ip->start_exp)) == ARRAY_TYPE)
|
|
|
|
|
ip->itype = DO_LOC;
|
|
|
|
|
else if (chill_varying_type_p (TREE_TYPE (ip->start_exp)))
|
|
|
|
|
ip->itype = DO_LOC_VARYING;
|
|
|
|
|
else
|
|
|
|
|
{
|
2001-12-03 20:15:19 +01:00
|
|
|
|
error ("loop's IN expression is not a composite object");
|
1998-08-27 22:51:39 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (start_exp == NULL_TREE && end_exp == NULL_TREE
|
|
|
|
|
&& step_exp == NULL_TREE && !down_flag)
|
|
|
|
|
ip->itype = DO_OD;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* FIXME: Move this to the lexer? */
|
|
|
|
|
#define CST_FITS_INT(NODE) (TREE_CODE(NODE) == INTEGER_CST &&\
|
|
|
|
|
int_fits_type_p (NODE, integer_type_node))
|
|
|
|
|
|
|
|
|
|
tree max_prec_type = integer_type_node;
|
|
|
|
|
|
|
|
|
|
if (! discrete_type_p (TREE_TYPE (ip->start_exp)))
|
|
|
|
|
{
|
|
|
|
|
error ("start expr must have discrete mode");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (TREE_CODE (TREE_TYPE (ip->start_exp)) == ENUMERAL_TYPE
|
|
|
|
|
&& CH_ENUM_IS_NUMBERED (TREE_TYPE (ip->start_exp)))
|
|
|
|
|
{
|
|
|
|
|
error ("DO FOR start expression is a numbered SET");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (TREE_CODE (TREE_TYPE (ip->end_exp)) == ENUMERAL_TYPE
|
|
|
|
|
&& CH_ENUM_IS_NUMBERED (TREE_TYPE (ip->end_exp)))
|
|
|
|
|
{
|
|
|
|
|
error ("TO expression is a numbered SET");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* Convert all three expressions to a common precision,
|
|
|
|
|
which is the largest precision they exhibit, but
|
|
|
|
|
INTEGER_CST nodes are built in the lexer as
|
|
|
|
|
long_integer_type nodes. We'll treat convert them to
|
|
|
|
|
integer_type_nodes if possible, for faster loop times. */
|
|
|
|
|
|
|
|
|
|
if (TYPE_PRECISION (max_prec_type) <
|
|
|
|
|
TYPE_PRECISION (TREE_TYPE (ip->start_exp))
|
|
|
|
|
&& !CST_FITS_INT (ip->start_exp))
|
|
|
|
|
max_prec_type = TREE_TYPE (ip->start_exp);
|
|
|
|
|
if (! discrete_type_p (TREE_TYPE (ip->end_exp)))
|
|
|
|
|
{
|
|
|
|
|
error ("TO expr must have discrete mode");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (! CH_COMPATIBLE (ip->start_exp,
|
|
|
|
|
TREE_TYPE (ip->end_exp)))
|
|
|
|
|
{
|
|
|
|
|
error ("start expr and TO expr must be compatible");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (TYPE_PRECISION (max_prec_type) <
|
|
|
|
|
TYPE_PRECISION (TREE_TYPE (ip->end_exp))
|
|
|
|
|
&& !CST_FITS_INT (ip->end_exp))
|
|
|
|
|
max_prec_type = TREE_TYPE (ip->end_exp);
|
|
|
|
|
if (ip->step_exp != NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
/* assure that default 'BY 1' gets a useful type */
|
|
|
|
|
if (ip->step_exp == integer_one_node)
|
|
|
|
|
ip->step_exp = convert (TREE_TYPE (ip->start_exp),
|
|
|
|
|
ip->step_exp);
|
|
|
|
|
if (! discrete_type_p (TREE_TYPE (ip->step_exp)))
|
|
|
|
|
{
|
|
|
|
|
error ("BY expr must have discrete mode");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (! CH_COMPATIBLE (ip->start_exp,
|
|
|
|
|
TREE_TYPE (ip->step_exp)))
|
|
|
|
|
{
|
|
|
|
|
error ("start expr and BY expr must be compatible");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (TYPE_PRECISION (max_prec_type) <
|
|
|
|
|
TYPE_PRECISION (TREE_TYPE (ip->step_exp))
|
|
|
|
|
&& !CST_FITS_INT (ip->step_exp))
|
|
|
|
|
max_prec_type = TREE_TYPE (ip->step_exp);
|
|
|
|
|
}
|
|
|
|
|
if (TREE_CODE (ip->start_exp) == INTEGER_CST
|
|
|
|
|
&& TREE_CODE (ip->end_exp) == INTEGER_CST
|
|
|
|
|
&& compare_int_csts (ip->down_flag ? LT_EXPR : GT_EXPR,
|
|
|
|
|
ip->start_exp, ip->end_exp))
|
|
|
|
|
warning ("body of DO FOR will never execute");
|
|
|
|
|
|
|
|
|
|
ip->start_exp =
|
|
|
|
|
convert (max_prec_type, ip->start_exp);
|
|
|
|
|
ip->end_exp =
|
|
|
|
|
convert (max_prec_type, ip->end_exp);
|
|
|
|
|
|
|
|
|
|
if (ip->step_exp != NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
ip->step_exp =
|
|
|
|
|
convert (max_prec_type, ip->step_exp);
|
|
|
|
|
|
|
|
|
|
if (TREE_CODE (ip->step_exp) != INTEGER_CST)
|
|
|
|
|
{
|
|
|
|
|
/* generate runtime check for negative BY expr */
|
|
|
|
|
ip->step_exp =
|
|
|
|
|
check_range (ip->step_exp, ip->step_exp,
|
|
|
|
|
integer_zero_node, NULL_TREE);
|
|
|
|
|
}
|
|
|
|
|
else if (compare_int_csts (LE_EXPR, ip->step_exp, integer_zero_node))
|
|
|
|
|
{
|
|
|
|
|
error ("BY expression is negative or zero");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ip->itype = DO_STEP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ip->error_flag = 0; /* no errors! */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
build_loop_start (while_control, start_label)
|
|
|
|
|
tree while_control, start_label;
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *firstp = loop_stack->iter_list;
|
|
|
|
|
|
|
|
|
|
firstp->condition = while_control;
|
|
|
|
|
|
|
|
|
|
if (firstp->error_flag)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* We didn't know at begin_loop_scope time about the condition;
|
|
|
|
|
adjust iterator type now. */
|
|
|
|
|
if (firstp->itype == DO_OD && firstp->condition)
|
|
|
|
|
firstp->itype = DO_WHILE;
|
|
|
|
|
|
|
|
|
|
if (initialize_iter_var ())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (maybe_skip_loop ())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* use the label as an 'exit' label,
|
|
|
|
|
'goto' needs another sort of label */
|
|
|
|
|
expand_start_loop (start_label != NULL_TREE);
|
|
|
|
|
|
|
|
|
|
if (top_loop_end_check ())
|
|
|
|
|
return;
|
|
|
|
|
emit_line_note (input_filename, lineno);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Called after the last action of the loop body
|
|
|
|
|
* has been parsed.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
build_loop_end ()
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *ip = loop_stack->iter_list;
|
|
|
|
|
|
|
|
|
|
emit_line_note (input_filename, lineno);
|
|
|
|
|
|
|
|
|
|
if (ip->error_flag)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (bottom_loop_end_check ())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (increment_temps ())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (ip->itype != DO_OD)
|
|
|
|
|
{
|
|
|
|
|
expand_end_loop ();
|
|
|
|
|
|
|
|
|
|
for (; ip != NULL; ip = ip->next)
|
|
|
|
|
{
|
|
|
|
|
switch (ip->itype)
|
|
|
|
|
{
|
|
|
|
|
case DO_LOC_VARYING:
|
|
|
|
|
case DO_STEP:
|
|
|
|
|
expand_end_cond ();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The rest of the routines in this file are called from
|
|
|
|
|
* the above three routines.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
classify_loop ()
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *firstp = loop_stack->iter_list, *ip;
|
|
|
|
|
|
|
|
|
|
firstp->error_flag = 0;
|
|
|
|
|
if (firstp->itype == DO_UNUSED || firstp->itype == DO_OD)
|
|
|
|
|
{
|
|
|
|
|
/* if we have just DO .. OD, do nothing - this is just a
|
|
|
|
|
BEGIN .. END without creating a new scope, and no looping */
|
|
|
|
|
if (firstp->condition != NULL_TREE)
|
|
|
|
|
firstp->itype = DO_WHILE;
|
|
|
|
|
else
|
|
|
|
|
firstp->itype = DO_OD;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Issue a warning if the any loop counter is mentioned more
|
|
|
|
|
than once in the iterator list. */
|
|
|
|
|
for (ip = firstp; ip != NULL; ip = ip->next)
|
|
|
|
|
{
|
|
|
|
|
switch (ip->itype)
|
|
|
|
|
{
|
|
|
|
|
case DO_FOREVER:
|
|
|
|
|
case DO_WHILE:
|
|
|
|
|
break;
|
|
|
|
|
case DO_STEP:
|
|
|
|
|
case DO_RANGE:
|
|
|
|
|
case DO_POWERSET:
|
|
|
|
|
case DO_LOC:
|
|
|
|
|
case DO_LOC_VARYING:
|
|
|
|
|
/* FIXME: check for name uniqueness */
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return firstp->error_flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Reserve space for any loop-control temporaries, initialize them
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
declare_temps ()
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *firstp = loop_stack->iter_list, *ip;
|
|
|
|
|
tree start_ptr;
|
|
|
|
|
|
|
|
|
|
for (ip = firstp; ip != NULL; ip = ip->next)
|
|
|
|
|
{
|
|
|
|
|
switch (ip->itype)
|
|
|
|
|
{
|
|
|
|
|
case DO_FOREVER:
|
|
|
|
|
case DO_WHILE:
|
|
|
|
|
break;
|
|
|
|
|
case DO_STEP:
|
|
|
|
|
ip->iter_type = chill_unsigned_type (TREE_TYPE (ip->start_exp));
|
|
|
|
|
|
|
|
|
|
/* create, initialize temporaries if expressions aren't constant */
|
|
|
|
|
ip->start_temp = maybe_make_for_temp (ip->start_exp, "for_start",
|
|
|
|
|
ip->iter_type);
|
|
|
|
|
ip->end_temp = maybe_make_for_temp (ip->end_exp, "for_end",
|
|
|
|
|
ip->iter_type);
|
|
|
|
|
/* this is just the step-expression */
|
|
|
|
|
ip->step_temp = maybe_make_for_temp (ip->step_exp, "for_step",
|
|
|
|
|
ip->iter_type);
|
|
|
|
|
goto do_step_range;
|
|
|
|
|
|
|
|
|
|
case DO_RANGE:
|
|
|
|
|
ip->iter_type = chill_unsigned_type_node;
|
|
|
|
|
|
|
|
|
|
ip->start_temp =
|
|
|
|
|
(ip->down_flag ? build_chill_upper : build_chill_lower)(TREE_TYPE (ip->start_exp));
|
|
|
|
|
ip->end_temp =
|
|
|
|
|
(ip->down_flag ? build_chill_lower : build_chill_upper)(TREE_TYPE (ip->start_exp));
|
|
|
|
|
|
|
|
|
|
ip->step_temp = integer_one_node;
|
|
|
|
|
|
|
|
|
|
do_step_range:
|
|
|
|
|
if (flag_local_loop_counter)
|
|
|
|
|
{
|
|
|
|
|
/* (re-)declare the user's iteration variable in the
|
|
|
|
|
loop's scope. */
|
|
|
|
|
tree id_node = ip->user_var;
|
|
|
|
|
IDENTIFIER_LOCAL_VALUE (id_node) = ip->user_var =
|
|
|
|
|
decl_temp1 (id_node, ip->iter_type, 0, NULL_TREE,
|
|
|
|
|
0, 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* in this case, it's a previously-declared
|
|
|
|
|
VAR_DECL node, checked in build_loop_iterator. */
|
|
|
|
|
if (TREE_CODE (ip->user_var) == IDENTIFIER_NODE)
|
|
|
|
|
ip->user_var = lookup_name (ip->user_var);
|
|
|
|
|
if (ip->user_var == NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
error ("loop identifier undeclared");
|
|
|
|
|
ip->error_flag = 1;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ip->iter_var =
|
|
|
|
|
decl_temp1 (get_unique_identifier ("iter_var"),
|
|
|
|
|
ip->iter_type, 0, NULL_TREE, 0, 0);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DO_POWERSET:
|
|
|
|
|
ip->iter_type = chill_unsigned_type (
|
|
|
|
|
TYPE_DOMAIN (TREE_TYPE (ip->start_exp)));
|
|
|
|
|
if (flag_local_loop_counter)
|
|
|
|
|
{
|
|
|
|
|
/* declare the user's iteration variable in the loop's scope. */
|
|
|
|
|
/* in this case, it's just an IDENTIFIER_NODE */
|
|
|
|
|
ip->user_var =
|
|
|
|
|
decl_temp1 (ip->user_var, ip->iter_type, 0, NULL_TREE, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* in this case, it's a previously-declared VAR_DECL node */
|
|
|
|
|
ip->user_var = lookup_name (ip->user_var);
|
|
|
|
|
}
|
|
|
|
|
/* the user's powerset-expression, evaluated and saved in a temp */
|
|
|
|
|
ip->powerset_temp = maybe_make_for_temp (ip->start_exp, "for_set",
|
|
|
|
|
TREE_TYPE (ip->start_exp));
|
|
|
|
|
mark_addressable (ip->powerset_temp);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DO_LOC:
|
|
|
|
|
case DO_LOC_VARYING:
|
|
|
|
|
ip->iter_type = chill_unsigned_type_node;
|
|
|
|
|
/* create the counter temp */
|
|
|
|
|
ip->iter_var =
|
|
|
|
|
build_temporary_variable ("iter_var", ip->iter_type);
|
|
|
|
|
|
|
|
|
|
if (!CH_LOCATION_P (ip->start_exp))
|
|
|
|
|
ip->start_exp
|
|
|
|
|
= decl_temp1 (get_unique_identifier ("iter_loc"),
|
|
|
|
|
TREE_TYPE (ip->start_exp), 0,
|
|
|
|
|
ip->start_exp, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (ip->itype == DO_LOC)
|
|
|
|
|
{
|
|
|
|
|
tree array_type = TREE_TYPE (ip->start_exp);
|
|
|
|
|
tree ptr_type;
|
|
|
|
|
tree temp;
|
|
|
|
|
|
|
|
|
|
if (TREE_CODE (TREE_TYPE (array_type)) == BOOLEAN_TYPE)
|
|
|
|
|
{
|
2001-12-03 20:15:19 +01:00
|
|
|
|
error ("can't iterate through array of BOOL");
|
1998-08-27 22:51:39 +02:00
|
|
|
|
ip->error_flag = 1;
|
|
|
|
|
return ip->error_flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FIXME: check for array type in ip->start_exp */
|
|
|
|
|
|
|
|
|
|
/* create pointer temporary */
|
|
|
|
|
ip->base_type = TREE_TYPE (array_type);
|
|
|
|
|
ptr_type = build_pointer_type (ip->base_type);
|
|
|
|
|
ip->loc_ptr_temp =
|
|
|
|
|
build_temporary_variable ("loc_ptr_tmp", ptr_type);
|
|
|
|
|
|
|
|
|
|
/* declare the user's iteration variable in
|
|
|
|
|
the loop's scope, as an expression, to be
|
|
|
|
|
passed to build_component_ref later */
|
|
|
|
|
save_expr_under_name (ip->user_var,
|
|
|
|
|
build1 (INDIRECT_REF, ip->base_type,
|
|
|
|
|
ip->loc_ptr_temp));
|
|
|
|
|
|
|
|
|
|
/* FIXME: see stor_layout */
|
|
|
|
|
ip->step_temp = size_in_bytes (ip->base_type);
|
|
|
|
|
|
|
|
|
|
temp = TYPE_DOMAIN (array_type);
|
|
|
|
|
|
|
|
|
|
/* pointer to first array entry to look at */
|
|
|
|
|
start_ptr = build1 (ADDR_EXPR, ptr_type, ip->start_exp);
|
|
|
|
|
mark_addressable (ip->start_exp);
|
|
|
|
|
ip->start_temp = ip->down_flag ?
|
|
|
|
|
fold (build (PLUS_EXPR, ptr_type,
|
|
|
|
|
start_ptr,
|
|
|
|
|
fold (build (MULT_EXPR, integer_type_node, ip->step_temp,
|
|
|
|
|
fold (build (MINUS_EXPR, integer_type_node,
|
|
|
|
|
TYPE_MAX_VALUE (temp),
|
|
|
|
|
TYPE_MIN_VALUE (temp)))))))
|
|
|
|
|
: start_ptr;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tree array_length =
|
|
|
|
|
convert (integer_type_node,
|
|
|
|
|
build_component_ref (ip->start_exp, var_length_id));
|
|
|
|
|
tree array_type = TREE_TYPE (TREE_CHAIN (
|
|
|
|
|
TYPE_FIELDS (TREE_TYPE (ip->start_exp))));
|
|
|
|
|
tree array_data_ptr =
|
|
|
|
|
build_component_ref (ip->start_exp, var_data_id);
|
|
|
|
|
tree ptr_type;
|
|
|
|
|
|
|
|
|
|
if (TREE_CODE (TREE_TYPE (array_type)) == BOOLEAN_TYPE)
|
|
|
|
|
{
|
|
|
|
|
error ("Can't iterate through array of BOOL");
|
|
|
|
|
firstp->error_flag = 1;
|
|
|
|
|
return firstp->error_flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* create pointer temporary */
|
|
|
|
|
ip->base_type = TREE_TYPE (array_type);
|
|
|
|
|
ptr_type = build_pointer_type (ip->base_type);
|
|
|
|
|
ip->loc_ptr_temp =
|
|
|
|
|
build_temporary_variable ("loc_ptr_temp", ptr_type);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* declare the user's iteration variable in
|
|
|
|
|
the loop's scope, as an expression, to be
|
|
|
|
|
passed to build_component_ref later */
|
|
|
|
|
save_expr_under_name (ip->user_var,
|
|
|
|
|
build1 (INDIRECT_REF, ip->base_type,
|
|
|
|
|
ip->loc_ptr_temp));
|
|
|
|
|
|
|
|
|
|
/* FIXME: see stor_layout */
|
|
|
|
|
ip->step_temp = size_in_bytes (ip->base_type);
|
|
|
|
|
|
|
|
|
|
/* pointer to first array entry to look at */
|
|
|
|
|
start_ptr = build1 (ADDR_EXPR, ptr_type, array_data_ptr);
|
|
|
|
|
mark_addressable (array_data_ptr);
|
|
|
|
|
ip->start_temp = ip->down_flag ?
|
|
|
|
|
fold (build (PLUS_EXPR, ptr_type,
|
|
|
|
|
start_ptr,
|
|
|
|
|
fold (build (MULT_EXPR, integer_type_node, ip->step_temp,
|
|
|
|
|
fold (build (MINUS_EXPR, integer_type_node,
|
|
|
|
|
array_length,
|
|
|
|
|
integer_one_node))))))
|
|
|
|
|
: start_ptr;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return firstp->error_flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Initialize the hidden iteration-control variables,
|
|
|
|
|
* and the user's explicit loop variable.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
initialize_iter_var ()
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *firstp = loop_stack->iter_list, *ip;
|
|
|
|
|
|
|
|
|
|
for (ip = firstp; ip != NULL; ip = ip->next)
|
|
|
|
|
{
|
|
|
|
|
switch (ip->itype)
|
|
|
|
|
{
|
|
|
|
|
case DO_FOREVER:
|
|
|
|
|
case DO_WHILE:
|
|
|
|
|
break;
|
|
|
|
|
case DO_STEP:
|
|
|
|
|
case DO_RANGE:
|
|
|
|
|
{
|
|
|
|
|
tree count =
|
|
|
|
|
fold (build (PLUS_EXPR, ip->iter_type, integer_one_node,
|
|
|
|
|
fold (build (TRUNC_DIV_EXPR, ip->iter_type,
|
|
|
|
|
convert (ip->iter_type,
|
|
|
|
|
fold (build (MINUS_EXPR, ip->iter_type,
|
|
|
|
|
ip->down_flag ? ip->start_temp : ip->end_temp,
|
|
|
|
|
ip->down_flag ? ip->end_temp : ip->start_temp))),
|
|
|
|
|
ip->step_temp))));
|
|
|
|
|
/* initialize the loop's hidden counter variable */
|
|
|
|
|
expand_expr_stmt (
|
|
|
|
|
build_chill_modify_expr (ip->iter_var, count));
|
|
|
|
|
|
|
|
|
|
/* initialize user's variable */
|
|
|
|
|
expand_expr_stmt (
|
|
|
|
|
build_chill_modify_expr (ip->user_var, ip->start_temp));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case DO_POWERSET:
|
|
|
|
|
break;
|
|
|
|
|
case DO_LOC:
|
|
|
|
|
{
|
|
|
|
|
tree array_type = TREE_TYPE (ip->start_exp);
|
|
|
|
|
tree array_length =
|
|
|
|
|
fold (build (TRUNC_DIV_EXPR, integer_type_node,
|
|
|
|
|
size_in_bytes (array_type),
|
|
|
|
|
size_in_bytes (TREE_TYPE (array_type))));
|
|
|
|
|
|
|
|
|
|
expand_expr_stmt (
|
|
|
|
|
build_chill_modify_expr (ip->iter_var, array_length));
|
|
|
|
|
goto do_loc_common;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case DO_LOC_VARYING:
|
|
|
|
|
expand_expr_stmt (
|
|
|
|
|
build_chill_modify_expr (ip->iter_var,
|
|
|
|
|
convert (integer_type_node,
|
|
|
|
|
build_component_ref (ip->start_exp, var_length_id))));
|
|
|
|
|
|
|
|
|
|
do_loc_common:
|
|
|
|
|
expand_expr_stmt (
|
|
|
|
|
build_chill_modify_expr (ip->loc_ptr_temp,
|
|
|
|
|
ip->start_temp));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return firstp->error_flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generate code to skip the whole loop, if start expression not
|
|
|
|
|
* <= end expression (or >= for DOWN loops). This comparison must
|
|
|
|
|
* *NOT* be done in unsigned mode, or it will fail.
|
|
|
|
|
* Also, skip processing an empty VARYING array.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
maybe_skip_loop ()
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *firstp = loop_stack->iter_list, *ip;
|
|
|
|
|
|
|
|
|
|
for (ip = firstp; ip != NULL; ip = ip->next)
|
|
|
|
|
{
|
|
|
|
|
switch (ip->itype)
|
|
|
|
|
{
|
|
|
|
|
case DO_STEP:
|
|
|
|
|
expand_start_cond (
|
|
|
|
|
build (ip->down_flag ? GE_EXPR : LE_EXPR,
|
|
|
|
|
TREE_TYPE (ip->start_exp),
|
|
|
|
|
ip->start_exp, ip->end_exp), 0);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DO_LOC_VARYING:
|
|
|
|
|
{ tree array_length =
|
|
|
|
|
convert (integer_type_node,
|
|
|
|
|
build_component_ref (ip->start_exp, var_length_id));
|
|
|
|
|
expand_start_cond (
|
|
|
|
|
build (NE_EXPR, TREE_TYPE (array_length),
|
|
|
|
|
array_length, integer_zero_node), 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check at the top of the loop for a termination
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
top_loop_end_check ()
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *firstp = loop_stack->iter_list, *ip;
|
|
|
|
|
|
|
|
|
|
/* now, exit the loop if the condition isn't TRUE. */
|
|
|
|
|
if (firstp->condition)
|
|
|
|
|
{
|
|
|
|
|
expand_exit_loop_if_false (0,
|
|
|
|
|
chill_truthvalue_conversion (firstp->condition));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (ip = firstp; ip != NULL; ip = ip->next)
|
|
|
|
|
{
|
|
|
|
|
switch (ip->itype)
|
|
|
|
|
{
|
|
|
|
|
case DO_FOREVER:
|
|
|
|
|
case DO_WHILE:
|
|
|
|
|
case DO_STEP:
|
|
|
|
|
case DO_RANGE:
|
|
|
|
|
break;
|
|
|
|
|
case DO_POWERSET:
|
|
|
|
|
{
|
|
|
|
|
tree temp1;
|
|
|
|
|
char *func_name;
|
|
|
|
|
|
|
|
|
|
if (ip->down_flag)
|
|
|
|
|
func_name = "__flsetclrpowerset";
|
|
|
|
|
else
|
|
|
|
|
func_name = "__ffsetclrpowerset";
|
|
|
|
|
|
|
|
|
|
temp1 = TYPE_MIN_VALUE
|
|
|
|
|
(TYPE_DOMAIN (TREE_TYPE (ip->powerset_temp)));
|
|
|
|
|
expand_exit_loop_if_false (0,
|
|
|
|
|
build_chill_function_call (lookup_name (get_identifier (func_name)),
|
|
|
|
|
tree_cons (NULL_TREE, force_addr_of (ip->powerset_temp),
|
|
|
|
|
tree_cons (NULL_TREE, powersetlen (ip->powerset_temp),
|
|
|
|
|
tree_cons (NULL_TREE, force_addr_of (ip->user_var),
|
|
|
|
|
tree_cons (NULL_TREE, size_in_bytes (TREE_TYPE (ip->user_var)),
|
|
|
|
|
tree_cons (NULL_TREE,
|
|
|
|
|
convert (long_integer_type_node, temp1),
|
|
|
|
|
NULL_TREE)))))));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case DO_LOC:
|
|
|
|
|
case DO_LOC_VARYING:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return firstp->error_flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check generated temporaries for loop's end
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
bottom_loop_end_check ()
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *firstp = loop_stack->iter_list, *ip;
|
|
|
|
|
|
|
|
|
|
emit_line_note (input_filename, lineno);
|
|
|
|
|
|
|
|
|
|
/* now, generate code to check each loop counter for termination */
|
|
|
|
|
for (ip = firstp; ip != NULL; ip = ip->next)
|
|
|
|
|
{
|
|
|
|
|
switch (ip->itype)
|
|
|
|
|
{
|
|
|
|
|
case DO_FOREVER:
|
|
|
|
|
case DO_WHILE:
|
|
|
|
|
break;
|
|
|
|
|
case DO_STEP:
|
|
|
|
|
case DO_RANGE:
|
|
|
|
|
case DO_LOC:
|
|
|
|
|
case DO_LOC_VARYING:
|
|
|
|
|
/* decrement iteration counter by one */
|
|
|
|
|
chill_expand_assignment (ip->iter_var, MINUS_EXPR, integer_one_node);
|
|
|
|
|
/* exit if it's zero */
|
|
|
|
|
expand_exit_loop_if_false (0,
|
|
|
|
|
build (NE_EXPR, boolean_type_node,
|
|
|
|
|
ip->iter_var,
|
|
|
|
|
integer_zero_node));
|
|
|
|
|
break;
|
|
|
|
|
case DO_POWERSET:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return firstp->error_flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* increment the loop-control variables.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
increment_temps ()
|
|
|
|
|
{
|
|
|
|
|
ITERATOR *firstp = loop_stack->iter_list, *ip;
|
|
|
|
|
|
|
|
|
|
for (ip = firstp; ip != NULL; ip = ip->next)
|
|
|
|
|
{
|
|
|
|
|
switch (ip->itype)
|
|
|
|
|
{
|
|
|
|
|
case DO_FOREVER:
|
|
|
|
|
case DO_WHILE:
|
|
|
|
|
break;
|
|
|
|
|
case DO_STEP:
|
|
|
|
|
case DO_RANGE:
|
|
|
|
|
{
|
|
|
|
|
tree delta =
|
|
|
|
|
fold (build (ip->down_flag ? MINUS_EXPR : PLUS_EXPR,
|
|
|
|
|
TREE_TYPE (ip->user_var), ip->user_var,
|
|
|
|
|
ip->step_temp));
|
|
|
|
|
expand_expr_stmt (
|
|
|
|
|
build_chill_modify_expr (ip->user_var, delta));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case DO_LOC:
|
|
|
|
|
case DO_LOC_VARYING:
|
|
|
|
|
/* This statement uses the C semantics, so that
|
|
|
|
|
the pointer is actually incremented by the
|
|
|
|
|
length of the object pointed to. */
|
|
|
|
|
#if 1
|
|
|
|
|
expand_expr_stmt (
|
|
|
|
|
build_modify_expr (ip->loc_ptr_temp,
|
|
|
|
|
ip->down_flag ? MINUS_EXPR : PLUS_EXPR,
|
|
|
|
|
integer_one_node));
|
|
|
|
|
#else
|
|
|
|
|
{
|
|
|
|
|
enum tree_code op = ip->down_flag ? MINUS_EXPR : PLUS_EXPR;
|
|
|
|
|
tree el_type = TREE_TYPE (TREE_TYPE (ip->loc_ptr_temp));
|
|
|
|
|
chill_expand_assignment (ip->loc_ptr_temp, NOP_EXPR,
|
|
|
|
|
build (op,
|
|
|
|
|
TREE_TYPE (ip->loc_ptr_temp),
|
|
|
|
|
ip->loc_ptr_temp,
|
|
|
|
|
size_in_bytes (el_type)));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
case DO_POWERSET:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return firstp->error_flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Generate a (temporary) unique identifier_node of
|
|
|
|
|
* the form "__tmp_%s_%d"
|
|
|
|
|
*/
|
|
|
|
|
tree
|
|
|
|
|
get_unique_identifier (lead)
|
|
|
|
|
char *lead;
|
|
|
|
|
{
|
|
|
|
|
char idbuf [256];
|
|
|
|
|
static int idcount = 0;
|
|
|
|
|
|
|
|
|
|
sprintf (idbuf, "__tmp_%s_%d", lead ? lead : "", idcount++);
|
|
|
|
|
return get_identifier (idbuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* build a temporary variable, given its NAME and TYPE.
|
|
|
|
|
* The name will have a number appended to assure uniqueness.
|
|
|
|
|
* return its DECL node.
|
|
|
|
|
*/
|
|
|
|
|
static tree
|
|
|
|
|
build_temporary_variable (name, type)
|
|
|
|
|
char *name;
|
|
|
|
|
tree type;
|
|
|
|
|
{
|
|
|
|
|
return decl_temp1 (get_unique_identifier (name), type, 0, NULL_TREE, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the given expression isn't a constant, build a temp for it
|
|
|
|
|
* and evaluate the expression into the temp. Return the tree
|
|
|
|
|
* representing either the original constant expression or the
|
|
|
|
|
* temp which now contains the expression's value.
|
|
|
|
|
*/
|
|
|
|
|
static tree
|
|
|
|
|
maybe_make_for_temp (exp, temp_name, exp_type)
|
|
|
|
|
tree exp;
|
|
|
|
|
char *temp_name;
|
|
|
|
|
tree exp_type;
|
|
|
|
|
{
|
|
|
|
|
tree result = exp;
|
|
|
|
|
|
|
|
|
|
if (exp != NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
/* if exp isn't constant, create a temporary for its value */
|
|
|
|
|
if (TREE_CONSTANT (exp))
|
|
|
|
|
{
|
|
|
|
|
/* FIXME: assure that TREE_TYPE (result) == ip->exp_type */
|
|
|
|
|
result = convert (exp_type, exp);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* build temp, assign the value */
|
|
|
|
|
result = decl_temp1 (get_unique_identifier (temp_name), exp_type, 0,
|
|
|
|
|
exp, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Adapt the C unsigned_type function to CHILL - we need to
|
|
|
|
|
* account for any CHILL-specific integer types here. So far,
|
|
|
|
|
* the 16-bit integer type is the only one.
|
|
|
|
|
*/
|
|
|
|
|
static tree
|
|
|
|
|
chill_unsigned_type (type)
|
|
|
|
|
tree type;
|
|
|
|
|
{
|
|
|
|
|
extern tree chill_unsigned_type_node;
|
|
|
|
|
tree type1 = TYPE_MAIN_VARIANT (type);
|
|
|
|
|
|
|
|
|
|
if (type1 == chill_integer_type_node)
|
|
|
|
|
return chill_unsigned_type_node;
|
|
|
|
|
else
|
|
|
|
|
return unsigned_type (type);
|
|
|
|
|
}
|