|  | /* Copyright (C) 2009-2025 Free Software Foundation, Inc. | 
|  | Contributed by Richard Henderson <rth@redhat.com>. | 
|  |  | 
|  | This file is part of the GNU Transactional Memory Library (libitm). | 
|  |  | 
|  | Libitm 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 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | Libitm 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. | 
|  |  | 
|  | Under Section 7 of GPL version 3, you are granted additional | 
|  | permissions described in the GCC Runtime Library Exception, version | 
|  | 3.1, as published by the Free Software Foundation. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License and | 
|  | a copy of the GCC Runtime Library Exception along with this program; | 
|  | see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "libitm_i.h" | 
|  |  | 
|  | using namespace GTM; | 
|  |  | 
|  | struct clone_entry | 
|  | { | 
|  | void *orig, *clone; | 
|  | }; | 
|  |  | 
|  | struct clone_table | 
|  | { | 
|  | clone_entry *table; | 
|  | size_t size; | 
|  | clone_table *next; | 
|  | }; | 
|  |  | 
|  | static clone_table *all_tables; | 
|  |  | 
|  | static void * | 
|  | find_clone (void *ptr) | 
|  | { | 
|  | clone_table *table; | 
|  |  | 
|  | for (table = all_tables; table ; table = table->next) | 
|  | { | 
|  | clone_entry *t = table->table; | 
|  | size_t lo = 0, hi = table->size, i; | 
|  |  | 
|  | /* Quick test for whether PTR is present in this table.  */ | 
|  | if (ptr < t[0].orig || ptr > t[hi - 1].orig) | 
|  | continue; | 
|  |  | 
|  | /* Otherwise binary search.  */ | 
|  | while (lo < hi) | 
|  | { | 
|  | i = (lo + hi) / 2; | 
|  | if (ptr < t[i].orig) | 
|  | hi = i; | 
|  | else if (ptr > t[i].orig) | 
|  | lo = i + 1; | 
|  | else | 
|  | return t[i].clone; | 
|  | } | 
|  |  | 
|  | /* Given the quick test above, if we don't find the entry in | 
|  | this table then it doesn't exist.  */ | 
|  | break; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | void * ITM_REGPARM | 
|  | _ITM_getTMCloneOrIrrevocable (void *ptr) | 
|  | { | 
|  | void *ret = find_clone (ptr); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | gtm_thr()->serialirr_mode (); | 
|  |  | 
|  | return ptr; | 
|  | } | 
|  |  | 
|  | void * ITM_REGPARM | 
|  | _ITM_getTMCloneSafe (void *ptr) | 
|  | { | 
|  | void *ret = find_clone (ptr); | 
|  | if (ret == NULL) | 
|  | abort (); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int | 
|  | clone_entry_compare (const void *a, const void *b) | 
|  | { | 
|  | const clone_entry *aa = (const clone_entry *)a; | 
|  | const clone_entry *bb = (const clone_entry *)b; | 
|  |  | 
|  | if (aa->orig < bb->orig) | 
|  | return -1; | 
|  | else if (aa->orig > bb->orig) | 
|  | return 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Within find_clone, we know that we are inside a transaction.  Because | 
|  | // of that, we have already synchronized with serial_lock.  By taking the | 
|  | // serial_lock for write, we exclude all transactions while we make this | 
|  | // change to the clone tables, without having to synchronize on a separate | 
|  | // lock.  Do be careful not to attempt a recursive write lock. | 
|  |  | 
|  | class ExcludeTransaction | 
|  | { | 
|  | bool do_lock; | 
|  |  | 
|  | public: | 
|  | ExcludeTransaction() | 
|  | { | 
|  | gtm_thread *tx = gtm_thr(); | 
|  | do_lock = !(tx && (tx->state & gtm_thread::STATE_SERIAL)); | 
|  |  | 
|  | if (do_lock) | 
|  | gtm_thread::serial_lock.write_lock (); | 
|  | } | 
|  |  | 
|  | ~ExcludeTransaction() | 
|  | { | 
|  | if (do_lock) | 
|  | gtm_thread::serial_lock.write_unlock (); | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // end anon namespace | 
|  |  | 
|  |  | 
|  | void | 
|  | _ITM_registerTMCloneTable (void *xent, size_t size) | 
|  | { | 
|  | clone_entry *ent = static_cast<clone_entry *>(xent); | 
|  | clone_table *table; | 
|  |  | 
|  | table = (clone_table *) xmalloc (sizeof (clone_table)); | 
|  | table->table = ent; | 
|  | table->size = size; | 
|  |  | 
|  | qsort (ent, size, sizeof (clone_entry), clone_entry_compare); | 
|  |  | 
|  | // Hold the serial_lock while we update the ALL_TABLES datastructure. | 
|  | { | 
|  | ExcludeTransaction exclude; | 
|  | table->next = all_tables; | 
|  | all_tables = table; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | _ITM_deregisterTMCloneTable (void *xent) | 
|  | { | 
|  | clone_entry *ent = static_cast<clone_entry *>(xent); | 
|  | clone_table *tab; | 
|  |  | 
|  | // Hold the serial_lock while we update the ALL_TABLES datastructure. | 
|  | { | 
|  | ExcludeTransaction exclude; | 
|  | clone_table **pprev; | 
|  |  | 
|  | for (pprev = &all_tables; | 
|  | tab = *pprev, tab->table != ent; | 
|  | pprev = &tab->next) | 
|  | continue; | 
|  | *pprev = tab->next; | 
|  | } | 
|  |  | 
|  | free (tab); | 
|  | } |