From aa1afbbdb118846cfc266a1d7a8d5188ac214e0e Mon Sep 17 00:00:00 2001 From: Alexei Sheplyakov Date: Sat, 8 Aug 2009 11:43:12 +0300 Subject: [PATCH] Fix the compliation error *for real* ... and restore performance Commit 8bf0597dde55e4c94a2ff39f1d8130902e3d7a9b (titled as 'Fixed the parser such that it can read in user defined classes again.') made the parser a bit slower, especially if the input contains many terms of user-defined type. The reason for that is quite simple: we throw and catch an exception every time we construct an object of user-defined type: // dirty hack to distinguish between serial numbers of functions and real // pointers. GiNaC::function* f = NULL; try { unsigned serial = (unsigned)(unsigned long)(void *)(reader->second); f = new GiNaC::function(serial, args); } catch ( std::runtime_error ) { if ( f ) delete f; ex ret = reader->second(args); return ret; } Fortunately functions are aligned and we can use much more efficient technique to distinguish between serial and pointers to functions. --- ginac/parser/default_reader.tpl | 55 ++++++++++++++++++++++++--------- ginac/parser/parser.cpp | 47 ++++++++++++++++++++-------- 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/ginac/parser/default_reader.tpl b/ginac/parser/default_reader.tpl index 5f83cfee..2018e04a 100644 --- a/ginac/parser/default_reader.tpl +++ b/ginac/parser/default_reader.tpl @@ -19,6 +19,12 @@ COMMENT a part of GiNaC parser -- construct functions from a byte stream. #include "power.h" #include "operators.h" #include "inifcns.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_STDINT_H +#include // for uintptr_t +#endif namespace GiNaC { @@ -31,6 +37,31 @@ static ex [+ (get "name") +]_reader(const exvector& ev) (format '#f "~{ev[~a]~^, ~}" (sequence 0 (- nargs 1)))) +]); }[+ ENDFOR +] +// function::registered_functions() is protected, but we need to access it +class registered_functions_hack : public function +{ +public: + static const std::vector& get_registered_functions() + { + return function::registered_functions(); + } +private: + registered_functions_hack(); + registered_functions_hack(const registered_functions_hack&); + registered_functions_hack& operator=(const registered_functions_hack&); +}; + +// Encode an integer into a pointer to a function. Since functions +// are aligned (the minimal alignment depends on CPU architecture) +// we can distinguish between pointers and integers. +static reader_func encode_serial_as_reader_func(unsigned serial) +{ + uintptr_t u = (uintptr_t)serial; + u = (u << 1) | (uintptr_t)1; + reader_func ptr = reinterpret_cast((void *)u); + return ptr; +} + const prototype_table& get_default_reader() { using std::make_pair; @@ -42,22 +73,16 @@ const prototype_table& get_default_reader() (if (exist? "args") (get "args") "1") +])] = [+ (get "name") +]_reader;[+ ENDFOR +] - try { - for ( unsigned ser=0; ; ++ser ) { - GiNaC::function f(ser); - std::string name = f.get_name(); - for ( std::size_t nargs=0; ; ++nargs ) { - try { - function::find_function(name, nargs); - prototype proto = std::pair(name, nargs); - std::pair ins = reader.insert(std::pair(proto, (reader_func)ser)); - if ( ins.second ) break; - } - catch ( std::runtime_error ) { } - } - } + std::vector::const_iterator it = + registered_functions_hack::get_registered_functions().begin(); + std::vector::const_iterator end = + registered_functions_hack::get_registered_functions().end(); + unsigned serial = 0; + for (; it != end; ++it) { + prototype proto = make_pair(it->get_name(), it->get_nparams()); + reader[proto] = encode_serial_as_reader_func(serial); + ++serial; } - catch ( std::runtime_error ) { } initialized = true; } return reader; diff --git a/ginac/parser/parser.cpp b/ginac/parser/parser.cpp index cfa313b4..acf5f3d0 100644 --- a/ginac/parser/parser.cpp +++ b/ginac/parser/parser.cpp @@ -32,6 +32,36 @@ namespace GiNaC { +// +// Find out if ptr is a pointer to a function or a specially crafted integer. +// It's possible to distinguish between these because functions are aligned. +// Returns true if ptr is a pointer and false otherwise. +static bool decode_serial(unsigned& serial, const reader_func ptr) +{ + uintptr_t u = (uintptr_t)(void *)ptr; + if (u & 1) { + u >>= 1; + serial = (unsigned)u; + return false; + } + return true; +} + +// Figures out if ptr is a pointer to function or a serial of GiNaC function. +// In the former case calls that function, in the latter case constructs +// GiNaC function with corresponding serial and arguments. +static ex dispatch_reader_fcn(const reader_func ptr, const exvector& args) +{ + unsigned serial = 0; // dear gcc, could you please shut up? + bool is_ptr = decode_serial(serial, ptr); + if (is_ptr) + return ptr(args); + else + return function(serial, args); +} +// + + /// identifier_expr: identifier | identifier '(' expression* ')' ex parser::parse_identifier_expr() { @@ -66,19 +96,10 @@ ex parser::parse_identifier_expr() Parse_error_("no function \"" << name << "\" with " << args.size() << " arguments"); } - // dirty hack to distinguish between serial numbers of functions and real - // pointers. - GiNaC::function* f = NULL; - try { - unsigned serial = (unsigned)(unsigned long)(void *)(reader->second); - f = new GiNaC::function(serial, args); - } - catch ( std::runtime_error ) { - if ( f ) delete f; - ex ret = reader->second(args); - return ret; - } - return f->setflag(status_flags::dynallocated); + // reader->second might be a pointer to a C++ function or a specially + // crafted serial of a GiNaC::function. + ex ret = dispatch_reader_fcn(reader->second, args); + return ret; } /// paren_expr: '(' expression ')' -- 2.44.0