blob: e1d227a0d66f384c636a20ca3dae49be32cf7dc3 [file]
/* m2statement.cc provides an interface to GCC statement trees.
Copyright (C) 2012-2026 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius@glam.ac.uk>.
This file is part of GNU Modula-2.
GNU Modula-2 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 3, or (at your option)
any later version.
GNU Modula-2 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 Modula-2; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "gcc-consolidation.h"
#include "../gm2-lang.h"
#include "../m2-tree.h"
/* Prototypes. */
#define m2statement_c
#include "m2assert.h"
#include "m2block.h"
#include "m2decl.h"
#include "m2expr.h"
#include "m2statement.h"
#include "m2tree.h"
#include "m2treelib.h"
#include "m2type.h"
#include "m2convert.h"
#include "m2builtins.h"
#include "m2pp.h"
static GTY (()) tree param_list = NULL_TREE; /* Ready for the next time we
call/define a function. */
static GTY (()) tree last_function = NULL_TREE;
/* BuildStartFunctionCode generate function entry code. */
void
m2statement_BuildStartFunctionCode (location_t location, tree fndecl,
bool isexported, bool isinline)
{
tree param_decl;
ASSERT_BOOL (isexported);
ASSERT_BOOL (isinline);
/* Announce we are compiling this function. */
announce_function (fndecl);
/* Set up to compile the function and enter it. */
DECL_INITIAL (fndecl) = NULL_TREE;
current_function_decl = fndecl;
m2block_pushFunctionScope (fndecl);
m2statement_SetBeginLocation (location);
ASSERT_BOOL ((cfun != NULL));
/* Initialize the RTL code for the function. */
allocate_struct_function (fndecl, false);
/* Begin the statement tree for this function. */
DECL_SAVED_TREE (fndecl) = NULL_TREE;
/* Set the context of these parameters to this function. */
for (param_decl = DECL_ARGUMENTS (fndecl); param_decl;
param_decl = TREE_CHAIN (param_decl))
DECL_CONTEXT (param_decl) = fndecl;
/* This function exists in static storage. (This does not mean
`static' in the C sense!) */
TREE_STATIC (fndecl) = 1;
TREE_PUBLIC (fndecl) = isexported;
/* We could do better here by detecting ADR
or type PROC used on this function. --fixme-- */
TREE_ADDRESSABLE (fndecl) = 1;
DECL_DECLARED_INLINE_P (fndecl) = 0; /* isinline; */
}
/* BuildEndFunctionCode generates the function epilogue. */
void
m2statement_BuildEndFunctionCode (location_t location, tree fndecl, bool nested)
{
tree block = DECL_INITIAL (fndecl);
BLOCK_SUPERCONTEXT (block) = fndecl;
/* Must mark the RESULT_DECL as being in this function. */
DECL_CONTEXT (DECL_RESULT (fndecl)) = fndecl;
/* And attach it to the function. */
DECL_INITIAL (fndecl) = block;
m2block_finishFunctionCode (fndecl);
m2statement_SetEndLocation (location);
m2pp_dump_gimple (M2PP_DUMP_PRE_GENERICIZE, fndecl);
gm2_genericize (fndecl);
if (nested)
(void)cgraph_node::get_create (fndecl);
else
{
m2pp_dump_gimple (M2PP_DUMP_POST_GENERICIZE, fndecl);
cgraph_node::finalize_function (fndecl, false);
}
m2block_popFunctionScope ();
/* We're leaving the context of this function, so zap cfun. It's
still in DECL_STRUCT_FUNCTION, and we'll restore it in
tree_rest_of_compilation. */
set_cfun (NULL);
current_function_decl = NULL;
}
/* BuildPushFunctionContext pushes the current function context.
Maps onto push_function_context in ../function.cc. */
void
m2statement_BuildPushFunctionContext (void)
{
push_function_context ();
}
/* BuildPopFunctionContext pops the current function context. Maps
onto pop_function_context in ../function.cc. */
void
m2statement_BuildPopFunctionContext (void)
{
pop_function_context ();
}
void
m2statement_SetBeginLocation (location_t location)
{
if (cfun != NULL)
cfun->function_start_locus = location;
}
void
m2statement_SetEndLocation (location_t location)
{
if (cfun != NULL)
cfun->function_end_locus = location;
}
/* copy_record_fields copy each record field from right to left. */
static
void
copy_record_fields (location_t location, tree left, tree right)
{
unsigned int i;
tree right_value;
tree left_type = TREE_TYPE (left);
vec<constructor_elt, va_gc> *values = CONSTRUCTOR_ELTS (right);
FOR_EACH_CONSTRUCTOR_VALUE (values, i, right_value)
{
tree left_field = m2treelib_get_field_no (left_type, NULL_TREE, false, i);
tree left_ref = m2expr_BuildComponentRef (location, left, left_field);
m2statement_CopyByField (location, left_ref, right_value);
}
}
/* copy_array copy each element of an array from array right to array left. */
static
void
copy_array (location_t location, tree left, tree right)
{
unsigned int i;
tree value;
vec<constructor_elt, va_gc> *values = CONSTRUCTOR_ELTS (right);
tree array_type = TREE_TYPE (left);
tree index_type = TYPE_DOMAIN (array_type);
tree elt_type = TREE_TYPE (array_type);
tree low_indice = TYPE_MIN_VALUE (index_type);
low_indice
= m2convert_BuildConvert (location, index_type, low_indice, false);
FOR_EACH_CONSTRUCTOR_VALUE (values, i, value)
{
tree idx = m2decl_BuildIntegerConstant (i);
idx = m2convert_BuildConvert (location, index_type, idx, false);
tree array_ref = build4_loc (location, ARRAY_REF, elt_type, left,
idx, low_indice, NULL_TREE);
m2statement_CopyByField (location, array_ref, value);
}
}
/* copy_array cst into left using strncpy. */
static
void
copy_strncpy (location_t location, tree left, tree cst)
{
tree result = m2builtins_BuiltinStrNCopy (location,
m2expr_BuildAddr (location, left, false),
m2expr_BuildAddr (location, cst, false),
m2decl_BuildIntegerConstant (m2expr_StringLength (cst)));
TREE_SIDE_EFFECTS (result) = true;
TREE_USED (left) = true;
TREE_USED (cst) = true;
add_stmt (location, result);
}
/* CopyMemcpy copy bytes from src into dest using builtin_memcpy. */
void
m2statement_CopyMemcpy (location_t location, tree dest, tree src, tree bytes)
{
tree addr_dest = m2expr_BuildAddr (location, dest, false);
tree result = m2builtins_BuiltinMemCopy (location,
addr_dest,
m2expr_BuildAddr (location, src, false),
bytes);
TREE_SIDE_EFFECTS (result) = true;
TREE_USED (dest) = true;
TREE_USED (src) = true;
add_stmt (location, result);
}
/* CopyByField_Lower copy right to left using memcpy for unions,
strncpy for string cst, field assignment for records,
array element assignment for array constructors. For all
other types it uses BuildAssignmentStatement. */
static
void
CopyByField_Lower (location_t location,
tree left, tree right)
{
tree left_type = TREE_TYPE (left);
enum tree_code right_code = TREE_CODE (right);
enum tree_code left_code = TREE_CODE (left_type);
if (left_code == RECORD_TYPE && right_code == CONSTRUCTOR)
copy_record_fields (location, left, right);
else if (left_code == ARRAY_TYPE && right_code == CONSTRUCTOR)
copy_array (location, left, right);
else if (left_code == UNION_TYPE && right_code == CONSTRUCTOR)
m2statement_CopyMemcpy (location, left, right,
m2expr_GetSizeOf (location, left));
else if (right_code == STRING_CST)
copy_strncpy (location, left, right);
else
m2statement_BuildAssignmentStatement (location, left, right);
}
/* CopyByField recursively checks each field to ensure GCC
type equivalence and if so it uses assignment.
Otherwise use strncpy or memcpy depending upon type. */
void
m2statement_CopyByField (location_t location, tree des, tree expr)
{
if (m2type_IsGccStrictTypeEquivalent (des, expr))
m2statement_BuildAssignmentStatement (location, des, expr);
else
CopyByField_Lower (location, des, expr);
}
/* BuildAssignmentTree builds the assignment of des and expr.
It returns des. */
void
m2statement_BuildAssignmentTree (location_t location, tree des, tree expr)
{
tree result;
m2assert_AssertLocation (location);
STRIP_TYPE_NOPS (expr);
if (TREE_CODE (expr) == FUNCTION_DECL)
result = build2 (MODIFY_EXPR, TREE_TYPE (des), des,
m2expr_BuildAddr (location, expr, false));
else
{
gcc_assert (TREE_CODE (TREE_TYPE (des)) != TYPE_DECL);
if ((TREE_CODE (expr) == CONSTRUCTOR)
&& (TREE_CODE (TREE_TYPE (des)) == ARRAY_TYPE))
{
m2statement_CopyMemcpy (location, des, expr,
m2expr_GetSizeOf (location,
m2type_GetTreeType (des)));
return;
}
else if (TREE_TYPE (expr) == TREE_TYPE (des))
result = build2 (MODIFY_EXPR, TREE_TYPE (des), des, expr);
else
result = build2 (
MODIFY_EXPR, TREE_TYPE (des), des,
m2convert_BuildConvert (location, TREE_TYPE (des), expr, false));
}
TREE_SIDE_EFFECTS (result) = true;
TREE_USED (des) = true;
TREE_USED (expr) = true;
add_stmt (location, result);
}
/* BuildAssignmentStatement builds the assignment of des and expr. */
void
m2statement_BuildAssignmentStatement (location_t location, tree des, tree expr)
{
m2statement_BuildAssignmentTree (location, des, expr);
}
/* BuildGoto builds a goto operation. */
void
m2statement_BuildGoto (location_t location, char *name)
{
tree label = m2block_getLabel (location, name);
m2assert_AssertLocation (location);
TREE_USED (label) = true;
add_stmt (location, build1 (GOTO_EXPR, void_type_node, label));
}
/* DeclareLabel create a label, name. */
void
m2statement_DeclareLabel (location_t location, char *name)
{
tree label = m2block_getLabel (location, name);
m2assert_AssertLocation (location);
add_stmt (location, build1 (LABEL_EXPR, void_type_node, label));
}
/* BuildParam build a list of parameters, ready for a subsequent
procedure call. */
void
m2statement_BuildParam (location_t location, tree param)
{
m2assert_AssertLocation (location);
TREE_USED (param) = true;
if (TREE_CODE (param) == FUNCTION_DECL)
param = m2expr_BuildAddr (location, param, false);
param_list = chainon (build_tree_list (NULL_TREE, param), param_list);
}
/* nCount return the number of chained tree nodes in list, t. */
static int
nCount (tree t)
{
int i = 0;
while (t != NULL)
{
i++;
t = TREE_CHAIN (t);
}
return i;
}
/* BuildProcedureCallTree creates a procedure call from a procedure
and parameter list and the return type, rettype. */
tree
m2statement_BuildProcedureCallTree (location_t location, tree procedure,
tree rettype)
{
tree functype = TREE_TYPE (procedure);
tree funcptr = build1 (ADDR_EXPR, build_pointer_type (functype), procedure);
tree call;
int n = nCount (param_list);
tree *argarray = XALLOCAVEC (tree, n);
tree t = param_list;
int i;
m2assert_AssertLocation (location);
ASSERT_CONDITION (
last_function
== NULL_TREE); /* Previous function value has not been collected. */
TREE_USED (procedure) = true;
for (i = 0; i < n; i++)
{
argarray[i] = TREE_VALUE (t);
t = TREE_CHAIN (t);
}
if (rettype == NULL_TREE)
{
rettype = void_type_node;
call = build_call_array_loc (location, rettype, funcptr, n, argarray);
TREE_USED (call) = true;
TREE_SIDE_EFFECTS (call) = true;
#if defined(DEBUG_PROCEDURE_CALLS)
fprintf (stderr, "built the modula-2 call, here is the tree\n");
fflush (stderr);
debug_tree (call);
#endif
param_list
= NULL_TREE; /* Ready for the next time we call a procedure. */
last_function = NULL_TREE;
return call;
}
else
{
last_function = build_call_array_loc (
location, m2tree_skip_type_decl (rettype), funcptr, n, argarray);
TREE_USED (last_function) = true;
TREE_SIDE_EFFECTS (last_function) = true;
param_list
= NULL_TREE; /* Ready for the next time we call a procedure. */
return last_function;
}
}
/* BuildIndirectProcedureCallTree creates a procedure call from a
procedure and parameter list and the return type, rettype. */
tree
m2statement_BuildIndirectProcedureCallTree (location_t location,
tree procedure, tree rettype)
{
tree call;
int n = nCount (param_list);
tree *argarray = XALLOCAVEC (tree, n);
tree t = param_list;
int i;
m2assert_AssertLocation (location);
TREE_USED (procedure) = true;
TREE_SIDE_EFFECTS (procedure) = true;
for (i = 0; i < n; i++)
{
argarray[i] = TREE_VALUE (t);
t = TREE_CHAIN (t);
}
if (rettype == NULL_TREE)
{
rettype = void_type_node;
call = build_call_array_loc (location, rettype, procedure, n, argarray);
TREE_USED (call) = true;
TREE_SIDE_EFFECTS (call) = true;
#if defined(DEBUG_PROCEDURE_CALLS)
fprintf (stderr, "built the modula-2 call, here is the tree\n");
fflush (stderr);
debug_tree (call);
#endif
last_function = NULL_TREE;
param_list
= NULL_TREE; /* Ready for the next time we call a procedure. */
return call;
}
else
{
last_function = build_call_array_loc (
location, m2tree_skip_type_decl (rettype), procedure, n, argarray);
TREE_USED (last_function) = true;
TREE_SIDE_EFFECTS (last_function) = true;
param_list
= NULL_TREE; /* Ready for the next time we call a procedure. */
return last_function;
}
}
/* BuildBuiltinCallTree calls the builtin procedure. */
tree
m2statement_BuildBuiltinCallTree (tree func)
{
TREE_USED (func) = true;
TREE_SIDE_EFFECTS (func) = true;
param_list
= NULL_TREE; /* Ready for the next time we call a procedure. */
return func;
}
/* BuildFunctValue generates code for
value := last_function (foobar). */
tree
m2statement_BuildFunctValue (location_t location, tree value)
{
tree assign
= m2treelib_build_modify_expr (location, value, NOP_EXPR, last_function);
m2assert_AssertLocation (location);
ASSERT_CONDITION (
last_function
!= NULL_TREE); /* No value available, possible used before. */
TREE_SIDE_EFFECTS (assign) = true;
TREE_USED (assign) = true;
TREE_USED (value) = true;
last_function = NULL_TREE;
return assign;
}
/* BuildCall2 builds a tree representing: function (arg1, arg2). */
tree
m2statement_BuildCall2 (location_t location, tree function, tree rettype,
tree arg1, tree arg2)
{
m2assert_AssertLocation (location);
ASSERT_CONDITION (param_list == NULL_TREE);
param_list = chainon (build_tree_list (NULL_TREE, arg2), param_list);
param_list = chainon (build_tree_list (NULL_TREE, arg1), param_list);
return m2statement_BuildProcedureCallTree (location, function, rettype);
}
/* BuildCall3 builds a tree representing: function (arg1, arg2, arg3). */
tree
m2statement_BuildCall3 (location_t location, tree function, tree rettype,
tree arg1, tree arg2, tree arg3)
{
m2assert_AssertLocation (location);
ASSERT_CONDITION (param_list == NULL_TREE);
param_list = chainon (build_tree_list (NULL_TREE, arg3), param_list);
param_list = chainon (build_tree_list (NULL_TREE, arg2), param_list);
param_list = chainon (build_tree_list (NULL_TREE, arg1), param_list);
return m2statement_BuildProcedureCallTree (location, function, rettype);
}
/* BuildFunctionCallTree creates a procedure function call from
a procedure and parameter list and the return type, rettype.
No tree is returned as the tree is held in the last_function global
variable. It is expected the BuildFunctValue is to be called after
a call to BuildFunctionCallTree. */
void
m2statement_BuildFunctionCallTree (location_t location, tree procedure,
tree rettype)
{
m2statement_BuildProcedureCallTree (location, procedure, rettype);
}
/* SetLastFunction assigns last_function to, t. */
void
m2statement_SetLastFunction (tree t)
{
last_function = t;
}
/* SetParamList assigns param_list to, t. */
void
m2statement_SetParamList (tree t)
{
param_list = t;
}
/* GetLastFunction returns, last_function. */
tree
m2statement_GetLastFunction (void)
{
return last_function;
}
/* GetParamList returns, param_list. */
tree
m2statement_GetParamList (void)
{
return param_list;
}
/* GetCurrentFunction returns the current_function. */
tree
m2statement_GetCurrentFunction (void)
{
return current_function_decl;
}
/* GetParamTree return parameter, i. */
tree
m2statement_GetParamTree (tree call, unsigned int i)
{
return CALL_EXPR_ARG (call, i);
}
/* BuildTryFinally returns a TRY_FINALL_EXPR with the call and
cleanups attached. */
tree
m2statement_BuildTryFinally (location_t location, tree call, tree cleanups)
{
return build_stmt (location, TRY_FINALLY_EXPR, call, cleanups);
}
/* BuildCleanUp return a CLEANUP_POINT_EXPR which will clobber,
param. */
tree
m2statement_BuildCleanUp (tree param)
{
tree clobber = build_constructor (TREE_TYPE (param), NULL);
TREE_THIS_VOLATILE (clobber) = 1;
return build2 (MODIFY_EXPR, TREE_TYPE (param), param, clobber);
}
/* BuildAsm generates an inline assembler instruction. */
void
m2statement_BuildAsm (location_t location, tree instr, bool isVolatile,
bool isSimple, tree inputs, tree outputs, tree trash,
tree labels)
{
tree string = resolve_asm_operand_names (instr, outputs, inputs, labels);
tree args = build_stmt (location, ASM_EXPR, string, outputs, inputs, trash,
labels);
m2assert_AssertLocation (location);
/* ASM statements without outputs, including simple ones, are treated
as volatile. */
ASM_BASIC_P (args) = isSimple;
ASM_VOLATILE_P (args) = isVolatile;
add_stmt (location, args);
}
/* BuildStart creates a module initialization function. We make
this function public if it is not an inner module. The linker
will create a call list for all linked modules which determines
the initialization sequence for all modules. */
tree
m2statement_BuildStart (location_t location, char *name, bool inner_module)
{
tree fntype;
tree fndecl;
m2assert_AssertLocation (location);
/* The function type depends on the return type and type of args. */
fntype = build_function_type (integer_type_node, NULL_TREE);
fndecl = build_decl (location, FUNCTION_DECL, get_identifier (name), fntype);
DECL_EXTERNAL (fndecl) = 0;
if (inner_module)
TREE_PUBLIC (fndecl) = 0;
else
TREE_PUBLIC (fndecl) = 1;
TREE_STATIC (fndecl) = 1;
DECL_RESULT (fndecl)
= build_decl (location, RESULT_DECL, NULL_TREE, integer_type_node);
DECL_CONTEXT (DECL_RESULT (fndecl)) = fndecl;
/* Prevent the optimizer from removing it if it is public. */
if (TREE_PUBLIC (fndecl))
gm2_mark_addressable (fndecl);
m2statement_BuildStartFunctionCode (location, fndecl, !inner_module,
inner_module);
return fndecl;
}
/* BuildEnd complete the initialization function for this module. */
void
m2statement_BuildEnd (location_t location, tree fndecl, bool nested)
{
m2statement_BuildEndFunctionCode (location, fndecl, nested);
current_function_decl = NULL;
set_cfun (NULL);
}
/* BuildCallInner call the inner module function. It has no
parameters and no return value. */
void
m2statement_BuildCallInner (location_t location, tree fndecl)
{
m2assert_AssertLocation (location);
param_list = NULL_TREE;
add_stmt (location,
m2statement_BuildProcedureCallTree (location, fndecl, NULL_TREE));
}
/* BuildIfThenDoEnd returns a tree which will only execute
statement, s, if, condition, is true. */
tree
m2statement_BuildIfThenDoEnd (tree condition, tree then_block)
{
if (then_block == NULL_TREE)
return NULL_TREE;
else
return fold_build3 (COND_EXPR, void_type_node, condition, then_block,
alloc_stmt_list ());
}
/* BuildIfThenElseEnd returns a tree which will execute then_block
or else_block depending upon, condition. */
tree
m2statement_BuildIfThenElseEnd (tree condition, tree then_block,
tree else_block)
{
if (then_block == NULL_TREE)
return NULL_TREE;
else
return fold_build3 (COND_EXPR, void_type_node, condition, then_block,
else_block);
}
/* BuildReturnValueCode generates the code associated with:
RETURN ( value ). */
void
m2statement_BuildReturnValueCode (location_t location, tree fndecl, tree value)
{
tree ret_stmt;
tree t;
m2assert_AssertLocation (location);
t = build2 (
MODIFY_EXPR, TREE_TYPE (DECL_RESULT (fndecl)), DECL_RESULT (fndecl),
m2convert_BuildConvert (
location, m2tree_skip_type_decl (TREE_TYPE (DECL_RESULT (fndecl))),
value, false));
ret_stmt = build_stmt (location, RETURN_EXPR, t);
add_stmt (location, ret_stmt);
}
/* IfExprJump if expr then jump to the label. */
void
m2statement_IfExprJump (location_t location, tree exp, char *label)
{
tree if_jump;
m2assert_AssertLocation (location);
if (TREE_CODE (TREE_TYPE (exp)) != BOOLEAN_TYPE)
exp = convert_loc (location, m2type_GetBooleanType (), exp);
m2block_push_statement_list (m2block_begin_statement_list ());
m2statement_BuildGoto (location, label);
if_jump = build3 (COND_EXPR, void_type_node, exp,
m2block_pop_statement_list (),
alloc_stmt_list ());
add_stmt (location, if_jump);
}
/* IfBitInSetJump if bit in set jump to label. */
void
m2statement_IfBitInSetJump (location_t location, bool invertCondition,
tree setvalue, tree bit, char *label)
{
tree condition;
condition = m2expr_BuildNotEqualTo (location,
m2expr_BuildLogicalAnd (location,
m2expr_BuildLSL (location,
m2expr_GetWordOne (location),
bit, false),
setvalue),
m2expr_GetWordZero (location)) ;
if (invertCondition)
condition = m2expr_BuildEqualTo (location, condition,
m2type_GetBooleanFalse ());
m2statement_IfExprJump (location, condition, label);
}
#include "gt-m2-m2statement.h"