#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "tree-gimple.h"
#include <stdio.h>
#include "ggc.h"
#include "toplev.h"
#include "defaults.h"
#include "real.h"
#include <gmp.h>
#include <assert.h>
#include "gfortran.h"
#include "trans.h"
#include "trans-stmt.h"
#include "trans-array.h"
#include "trans-types.h"
#include "trans-const.h"
static gfc_file *gfc_current_backend_file;
tree
gfc_advance_chain (tree t, int n)
{
for (; n > 0; n--)
{
assert (t != NULL_TREE);
t = TREE_CHAIN (t);
}
return t;
}
tree
gfc_chainon_list (tree list, tree add)
{
tree l;
l = tree_cons (NULL_TREE, add, NULL_TREE);
return chainon (list, l);
}
static inline void
remove_suffix (char *name, int len)
{
int i;
for (i = 2; i < 8 && len > i; i++)
{
if (name[len - i] == '.')
{
name[len - i] = '\0';
break;
}
}
}
tree
gfc_create_var_np (tree type, const char *prefix)
{
return create_tmp_var_raw (type, prefix);
}
tree
gfc_create_var (tree type, const char *prefix)
{
tree tmp;
tmp = gfc_create_var_np (type, prefix);
pushdecl (tmp);
return tmp;
}
tree
gfc_evaluate_now (tree expr, stmtblock_t * pblock)
{
tree var;
if (TREE_CODE_CLASS (TREE_CODE (expr)) == 'c')
return expr;
var = gfc_create_var (TREE_TYPE (expr), NULL);
gfc_add_modify_expr (pblock, var, expr);
return var;
}
void
gfc_add_modify_expr (stmtblock_t * pblock, tree lhs, tree rhs)
{
tree tmp;
tmp = fold (build_v (MODIFY_EXPR, lhs, rhs));
gfc_add_expr_to_block (pblock, tmp);
}
void
gfc_start_block (stmtblock_t * block)
{
pushlevel (0);
block->has_scope = 1;
block->head = NULL_TREE;
}
void
gfc_init_block (stmtblock_t * block)
{
block->head = NULL_TREE;
block->has_scope = 0;
}
void
gfc_merge_block_scope (stmtblock_t * block)
{
tree decl;
tree next;
assert (block->has_scope);
block->has_scope = 0;
decl = getdecls ();
poplevel (0, 0, 0);
while (decl != NULL_TREE)
{
next = TREE_CHAIN (decl);
TREE_CHAIN (decl) = NULL_TREE;
pushdecl (decl);
decl = next;
}
}
tree
gfc_finish_block (stmtblock_t * stmtblock)
{
tree decl;
tree expr;
tree block;
expr = rationalize_compound_expr (stmtblock->head);
stmtblock->head = NULL_TREE;
if (stmtblock->has_scope)
{
decl = getdecls ();
if (decl)
{
block = poplevel (1, 0, 0);
expr = build_v (BIND_EXPR, decl, expr, block);
}
else
poplevel (0, 0, 0);
}
return expr;
}
tree
gfc_build_addr_expr (tree type, tree t)
{
tree base_type = TREE_TYPE (t);
tree natural_type;
if (type && POINTER_TYPE_P (type)
&& TREE_CODE (base_type) == ARRAY_TYPE
&& TYPE_MAIN_VARIANT (TREE_TYPE (type))
== TYPE_MAIN_VARIANT (TREE_TYPE (base_type)))
natural_type = type;
else
natural_type = build_pointer_type (base_type);
if (TREE_CODE (t) == INDIRECT_REF)
{
if (!type)
type = natural_type;
t = TREE_OPERAND (t, 0);
natural_type = TREE_TYPE (t);
}
else
{
if (DECL_P (t))
TREE_ADDRESSABLE (t) = 1;
t = build1 (ADDR_EXPR, natural_type, t);
}
if (type && natural_type != type)
t = convert (type, t);
return t;
}
tree
gfc_build_indirect_ref (tree t)
{
tree type = TREE_TYPE (t);
if (!POINTER_TYPE_P (type))
abort ();
type = TREE_TYPE (type);
if (TREE_CODE (t) == ADDR_EXPR)
return TREE_OPERAND (t, 0);
else
return build1 (INDIRECT_REF, type, t);
}
tree
gfc_build_array_ref (tree base, tree offset)
{
tree type = TREE_TYPE (base);
if (TREE_CODE (type) != ARRAY_TYPE)
abort ();
type = TREE_TYPE (type);
if (DECL_P (base))
TREE_ADDRESSABLE (base) = 1;
return build (ARRAY_REF, type, base, offset);
}
tree
gfc_build_function_call (tree fndecl, tree arglist)
{
tree fn;
tree call;
fn = gfc_build_addr_expr (NULL, fndecl);
call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fndecl)), fn, arglist, NULL);
TREE_SIDE_EFFECTS (call) = 1;
return call;
}
void
gfc_trans_runtime_check (tree cond, tree msg, stmtblock_t * pblock)
{
stmtblock_t block;
tree body;
tree tmp;
tree args;
cond = fold (cond);
if (integer_zerop (cond))
return;
gfc_start_block (&block);
assert (TREE_CODE (msg) == STRING_CST);
TREE_USED (msg) = 1;
tmp = gfc_build_addr_expr (pchar_type_node, msg);
args = gfc_chainon_list (NULL_TREE, tmp);
tmp = gfc_build_addr_expr (pchar_type_node, gfc_strconst_current_filename);
args = gfc_chainon_list (args, tmp);
tmp = build_int_2 (input_line, 0);
args = gfc_chainon_list (args, tmp);
tmp = gfc_build_function_call (gfor_fndecl_runtime_error, args);
gfc_add_expr_to_block (&block, tmp);
body = gfc_finish_block (&block);
if (integer_onep (cond))
{
gfc_add_expr_to_block (pblock, body);
}
else
{
tmp = gfc_chainon_list (NULL_TREE, cond);
tmp = gfc_chainon_list (tmp, integer_zero_node);
cond = gfc_build_function_call (built_in_decls[BUILT_IN_EXPECT], tmp);
tmp = build_v (COND_EXPR, cond, body, build_empty_stmt ());
gfc_add_expr_to_block (pblock, tmp);
}
}
void
gfc_add_expr_to_block (stmtblock_t * block, tree expr)
{
assert (block);
if (expr == NULL_TREE || IS_EMPTY_STMT (expr))
return;
expr = fold (expr);
if (block->head)
block->head = build_v (COMPOUND_EXPR, block->head, expr);
else
block->head = expr;
}
void
gfc_add_block_to_block (stmtblock_t * block, stmtblock_t * append)
{
assert (append);
assert (!append->has_scope);
gfc_add_expr_to_block (block, append->head);
append->head = NULL_TREE;
}
void
gfc_get_backend_locus (locus * loc)
{
loc->lb = gfc_getmem (sizeof (gfc_linebuf));
loc->lb->linenum = input_line - 1;
loc->lb->file = gfc_current_backend_file;
}
void
gfc_set_backend_locus (locus * loc)
{
input_line = loc->lb->linenum;
gfc_current_backend_file = loc->lb->file;
input_filename = loc->lb->file->filename;
}
tree
gfc_trans_code (gfc_code * code)
{
stmtblock_t block;
tree res;
if (!code)
return build_empty_stmt ();
gfc_start_block (&block);
for (; code; code = code->next)
{
gfc_set_backend_locus (&code->loc);
if (code->here != 0)
{
res = gfc_trans_label_here (code);
gfc_add_expr_to_block (&block, res);
}
switch (code->op)
{
case EXEC_NOP:
res = NULL_TREE;
break;
case EXEC_ASSIGN:
res = gfc_trans_assign (code);
break;
case EXEC_LABEL_ASSIGN:
res = gfc_trans_label_assign (code);
break;
case EXEC_POINTER_ASSIGN:
res = gfc_trans_pointer_assign (code);
break;
case EXEC_CONTINUE:
res = NULL_TREE;
break;
case EXEC_CYCLE:
res = gfc_trans_cycle (code);
break;
case EXEC_EXIT:
res = gfc_trans_exit (code);
break;
case EXEC_GOTO:
res = gfc_trans_goto (code);
break;
case EXEC_PAUSE:
res = gfc_trans_pause (code);
break;
case EXEC_STOP:
res = gfc_trans_stop (code);
break;
case EXEC_CALL:
res = gfc_trans_call (code);
break;
case EXEC_RETURN:
res = gfc_trans_return (code);
break;
case EXEC_IF:
res = gfc_trans_if (code);
break;
case EXEC_ARITHMETIC_IF:
res = gfc_trans_arithmetic_if (code);
break;
case EXEC_DO:
res = gfc_trans_do (code);
break;
case EXEC_DO_WHILE:
res = gfc_trans_do_while (code);
break;
case EXEC_SELECT:
res = gfc_trans_select (code);
break;
case EXEC_FORALL:
res = gfc_trans_forall (code);
break;
case EXEC_WHERE:
res = gfc_trans_where (code);
break;
case EXEC_ALLOCATE:
res = gfc_trans_allocate (code);
break;
case EXEC_DEALLOCATE:
res = gfc_trans_deallocate (code);
break;
case EXEC_OPEN:
res = gfc_trans_open (code);
break;
case EXEC_CLOSE:
res = gfc_trans_close (code);
break;
case EXEC_READ:
res = gfc_trans_read (code);
break;
case EXEC_WRITE:
res = gfc_trans_write (code);
break;
case EXEC_IOLENGTH:
res = gfc_trans_iolength (code);
break;
case EXEC_BACKSPACE:
res = gfc_trans_backspace (code);
break;
case EXEC_ENDFILE:
res = gfc_trans_endfile (code);
break;
case EXEC_INQUIRE:
res = gfc_trans_inquire (code);
break;
case EXEC_REWIND:
res = gfc_trans_rewind (code);
break;
case EXEC_TRANSFER:
res = gfc_trans_transfer (code);
break;
case EXEC_DT_END:
res = gfc_trans_dt_end (code);
break;
default:
internal_error ("gfc_trans_code(): Bad statement code");
}
if (res != NULL_TREE && ! IS_EMPTY_STMT (res))
{
annotate_with_locus (res, input_location);
gfc_add_expr_to_block (&block, res);
}
}
return gfc_finish_block (&block);
}
void
gfc_generate_code (gfc_namespace * ns)
{
gfc_symbol *main_program = NULL;
symbol_attribute attr;
if (!ns->proc_name)
{
gfc_get_symbol ("MAIN__", ns, &main_program);
gfc_clear_attr (&attr);
attr.flavor = FL_PROCEDURE;
attr.proc = PROC_UNKNOWN;
attr.subroutine = 1;
attr.access = ACCESS_PUBLIC;
main_program->attr = attr;
ns->proc_name = main_program;
gfc_commit_symbols ();
}
gfc_generate_function_code (ns);
}
void
gfc_generate_module_code (gfc_namespace * ns)
{
gfc_namespace *n;
gfc_generate_module_vars (ns);
for (n = ns->contained; n; n = n->sibling)
{
if (!n->proc_name)
continue;
gfc_build_function_decl (n->proc_name);
}
for (n = ns->contained; n; n = n->sibling)
{
if (!n->proc_name)
continue;
gfc_generate_function_code (n);
}
}