blob: 991781e847855c9e1a3b2b6e8b183d953c8d09ba [file]
/* m2treelib.cc provides call trees, modify_expr and miscelaneous.
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"
#define m2treelib_c
#include "m2assert.h"
#include "m2block.h"
#include "m2convert.h"
#include "m2decl.h"
#include "m2expr.h"
#include "m2statement.h"
#include "m2tree.h"
#include "m2treelib.h"
#include "m2treelib.h"
#include "m2type.h"
/* do_jump_if_bit - tests bit in word against integer zero using
operator, code. If the result is true then jump to label. */
void
m2treelib_do_jump_if_bit (location_t location, enum tree_code code, tree word,
tree bit, char *label)
{
word = m2convert_ToWord (location, word);
bit = m2convert_ToWord (location, bit);
m2statement_IfExprJump (
location,
m2expr_build_binary_op (
location, code,
m2expr_build_binary_op (
location, BIT_AND_EXPR, word,
m2expr_BuildLSL (location, m2expr_GetWordOne (location), bit,
FALSE),
FALSE),
m2expr_GetWordZero (location), FALSE),
label);
}
/* build_modify_expr - taken from c-typeck.cc and heavily pruned.
Build an assignment expression of lvalue LHS from value RHS. If
LHS_ORIGTYPE is not NULL, it is the original type of LHS, which
may differ from TREE_TYPE (LHS) for an enum bitfield. MODIFYCODE
is the code for a binary operator that we use to combine the old
value of LHS with RHS to get the new value. Or else MODIFYCODE is
NOP_EXPR meaning do a simple assignment. If RHS_ORIGTYPE is not
NULL_TREE, it is the original type of RHS, which may differ from
TREE_TYPE (RHS) for an enum value.
LOCATION is the location of the MODIFYCODE operator. RHS_LOC is the
location of the RHS. */
static tree
build_modify_expr (location_t location, tree lhs, enum tree_code modifycode,
tree rhs)
{
tree result;
tree newrhs;
tree rhs_semantic_type = NULL_TREE;
tree lhstype = TREE_TYPE (lhs);
tree olhstype = lhstype;
ASSERT_CONDITION (modifycode == NOP_EXPR);
if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
{
rhs_semantic_type = TREE_TYPE (rhs);
rhs = TREE_OPERAND (rhs, 0);
}
newrhs = rhs;
/* If storing into a structure or union member, it has probably been
given type `int'. Compute the type that would go with the actual
amount of storage the member occupies. */
if (TREE_CODE (lhs) == COMPONENT_REF
&& (TREE_CODE (lhstype) == INTEGER_TYPE
|| TREE_CODE (lhstype) == BOOLEAN_TYPE
|| SCALAR_FLOAT_TYPE_P (lhstype)
|| TREE_CODE (lhstype) == ENUMERAL_TYPE))
lhstype = TREE_TYPE (get_unwidened (lhs, 0));
/* If storing in a field that is in actuality a short or narrower
than one, we must store in the field in its actual type. */
if (lhstype != TREE_TYPE (lhs))
{
lhs = copy_node (lhs);
TREE_TYPE (lhs) = lhstype;
}
newrhs = fold (newrhs);
if (rhs_semantic_type)
newrhs = build1 (EXCESS_PRECISION_EXPR, rhs_semantic_type, newrhs);
/* Scan operands. */
result = build2 (MODIFY_EXPR, lhstype, lhs, newrhs);
TREE_SIDE_EFFECTS (result) = 1;
protected_set_expr_location (result, location);
/* If we got the LHS in a different type for storing in, convert the
result back to the nominal type of LHS so that the value we return
always has the same type as the LHS argument. */
ASSERT_CONDITION (olhstype == TREE_TYPE (result));
/* In Modula-2 I'm assuming this will be true this maybe wrong, but
at least I'll know about it soon. If true then we do not need to
implement convert_for_assignment - which is a huge win. */
return result;
}
/* m2treelib_build_modify_expr - wrapper function for
build_modify_expr. */
tree
m2treelib_build_modify_expr (location_t location, tree des,
enum tree_code modifycode, tree copy)
{
return build_modify_expr (location, des, modifycode, copy);
}
/* nCount - return the number of trees chained on, t. */
int
m2treelib_nCount (tree t)
{
int i = 0;
while (t != NULL)
{
i++;
t = TREE_CHAIN (t);
}
return i;
}
/* DoCall - build a call tree arranging the parameter list as a
vector. */
tree
m2treelib_DoCall (location_t location, tree rettype, tree funcptr,
tree param_list)
{
int n = m2treelib_nCount (param_list);
tree *argarray = XALLOCAVEC (tree, n);
tree l = param_list;
int i;
for (i = 0; i < n; i++)
{
argarray[i] = TREE_VALUE (l);
l = TREE_CHAIN (l);
}
return build_call_array_loc (location, rettype, funcptr, n, argarray);
}
/* DoCall0 - build a call tree with no parameters. */
tree
m2treelib_DoCall0 (location_t location, tree rettype, tree funcptr)
{
tree *argarray = XALLOCAVEC (tree, 1);
argarray[0] = NULL_TREE;
return build_call_array_loc (location, rettype, funcptr, 0, argarray);
}
/* DoCall1 - build a call tree with 1 parameter. */
tree
m2treelib_DoCall1 (location_t location, tree rettype, tree funcptr, tree arg0)
{
tree *argarray = XALLOCAVEC (tree, 1);
argarray[0] = arg0;
return build_call_array_loc (location, rettype, funcptr, 1, argarray);
}
/* DoCall2 - build a call tree with 2 parameters. */
tree
m2treelib_DoCall2 (location_t location, tree rettype, tree funcptr, tree arg0,
tree arg1)
{
tree *argarray = XALLOCAVEC (tree, 2);
argarray[0] = arg0;
argarray[1] = arg1;
return build_call_array_loc (location, rettype, funcptr, 2, argarray);
}
/* DoCall3 - build a call tree with 3 parameters. */
tree
m2treelib_DoCall3 (location_t location, tree rettype, tree funcptr, tree arg0,
tree arg1, tree arg2)
{
tree *argarray = XALLOCAVEC (tree, 3);
argarray[0] = arg0;
argarray[1] = arg1;
argarray[2] = arg2;
return build_call_array_loc (location, rettype, funcptr, 3, argarray);
}
/* get_field_no - returns the field no for, op. The, op, is either a
constructor or a variable of type record. If, op, is a
constructor (a set constant in GNU Modula-2) then this function is
essentially a no-op and it returns op. Else we iterate over the
field list and return the appropriate field number. */
tree
m2treelib_get_field_no (tree type, tree op, bool is_const, unsigned int fieldNo)
{
ASSERT_BOOL (is_const);
if (is_const)
return op;
else
{
tree list = TYPE_FIELDS (type);
while (fieldNo > 0 && list != NULL_TREE)
{
list = TREE_CHAIN (list);
fieldNo--;
}
return list;
}
}
/* get_set_value - returns the value indicated by, field, in the set.
Either p->field or the constant(op.fieldNo) is returned. */
tree
m2treelib_get_set_value (location_t location, tree p, tree field, bool is_const,
bool is_lvalue, tree op, unsigned int fieldNo)
{
tree value;
constructor_elt *ce;
ASSERT_BOOL (is_const);
ASSERT_BOOL (is_lvalue);
if (is_const)
{
ASSERT_CONDITION (is_lvalue == FALSE);
gcc_assert (!vec_safe_is_empty (CONSTRUCTOR_ELTS (op)));
unsigned int size = vec_safe_length (CONSTRUCTOR_ELTS (op));
if (size < fieldNo)
internal_error ("field number exceeds definition of set");
if (vec_safe_iterate (CONSTRUCTOR_ELTS (op), fieldNo, &ce))
value = ce->value;
else
internal_error (
"field number out of range trying to access set element");
}
else if (is_lvalue)
{
if (TREE_CODE (TREE_TYPE (p)) == POINTER_TYPE)
value = m2expr_BuildComponentRef (
location, m2expr_BuildIndirect (location, p, TREE_TYPE (p)),
field);
else
{
ASSERT_CONDITION (TREE_CODE (TREE_TYPE (p)) == REFERENCE_TYPE);
value = m2expr_BuildComponentRef (location, p, field);
}
}
else
{
tree type = TREE_TYPE (op);
enum tree_code code = TREE_CODE (type);
ASSERT_CONDITION (code == RECORD_TYPE
|| (code == POINTER_TYPE
&& (TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE)));
value = m2expr_BuildComponentRef (location, op, field);
}
value = m2convert_ToBitset (location, value);
return value;
}
/* get_set_address - returns the address of op1. */
tree
m2treelib_get_set_address (location_t location, tree op1, bool is_lvalue)
{
if (is_lvalue)
return op1;
else
return m2expr_BuildAddr (location, op1, FALSE);
}
/* get_set_field_lhs - returns the address of p->field. */
tree
m2treelib_get_set_field_lhs (location_t location, tree p, tree field)
{
return m2expr_BuildAddr (
location, m2convert_ToBitset (
location, m2expr_BuildComponentRef (location, p, field)),
FALSE);
}
/* get_set_field_rhs - returns the value of p->field. */
tree
m2treelib_get_set_field_rhs (location_t location, tree p, tree field)
{
return m2convert_ToBitset (location,
m2expr_BuildComponentRef (location, p, field));
}
/* get_set_field_des - returns the p->field ready to be a (rhs)
designator. */
tree
m2treelib_get_set_field_des (location_t location, tree p, tree field)
{
return m2expr_BuildIndirect (
location,
m2expr_BuildAddr (location,
m2expr_BuildComponentRef (location, p, field), FALSE),
m2type_GetBitsetType ());
}
/* get_set_address_if_var - returns the address of, op, providing it
is not a constant. NULL is returned if, op, is a constant. */
tree
m2treelib_get_set_address_if_var (location_t location, tree op, bool is_lvalue,
bool is_const)
{
if (is_const)
return NULL;
else
return m2treelib_get_set_address (location, op, is_lvalue);
}
/* add_stmt add stmt to the statement-tree. */
tree
add_stmt (location_t location, tree stmt)
{
return m2block_add_stmt (location, stmt);
}
/* taken from gcc/c-semantics.cc. */
/* Build a generic statement based on the given type of node and
arguments. Similar to `build_nt', except that we set EXPR_LOCATION
to LOC. */
tree
build_stmt (location_t loc, enum tree_code code, ...)
{
tree ret;
int length, i;
va_list p;
bool side_effects;
m2assert_AssertLocation (loc);
/* This function cannot be used to construct variably-sized nodes. */
gcc_assert (TREE_CODE_CLASS (code) != tcc_vl_exp);
va_start (p, code);
ret = make_node (code);
TREE_TYPE (ret) = void_type_node;
length = TREE_CODE_LENGTH (code);
SET_EXPR_LOCATION (ret, loc);
/* TREE_SIDE_EFFECTS will already be set for statements with implicit
side effects. Here we make sure it is set for other expressions by
checking whether the parameters have side effects. */
side_effects = false;
for (i = 0; i < length; i++)
{
tree t = va_arg (p, tree);
if (t && !TYPE_P (t))
side_effects |= TREE_SIDE_EFFECTS (t);
TREE_OPERAND (ret, i) = t;
}
TREE_SIDE_EFFECTS (ret) |= side_effects;
va_end (p);
return ret;
}