blob: 190c16a9d15f246d6ac0cb0657ecf03e22c76375 [file] [log] [blame]
/* Libcalls to Algol 68 run-time functions.
Copyright (C) 2006-2026 Free Software Foundation, Inc.
Copyright (C) 2025 Jose E. Marchesi.
Written by Jose E. Marchesi.
Adapted from gcc/d/runtime.cc.
GCC 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.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "fold-const.h"
#include "langhooks.h"
#include "tm.h"
#include "function.h"
#include "cgraph.h"
#include "toplev.h"
#include "varasm.h"
#include "predict.h"
#include "stor-layout.h"
#include "tree-iterator.h"
#include "stringpool.h"
#include "print-tree.h"
#include "gimplify.h"
#include "dumpfile.h"
#include "convert.h"
#include "a68.h"
/* The lowering pass may generate expressions to call various runtime library
functions. Most of these functions are implemented in libga68. This file
provides facilities to compile libcalls to runtime functions. The file
a68-low-runtime.def contains a database of available runtime library
functions. */
enum a68_libcall_type
{
LCT_VOID,
LCT_CHAR,
LCT_CONSTCHARPTR,
LCT_VOIDPTR,
LCT_UNISTR,
LCT_UNISTRPTR,
LCT_SIZE,
LCT_SSIZE,
LCT_SIZEPTR,
LCT_UINT,
LCT_INT,
LCT_LONGLONGINT,
LCT_FLOAT,
LCT_DOUBLE,
LCT_LONGDOUBLE,
LCT_END
};
/* An array of all types that are used by the runtime functions we need. */
static tree libcall_types[LCT_END];
/* Internal list of library functions. */
static tree libcall_decls[A68_LIBCALL_LAST];
/* Return the TREE type that is described by TYPE. */
static tree
get_libcall_type (a68_libcall_type type)
{
if (libcall_types[type])
return libcall_types[type];
if (type == LCT_VOID)
libcall_types[type] = void_type_node;
else if (type == LCT_CHAR)
libcall_types[type] = uint32_type_node;
else if (type == LCT_CONSTCHARPTR)
libcall_types[type] = build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
else if (type == LCT_VOIDPTR)
libcall_types[type] = ptr_type_node;
else if (type == LCT_UNISTR)
libcall_types[type] = build_pointer_type (a68_char_type);
else if (type == LCT_UNISTRPTR)
libcall_types[type] = build_pointer_type (build_pointer_type (a68_char_type));
else if (type == LCT_SIZE)
libcall_types[type] = sizetype;
else if (type == LCT_SSIZE)
libcall_types[type] = ssizetype;
else if (type == LCT_SIZEPTR)
libcall_types[type] = build_pointer_type (sizetype);
else if (type == LCT_UINT)
libcall_types[type] = unsigned_type_node;
else if (type == LCT_INT)
libcall_types[type] = integer_type_node;
else if (type == LCT_LONGLONGINT)
libcall_types[type] = long_long_integer_type_node;
else if (type == LCT_FLOAT)
libcall_types[type] = float_type_node;
else if (type == LCT_DOUBLE)
libcall_types[type] = double_type_node;
else if (type == LCT_LONGDOUBLE)
libcall_types[type] = long_double_type_node;
else
gcc_unreachable ();
return libcall_types[type];
}
/* Build and return a function declaration named NAME. The RETURN_TYPE is the
type returned, FLAGS are the expression call flags, and NPARAMS is the
number of arguments, the types of which are provided in `...'. */
static tree
build_libcall_decl (const char *name, a68_libcall_type return_type,
int flags, int nparams, ...)
{
tree *args = XALLOCAVEC (tree, nparams);
bool varargs = false;
tree fntype;
/* Add parameter types, using `void' as the last parameter type
to mean this function accepts a variable list of arguments. */
va_list ap;
va_start (ap, nparams);
for (int i = 0; i < nparams; i++)
{
a68_libcall_type ptype = (a68_libcall_type) va_arg (ap, int);
tree type = get_libcall_type (ptype);
if (type == void_type_node)
{
varargs = true;
nparams = i;
}
else
args[i] = type;
}
va_end (ap);
/* Build the function. */
tree tret = get_libcall_type (return_type);
if (varargs)
fntype = build_varargs_function_type_array (tret, nparams, args);
else
fntype = build_function_type_array (tret, nparams, args);
tree decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,
get_identifier (name), fntype);
DECL_EXTERNAL (decl) = 1;
TREE_PUBLIC (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT;
DECL_VISIBILITY_SPECIFIED (decl) = 1;
/* Set any attributes on the function, such as malloc or noreturn. */
set_call_expr_flags (decl, flags);
return decl;
}
/* Return or create the runtime library function declaration for LIBCALL.
Library functions are generated as needed. This could probably be changed
in the future to be done in the compiler init stage, like GCC builtin trees
are. */
tree
a68_get_libcall (a68_libcall_fn libcall)
{
if (libcall_decls[libcall])
return libcall_decls[libcall];
switch (libcall)
{
#define DEF_A68_RUNTIME(CODE, NAME, TYPE, PARAMS, FLAGS) \
case A68_LIBCALL_ ## CODE: \
libcall_decls[libcall] = build_libcall_decl (NAME, TYPE, FLAGS, PARAMS); \
break;
#include "a68-low-runtime.def"
#undef DEF_A68_RUNTIME
default:
gcc_unreachable ();
}
return libcall_decls[libcall];
}
/* Generate a call to LIBCALL, returning the result as TYPE. NARGS is the
number of call arguments, the expressions of which are provided in `...'.
This does not perform conversions or promotions on the arguments. */
tree
a68_build_libcall (a68_libcall_fn libcall, tree type ATTRIBUTE_UNUSED,
int nargs, ...)
{
/* Build the call expression to the runtime function. */
tree decl = a68_get_libcall (libcall);
tree *args = XALLOCAVEC (tree, nargs);
va_list ap;
va_start (ap, nargs);
for (int i = 0; i < nargs; i++)
args[i] = va_arg (ap, tree);
va_end (ap);
tree result = build_call_expr_loc_array (input_location, decl, nargs, args);
/* Assumes caller knows what it is doing. */
return result;
}