1 /** @file excompiler.cpp
3 * Functions to facilitate the conversion of a ex to a function pointer suited for
4 * fast numerical integration.
9 * GiNaC Copyright (C) 1999-2020 Johannes Gutenberg University Mainz, Germany
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "excompiler.h"
34 #include "operators.h"
35 #include "relational.h"
40 #endif // def HAVE_LIBDL
45 # include <io.h> // for close(3)
46 # endif // def _MSC_VER
47 #endif // def HAVE_UNISTD_H
61 * Small class that manages modules opened by libdl. It is used by compile_ex
62 * and link_ex in order to have a clean-up of opened modules and their
63 * associated source and so-files at the time of program termination. It is
64 * supposed to be statically instantiated once (see definition of
65 * global_excompiler below). The dtor of that object then performs the
66 * clean-up. On top of that it provides some functionality shared between
67 * different compile_ex and link_ex specializations.
72 * Holds all necessary information about opened modules.
77 std::string name; /**< filename with .so suffix */
78 bool clean_up; /**< if true, source and so-file will be deleted */
80 std::vector<filedesc> filelist; /**< List of all opened modules */
83 * Complete clean-up of opened modules is done on destruction.
87 for (auto it = filelist.begin(); it != filelist.end(); ++it) {
92 * Adds a new module to the list.
94 void add_opened_module(void* module, const std::string& name, bool clean_up)
99 fd.clean_up = clean_up;
100 filelist.push_back(fd);
103 * Closes a module and deletes the so-file if the associated clean-up flag is true.
105 void clean_up(const std::vector<filedesc>::const_iterator it)
109 remove(it->name.c_str());
113 * Creates a new C source file and adds a standard header. If filename is
114 * empty, a unique random name is produced and used.
116 void create_src_file(std::string& filename, std::ofstream& ofs)
118 if (filename.empty()) {
119 // fill filename with unique random word
120 const char* filename_pattern = "./GiNaCXXXXXX";
121 char* new_filename = new char[strlen(filename_pattern)+1];
122 strcpy(new_filename, filename_pattern);
123 int fd = mkstemp(new_filename);
125 delete[] new_filename;
126 throw std::runtime_error("mkstemp failed");
128 filename = std::string(new_filename);
129 ofs.open(new_filename, std::ios::out);
131 delete[] new_filename;
133 // use parameter as filename
134 ofs.open(filename.c_str(), std::ios::out);
138 throw std::runtime_error("could not create source code file for compilation");
141 ofs << "#include <stddef.h> " << std::endl;
142 ofs << "#include <stdlib.h> " << std::endl;
143 ofs << "#include <math.h> " << std::endl;
147 * Calls the shell script 'ginac-excompiler' to compile the produced C
148 * source file into an linkable so-file. On demand the C source file is
151 void compile_src_file(const std::string filename, bool clean_up)
153 std::string strcompile = LIBEXECDIR "ginac-excompiler " + filename;
154 if (system(strcompile.c_str())) {
155 throw std::runtime_error("excompiler::compile_src_file: error compiling source file!");
158 remove(filename.c_str());
162 * Links a so-file whose filename is given.
164 void* link_so_file(const std::string filename, bool clean_up)
166 void* module = nullptr;
167 module = dlopen(filename.c_str(), RTLD_NOW);
168 if (module == nullptr) {
169 throw std::runtime_error("excompiler::link_so_file: could not open compiled module!");
172 add_opened_module(module, filename, clean_up);
174 return dlsym(module, "compiled_ex");
177 * Removes a modules from the module list. Performs a clean-up before that.
178 * Every module with the given name will be affected.
180 void unlink(const std::string filename)
182 for (auto it = filelist.begin(); it != filelist.end();) {
183 if (it->name == filename) {
185 it = filelist.erase(it);
194 * This static object manages the modules opened by the compile_ex and link_ex
195 * functions. On program termination its dtor is called and all open modules
196 * are closed. The associated source and so-files are eventually deleted then
198 * In principle this could lead to a static deconstruction order fiasco, if
199 * other code from this library uses the compile_ex and link_ex functions
200 * (which it doesn't at the moment and won't in the likely future, so therefore
201 * we ignore this issue).
203 static excompiler global_excompiler;
205 void compile_ex(const ex& expr, const symbol& sym, FUNCP_1P& fp, const std::string filename)
208 ex expr_with_x = expr.subs(lst{sym==x});
211 std::string unique_filename = filename;
212 global_excompiler.create_src_file(unique_filename, ofs);
214 ofs << "double compiled_ex(double x)" << std::endl;
215 ofs << "{" << std::endl;
216 ofs << "double res = ";
217 expr_with_x.print(GiNaC::print_csrc_double(ofs));
218 ofs << ";" << std::endl;
219 ofs << "return(res); " << std::endl;
220 ofs << "}" << std::endl;
224 global_excompiler.compile_src_file(unique_filename, filename.empty());
225 // This is not standard compliant! ... no conversion between
226 // pointer-to-functions and pointer-to-objects ...
227 fp = (FUNCP_1P) global_excompiler.link_so_file(unique_filename+".so", filename.empty());
230 void compile_ex(const ex& expr, const symbol& sym1, const symbol& sym2, FUNCP_2P& fp, const std::string filename)
232 symbol x("x"), y("y");
233 ex expr_with_xy = expr.subs(lst{sym1==x, sym2==y});
236 std::string unique_filename = filename;
237 global_excompiler.create_src_file(unique_filename, ofs);
239 ofs << "double compiled_ex(double x, double y)" << std::endl;
240 ofs << "{" << std::endl;
241 ofs << "double res = ";
242 expr_with_xy.print(GiNaC::print_csrc_double(ofs));
243 ofs << ";" << std::endl;
244 ofs << "return(res); " << std::endl;
245 ofs << "}" << std::endl;
249 global_excompiler.compile_src_file(unique_filename, filename.empty());
250 // This is not standard compliant! ... no conversion between
251 // pointer-to-functions and pointer-to-objects ...
252 fp = (FUNCP_2P) global_excompiler.link_so_file(unique_filename+".so", filename.empty());
255 void compile_ex(const lst& exprs, const lst& syms, FUNCP_CUBA& fp, const std::string filename)
258 for (std::size_t count=0; count<syms.nops(); ++count) {
259 std::ostringstream s;
260 s << "a[" << count << "]";
261 replacements.append(syms.op(count) == symbol(s.str()));
264 std::vector<ex> expr_with_cname;
265 for (std::size_t count=0; count<exprs.nops(); ++count) {
266 expr_with_cname.push_back(exprs.op(count).subs(replacements));
270 std::string unique_filename = filename;
271 global_excompiler.create_src_file(unique_filename, ofs);
273 ofs << "void compiled_ex(const int* an, const double a[], const int* fn, double f[])" << std::endl;
274 ofs << "{" << std::endl;
275 for (std::size_t count=0; count<exprs.nops(); ++count) {
276 ofs << "f[" << count << "] = ";
277 expr_with_cname[count].print(GiNaC::print_csrc_double(ofs));
278 ofs << ";" << std::endl;
280 ofs << "}" << std::endl;
284 global_excompiler.compile_src_file(unique_filename, filename.empty());
285 // This is not standard compliant! ... no conversion between
286 // pointer-to-functions and pointer-to-objects ...
287 fp = (FUNCP_CUBA) global_excompiler.link_so_file(unique_filename+".so", filename.empty());
290 void link_ex(const std::string filename, FUNCP_1P& fp)
292 // This is not standard compliant! ... no conversion between
293 // pointer-to-functions and pointer-to-objects ...
294 fp = (FUNCP_1P) global_excompiler.link_so_file(filename, false);
297 void link_ex(const std::string filename, FUNCP_2P& fp)
299 // This is not standard compliant! ... no conversion between
300 // pointer-to-functions and pointer-to-objects ...
301 fp = (FUNCP_2P) global_excompiler.link_so_file(filename, false);
304 void link_ex(const std::string filename, FUNCP_CUBA& fp)
306 // This is not standard compliant! ... no conversion between
307 // pointer-to-functions and pointer-to-objects ...
308 fp = (FUNCP_CUBA) global_excompiler.link_so_file(filename, false);
311 void unlink_ex(const std::string filename)
313 global_excompiler.unlink(filename);
316 #else // def HAVE_LIBDL
319 * In case no working libdl has been found by configure, the following function
320 * stubs preserve the interface. Every function just raises an exception.
323 void compile_ex(const ex& expr, const symbol& sym, FUNCP_1P& fp, const std::string filename)
325 throw std::runtime_error("compile_ex has been disabled because of missing libdl!");
328 void compile_ex(const ex& expr, const symbol& sym1, const symbol& sym2, FUNCP_2P& fp, const std::string filename)
330 throw std::runtime_error("compile_ex has been disabled because of missing libdl!");
333 void compile_ex(const lst& exprs, const lst& syms, FUNCP_CUBA& fp, const std::string filename)
335 throw std::runtime_error("compile_ex has been disabled because of missing libdl!");
338 void link_ex(const std::string filename, FUNCP_1P& fp)
340 throw std::runtime_error("link_ex has been disabled because of missing libdl!");
343 void link_ex(const std::string filename, FUNCP_2P& fp)
345 throw std::runtime_error("link_ex has been disabled because of missing libdl!");
348 void link_ex(const std::string filename, FUNCP_CUBA& fp)
350 throw std::runtime_error("link_ex has been disabled because of missing libdl!");
353 void unlink_ex(const std::string filename)
355 throw std::runtime_error("unlink_ex has been disabled because of missing libdl!");
358 #endif // def HAVE_LIBDL