gcc/libstdc++-v3/testsuite/util/testsuite_abi.cc
Jonathan Wakely 1352ea1925 libstdc++: Inline std::exception_ptr members [PR 90295]
This inlines most members of std::exception_ptr so that all operations
on a null exception_ptr can be optimized away. This benefits code like
std::future and coroutines where an exception_ptr object is present to
cope with exceptional cases, but is usually not used and remains null.

Since those functions were previously non-inline we have to continue to
export them from the library, for objects that were compiled against the
old headers and expect to find definitions in the library.

In order to inline the copy constructor and destructor we need to export
the _M_addref() and _M_release() members that increment/decrement the
reference count when copying/destroying a non-null exception_ptr. The
copy ctor and dtor check for null and don't call _M_addref and
_M_release unless they need to. The checks for null pointers in
_M_addref and _M_release are still needed because old code might call
them without checking for null first. But we can use __builtin_expect to
predict that they are usually called for the non-null case.

libstdc++-v3/ChangeLog:

	PR libstdc++/90295
	* config/abi/pre/gnu.ver (CXXABI_1.3.13): New symbol version.
	(exception_ptr::_M_addref(), exception_ptr::_M_release()):
	Export symbols.
	* libsupc++/eh_ptr.cc (exception_ptr::exception_ptr()):
	Remove out-of-line definition.
	(exception_ptr::exception_ptr(const exception_ptr&)):
	Likewise.
	(exception_ptr::~exception_ptr()): Likewise.
	(exception_ptr::operator=(const exception_ptr&)):
	Likewise.
	(exception_ptr::swap(exception_ptr&)): Likewise.
	(exception_ptr::_M_addref()): Add branch prediction.
	* libsupc++/exception_ptr.h (exception_ptr::operator bool):
	Add noexcept.
	[!_GLIBCXX_EH_PTR_COMPAT] (operator==, operator!=): Define
	inline as hidden friends. Remove declarations at namespace
	scope.
	(exception_ptr::exception_ptr()): Define inline.
	(exception_ptr::exception_ptr(const exception_ptr&)):
	Likewise.
	(exception_ptr::~exception_ptr()): Likewise.
	(exception_ptr::operator=(const exception_ptr&)):
	Likewise.
	(exception_ptr::swap(exception_ptr&)): Likewise.
	* testsuite/util/testsuite_abi.cc: Add CXXABI_1.3.13.
	* testsuite/18_support/exception_ptr/90295.cc: New test.
2020-10-06 17:24:16 +01:00

640 lines
17 KiB
C++

