/* runtime.cc -- D runtime functions called by generated code. Copyright (C) 2006-2023 Free Software Foundation, Inc. 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 . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "dmd/aggregate.h" #include "dmd/mtype.h" #include "tree.h" #include "fold-const.h" #include "stringpool.h" #include "d-tree.h" /* During the codegen pass, the compiler may do lowering of expressions to call various runtime library functions. Most are implemented in the `rt' package. We represent them in the frontend here, however there's no guarantee that the compiler implementation actually matches the actual implementation. */ enum d_libcall_type { LCT_VOID, /* void */ LCT_BYTE, /* byte */ LCT_INT, /* int */ LCT_UINT, /* uint */ LCT_BOOL, /* bool */ LCT_DCHAR, /* dchar */ LCT_VOIDPTR, /* void* */ LCT_STRING, /* string */ LCT_WSTRING, /* wstring */ LCT_DSTRING, /* dstring */ LCT_SIZE_T, /* size_t */ LCT_ASSOCARRAY, /* void[void] */ LCT_ARRAY_VOID, /* void[] */ LCT_ARRAY_SIZE_T, /* size_t[] */ LCT_ARRAY_BYTE, /* byte[] */ LCT_IMMUTABLE_CHARPTR, /* immutable(char*) */ LCT_ARRAY_STRING, /* string[] */ LCT_ARRAY_WSTRING, /* wstring[] */ LCT_ARRAY_DSTRING, /* dstring[] */ LCT_ARRAYARRAY_BYTE, /* byte[][] */ LCT_POINTER_ASSOCARRAY, /* void[void]* */ LCT_POINTER_VOIDPTR, /* void** */ LCT_ARRAYPTR_VOID, /* void[]* */ LCT_ARRAYPTR_BYTE, /* byte[]* */ LCT_TYPEINFO, /* TypeInfo */ LCT_CLASSINFO, /* TypeInfo_Class */ LCT_OBJECT, /* Object */ LCT_CONST_TYPEINFO, /* const(TypeInfo) */ LCT_CONST_CLASSINFO, /* const(ClassInfo) */ LCT_END }; /* An array of all types that are used by the runtime functions we need. */ static Type *libcall_types[LCT_END]; /* Our internal list of library functions. */ static tree libcall_decls[LIBCALL_LAST]; /* Return the frontend Type that is described by TYPE. Most are readily cached by the frontend proper, and likewise the use of pointerTo(), constOf(), and arrayOf() will return cached types if they have been requested before. */ static Type * get_libcall_type (d_libcall_type type) { if (libcall_types[type]) return libcall_types[type]; switch (type) { case LCT_VOID: libcall_types[type] = Type::tvoid; break; case LCT_BYTE: libcall_types[type] = Type::tint8; break; case LCT_INT: libcall_types[type] = Type::tint32; break; case LCT_UINT: libcall_types[type] = Type::tuns32; break; case LCT_BOOL: libcall_types[type] = Type::tbool; break; case LCT_DCHAR: libcall_types[type] = Type::tdchar; break; case LCT_VOIDPTR: libcall_types[type] = Type::tvoidptr; break; case LCT_STRING: libcall_types[type] = Type::tstring; break; case LCT_WSTRING: libcall_types[type] = Type::twstring; break; case LCT_DSTRING: libcall_types[type] = Type::tdstring; break; case LCT_SIZE_T: libcall_types[type] = Type::tsize_t; break; case LCT_ASSOCARRAY: libcall_types[type] = TypeAArray::create (Type::tvoid, Type::tvoid); break; case LCT_TYPEINFO: libcall_types[type] = Type::dtypeinfo->type; break; case LCT_CLASSINFO: libcall_types[type] = Type::typeinfoclass->type; break; case LCT_OBJECT: libcall_types[type] = get_object_type (); break; case LCT_CONST_TYPEINFO: libcall_types[type] = Type::dtypeinfo->type->constOf (); break; case LCT_CONST_CLASSINFO: libcall_types[type] = Type::typeinfoclass->type->constOf (); break; case LCT_ARRAY_VOID: libcall_types[type] = Type::tvoid->arrayOf (); break; case LCT_ARRAY_SIZE_T: libcall_types[type] = Type::tsize_t->arrayOf (); break; case LCT_ARRAY_BYTE: libcall_types[type] = Type::tint8->arrayOf (); break; case LCT_ARRAY_STRING: libcall_types[type] = Type::tstring->arrayOf (); break; case LCT_ARRAY_WSTRING: libcall_types[type] = Type::twstring->arrayOf (); break; case LCT_ARRAY_DSTRING: libcall_types[type] = Type::tdstring->arrayOf (); break; case LCT_ARRAYARRAY_BYTE: libcall_types[type] = Type::tint8->arrayOf ()->arrayOf (); break; case LCT_POINTER_ASSOCARRAY: libcall_types[type] = get_libcall_type (LCT_ASSOCARRAY)->pointerTo (); break; case LCT_POINTER_VOIDPTR: libcall_types[type] = Type::tvoidptr->arrayOf (); break; case LCT_ARRAYPTR_VOID: libcall_types[type] = Type::tvoid->arrayOf ()->pointerTo (); break; case LCT_ARRAYPTR_BYTE: libcall_types[type] = Type::tint8->arrayOf ()->pointerTo (); break; case LCT_IMMUTABLE_CHARPTR: libcall_types[type] = Type::tchar->pointerTo ()->immutableOf (); break; default: gcc_unreachable (); } return libcall_types[type]; } /* Builds and returns 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, d_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++) { d_libcall_type ptype = (d_libcall_type) va_arg (ap, int); Type *type = get_libcall_type (ptype); if (type == Type::tvoid) { varargs = true; nparams = i; } else args[i] = build_ctype (type); } va_end (ap); /* Build the function. */ tree tret = build_ctype (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, however we depend on run-time initialization of types whose definitions are in the library such as `Object' or `TypeInfo'. */ static tree get_libcall (libcall_fn libcall) { if (libcall_decls[libcall]) return libcall_decls[libcall]; switch (libcall) { #define DEF_D_RUNTIME(CODE, NAME, TYPE, PARAMS, FLAGS) \ case LIBCALL_ ## CODE: \ libcall_decls[libcall] = build_libcall_decl (NAME, TYPE, FLAGS, PARAMS); \ break; #include "runtime.def" #undef DEF_D_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 build_libcall (libcall_fn libcall, Type *type, int nargs, ...) { /* Build the call expression to the runtime function. */ tree decl = 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 convert (build_ctype (type), result); }