// -*- C++ -*-
// Copyright (C) 2004-2020 Free Software Foundation, Inc.
// This library 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.
// This library 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 this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// Benjamin Kosnik <bkoz@redhat.com>
#include "testsuite_abi.h"
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void
symbol::init(string& data)
{
const char delim = ':';
const char version_delim = '@';
const string::size_type npos = string::npos;
string::size_type n = 0;
// Set the type.
if (data.find("FUNC") == 0)
type = symbol::function;
else if (data.find("OBJECT") == 0)
type = symbol::object;
else if (data.find("TLS") == 0)
type = symbol::tls;
n = data.find_first_of(delim);
if (n != npos)
data.erase(data.begin(), data.begin() + n + 1);
// Iff object or TLS, get size info.
if (type == symbol::object || type == symbol::tls)
{
n = data.find_first_of(delim);
if (n != npos)
{
string objectsize(data.begin(), data.begin() + n);
istringstream iss(objectsize);
int x;
iss >> x;
if (!iss.fail())
size = x;
data.erase(data.begin(), data.begin() + n + 1);
}
}
// Set the name and raw_name.
raw_name = string(data.begin(), data.end());
n = data.find_first_of(version_delim);
if (n != npos)
{
// Found version string.
name = string(data.begin(), data.begin() + n);
n = data.find_last_of(version_delim);
data.erase(data.begin(), data.begin() + n + 1);
// Set version name.
version_name = data;
}
else
{
// No versioning info.
name = string(data.begin(), data.end());
version_status = symbol::none;
}
// Set the demangled name.
demangled_name = demangle(name);
}
void
symbol::print() const
{
const char tab = '\t';
cout << name << endl;
if (demangled_name != name)
cout << demangled_name << endl;
string vers;
switch (version_status)
{
case none:
vers = "none";
break;
case compatible:
vers = "compatible";
break;
case incompatible:
vers = "incompatible";
break;
case unversioned:
vers = "unversioned";
break;
default:
vers = "<default>";
}
cout << "version status: " << vers << endl;
if (version_name.size()
&& (version_status == compatible || version_status == incompatible))
cout << version_name << endl;
string type_string;
switch (type)
{
case function:
type_string = "function";
break;
case object:
type_string = "object";
break;
case tls:
type_string = "tls";
break;
case uncategorized:
type_string = "uncategorized";
break;
default:
type_string = "<default>";
}
cout << "type: " << type_string << endl;
if (type == object || type == tls)
cout << "type size: " << size << endl;
string status_string;
switch (status)
{
case added:
status_string = "added";
break;
case subtracted:
status_string = "subtracted";
break;
case undesignated:
status_string = "undesignated";
break;
default:
status_string = "<default>";
}
cout << "status: " << status_string << endl;
cout << endl;
}
bool
check_version(symbol& test, bool added)
{
// Construct list of compatible versions.
typedef std::vector<std::string> compat_list;
static compat_list known_versions;
if (known_versions.empty())
{
// NB: First version here must be the default version for this
// version of DT_SONAME.
known_versions.push_back("GLIBCXX_3.4");
known_versions.push_back("GLIBCXX_LDBL_3.4");
known_versions.push_back("GLIBCXX_3.4.1");
known_versions.push_back("GLIBCXX_3.4.2");
known_versions.push_back("GLIBCXX_3.4.3");
known_versions.push_back("GLIBCXX_3.4.4");
known_versions.push_back("GLIBCXX_3.4.5");
known_versions.push_back("GLIBCXX_3.4.6");
known_versions.push_back("GLIBCXX_3.4.7");
known_versions.push_back("GLIBCXX_LDBL_3.4.7");
known_versions.push_back("GLIBCXX_3.4.8");
known_versions.push_back("GLIBCXX_3.4.9");
known_versions.push_back("GLIBCXX_3.4.10");
known_versions.push_back("GLIBCXX_LDBL_3.4.10");
known_versions.push_back("GLIBCXX_3.4.11");
known_versions.push_back("GLIBCXX_3.4.12");
known_versions.push_back("GLIBCXX_3.4.13");
known_versions.push_back("GLIBCXX_3.4.14");
known_versions.push_back("GLIBCXX_3.4.15");
known_versions.push_back("GLIBCXX_3.4.16");
known_versions.push_back("GLIBCXX_3.4.17");
known_versions.push_back("GLIBCXX_3.4.18");
known_versions.push_back("GLIBCXX_3.4.19");
known_versions.push_back("GLIBCXX_3.4.20");
known_versions.push_back("GLIBCXX_3.4.21");
known_versions.push_back("GLIBCXX_LDBL_3.4.21");
known_versions.push_back("GLIBCXX_3.4.22");
known_versions.push_back("GLIBCXX_3.4.23");
known_versions.push_back("GLIBCXX_3.4.24");
known_versions.push_back("GLIBCXX_3.4.25");
known_versions.push_back("GLIBCXX_3.4.26");
known_versions.push_back("GLIBCXX_3.4.27");
known_versions.push_back("GLIBCXX_3.4.28");
known_versions.push_back("GLIBCXX_3.4.29");
known_versions.push_back("GLIBCXX_LDBL_3.4.29");
known_versions.push_back("CXXABI_1.3");
known_versions.push_back("CXXABI_LDBL_1.3");
known_versions.push_back("CXXABI_1.3.1");
known_versions.push_back("CXXABI_1.3.2");
known_versions.push_back("CXXABI_1.3.3");
known_versions.push_back("CXXABI_1.3.4");
known_versions.push_back("CXXABI_1.3.5");
known_versions.push_back("CXXABI_1.3.6");
known_versions.push_back("CXXABI_1.3.7");
known_versions.push_back("CXXABI_1.3.8");
known_versions.push_back("CXXABI_1.3.9");
known_versions.push_back("CXXABI_1.3.10");
known_versions.push_back("CXXABI_1.3.11");
known_versions.push_back("CXXABI_1.3.12");
known_versions.push_back("CXXABI_1.3.13");
known_versions.push_back("CXXABI_TM_1");
known_versions.push_back("CXXABI_FLOAT128");
}
compat_list::iterator begin = known_versions.begin();
compat_list::iterator end = known_versions.end();
// Check for compatible version.
if (test.version_name.size())
{
compat_list::iterator it1 = find(begin, end, test.version_name);
compat_list::iterator it2 = find(begin, end, test.name);
if (it1 != end)
test.version_status = symbol::compatible;
else
test.version_status = symbol::incompatible;
// Check that added symbols are added in the latest pre-release version.
bool latestp = (test.version_name == "GLIBCXX_3.4.29"
// XXX remove next line when GLIBCXX_3.4.30 is added and baselines
// have been regenerated to include GLIBCXX_LDBL_3.4.29 symbols:
|| test.version_name == "GLIBCXX_LDBL_3.4.29"
|| test.version_name == "CXXABI_1.3.13"
|| test.version_name == "CXXABI_FLOAT128"
|| test.version_name == "CXXABI_TM_1");
if (added && !latestp)
test.version_status = symbol::incompatible;
// Check that long double compatibility symbols demangled as
// __float128 and regular __float128 symbols are put into some _LDBL_
// or _FLOAT128 version name.
if (added && test.demangled_name.find("__float128") != std::string::npos
&& test.demangled_name.find("std::__cxx11::") != 0)
{
if (test.version_name.find("_LDBL_") == std::string::npos
&& test.version_name.find("_FLOAT128") == std::string::npos)
test.version_status = symbol::incompatible;
}
// Check for weak label.
if (it1 == end && it2 == end)
test.version_status = symbol::incompatible;
// Check that
// GLIBCXX_3.4
// GLIBCXX_3.4.5
// version as compatible
// XXX
}
else
{
if (added)
{
// New version labels are ok. The rest are not.
compat_list::iterator it2 = find(begin, end, test.name);
if (it2 != end)
test.version_status = symbol::compatible;
else
test.version_status = symbol::incompatible;
}
}
return test.version_status == symbol::compatible;
}
bool
check_compatible(symbol& lhs, symbol& rhs, bool verbose)
{
bool ret = true;
const char tab = '\t';
// Check to see if symbol_objects are compatible.
if (lhs.type != rhs.type)
{
ret = false;
if (verbose)
cout << tab << "incompatible types" << endl;
}
if (lhs.name != rhs.name)
{
ret = false;
if (verbose)
cout << tab << "incompatible names" << endl;
}
if (lhs.size != rhs.size)
{
ret = false;
if (verbose)
{
cout << tab << "incompatible sizes" << endl;
cout << tab << lhs.size << endl;
cout << tab << rhs.size << endl;
}
}
if (lhs.version_name != rhs.version_name
&& !check_version(lhs) && !check_version(rhs))
{
ret = false;
if (verbose)
{
cout << tab << "incompatible versions" << endl;
cout << tab << lhs.version_name << endl;
cout << tab << rhs.version_name << endl;
}
}
if (verbose)
cout << endl;
return ret;
}
inline bool
has_symbol(const string& name, const symbols& s) throw()
{ return s.find(name) != s.end(); }
const symbol&
get_symbol(const string& name, const symbols& s)
{
symbols::const_iterator i = s.find(name);
if (i != s.end())
{
return i->second;
}
else
{
ostringstream os;
os << "get_symbol failed for symbol " << name;
__throw_logic_error(os.str().c_str());
}
}
void
examine_symbol(const char* name, const char* file)
{
symbols s = create_symbols(file);
const symbol& sym = get_symbol(name, s);
sym.print();
}
int
compare_symbols(const char* baseline_file, const char* test_file,
bool verbose)
{
// Input both lists of symbols into container.
symbols baseline = create_symbols(baseline_file);
symbols test = create_symbols(test_file);
// Sanity check results.
if (!baseline.size() || !test.size())
{
cerr << "Problems parsing the list of exported symbols." << endl;
exit(2);
}
// Check to see if any long double compatibility symbols are produced.
bool ld_version_found(false);
symbols::iterator li(test.begin());
while (!ld_version_found && li != test.end())
{
if (li->second.version_name.find("_LDBL_") != std::string::npos)
ld_version_found = true;
++li;
}
// Sort out names.
// Assuming all baseline names and test names are both unique w/ no
// duplicates.
//
// The names added to missing_names are baseline names not found in
// test names
// -> symbols that have been deleted.
//
// The names added to added_names are test names not in
// baseline names
// -> symbols that have been added.
typedef std::vector<std::string> symbol_names;
symbol_names shared_names;
symbol_names missing_names;
symbol_names added_names;
for (li = test.begin(); li != test.end(); ++li)
added_names.push_back(li->first);
for (symbols::iterator i = baseline.begin(); i != baseline.end(); ++i)
{
string name(i->first);
symbol_names::iterator end = added_names.end();
symbol_names::iterator it = find(added_names.begin(), end, name);
if (it != end)
{
// Found.
shared_names.push_back(name);
added_names.erase(it);
}
else
{
// Iff no test long double compatibility symbols at all and the symbol
// missing is a baseline long double compatibility symbol, skip.
string version_name(i->second.version_name);
bool base_ld(version_name.find("_LDBL_") != std::string::npos);
if (!base_ld || base_ld && ld_version_found)
missing_names.push_back(name);
}
}
// Fill out list of incompatible symbols.
typedef pair<symbol, symbol> symbol_pair;
vector<symbol_pair> incompatible;
// Fill out list of undesignated symbols.
vector<symbol> undesignated;
// Check missing names for compatibility.
for (size_t j = 0; j < missing_names.size(); ++j)
{
symbol& sbase = baseline[missing_names[j]];
sbase.status = symbol::subtracted;
incompatible.push_back(symbol_pair(sbase, sbase));
}
// Check shared names for compatibility.
const symbol_names::size_type shared_size = shared_names.size();
for (size_t k = 0; k < shared_size; ++k)
{
symbol& sbase = baseline[shared_names[k]];
symbol& stest = test[shared_names[k]];
stest.status = symbol::existing;
if (!check_compatible(sbase, stest))
incompatible.push_back(symbol_pair(sbase, stest));
}
// Check added names for compatibility.
const symbol_names::size_type added_size = added_names.size();
for (size_t l = 0; l < added_size; ++l)
{
symbol& stest = test[added_names[l]];
// Mark TLS as undesignated, remove from added.
if (stest.type == symbol::tls)
{
stest.status = symbol::undesignated;
if (!check_version(stest, false))
incompatible.push_back(symbol_pair(stest, stest));
else
undesignated.push_back(stest);
}
else
{
stest.status = symbol::added;
if (!check_version(stest, true))
incompatible.push_back(symbol_pair(stest, stest));
}
}
// Normalize added names and undesignated names.
const size_t undesignated_size = undesignated.size();
for (size_t l = 0; l < undesignated_size; ++l)
{
symbol& sundes = undesignated[l];
symbol_names::iterator end = added_names.end();
symbol_names::iterator it = find(added_names.begin(), end, sundes.name);
if (it != end)
{
// Found.
added_names.erase(it);
}
else
__throw_runtime_error(sundes.name.c_str());
}
// Report results.
if (verbose && added_names.size())
{
cout << endl << added_names.size() << " added symbols " << endl;
for (size_t j = 0; j < added_names.size() ; ++j)
{
cout << j << endl;
test[added_names[j]].print();
}
}
if (verbose && missing_names.size())
{
cout << endl << missing_names.size() << " missing symbols " << endl;
for (size_t j = 0; j < missing_names.size() ; ++j)
{
cout << j << endl;
baseline[missing_names[j]].print();
}
}
if (verbose && undesignated.size())
{
cout << endl << undesignated.size() << " undesignated symbols " << endl;
for (size_t j = 0; j < undesignated.size() ; ++j)
{
// First, print index.
cout << j << endl;
// Second, report name.
symbol& s = undesignated[j];
s.print();
}
}
if (verbose && incompatible.size())
{
cout << endl << incompatible.size() << " incompatible symbols " << endl;
for (size_t j = 0; j < incompatible.size() ; ++j)
{
// First, print index.
cout << j << endl;
// Second, report name.
symbol& sbase = incompatible[j].first;
symbol& stest = incompatible[j].second;
stest.print();
// Third, report reason or reasons incompatible.
check_compatible(sbase, stest, true);
}
}
cout << "\n\t\t==== libstdc++-v3 check-abi Summary ====" << endl;
cout << endl;
cout << "# of added symbols:\t\t " << added_names.size() << endl;
cout << "# of missing symbols:\t\t " << missing_names.size() << endl;
cout << "# of undesignated symbols:\t " << undesignated.size() << endl;
cout << "# of incompatible symbols:\t " << incompatible.size() << endl;
cout << endl;
cout << "using: " << baseline_file << endl;
return !(missing_names.size() || incompatible.size());
}
symbols
create_symbols(const char* file)
{
symbols s;
ifstream ifs(file);
if (ifs.is_open())
{
// Organize file data into an associated container (symbols) of symbol
// objects mapped to mangled names without versioning
// information.
const string empty;
string line = empty;
while (getline(ifs, line).good())
{
symbol tmp;
tmp.init(line);
s[tmp.name] = tmp;
line = empty;
}
}
else
{
ostringstream os;
os << "create_symbols failed for file " << file;
__throw_runtime_error(os.str().c_str());
}
return s;
}
std::string
demangle(const std::string& mangled)
{
std::string name;
if (mangled[0] != '_' || mangled[1] != 'Z')
{
// This is not a mangled symbol, thus has "C" linkage.
name = mangled;
}
else
{
// Use __cxa_demangle to demangle.
int status = 0;
char* ptr = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status);
if (ptr)
{
name = ptr;
free(ptr);
}
else
{
switch (status)
{
case 0:
name = "error code = 0: success";
break;
case -1:
name = "error code = -1: memory allocation failure";
break;
case -2:
name = "error code = -2: invalid mangled name";
break;
case -3:
name = "error code = -3: invalid arguments";
break;
default:
name = "error code unknown - who knows what happened";
}
}
}
return name;
}