mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			336 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			336 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* Copyright (C) 2008-2014 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"
 | |
| 
 | |
| // Avoid a dependency on libstdc++ for the pure virtuals in abi_dispatch.
 | |
| extern "C" void HIDDEN
 | |
| __cxa_pure_virtual ()
 | |
| {
 | |
|   abort ();
 | |
| }
 | |
| 
 | |
| using namespace GTM;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| // This group consists of the serial, serialirr, and serialirr_onwrite
 | |
| // methods, which all need no global state (except what is already provided
 | |
| // by the serial mode implementation).
 | |
| struct serial_mg : public method_group
 | |
| {
 | |
|   virtual void init() { }
 | |
|   virtual void fini() { }
 | |
| };
 | |
| 
 | |
| static serial_mg o_serial_mg;
 | |
| 
 | |
| 
 | |
| class serialirr_dispatch : public abi_dispatch
 | |
| {
 | |
|  public:
 | |
|   serialirr_dispatch() : abi_dispatch(false, true, true, false,
 | |
|       gtm_thread::STATE_SERIAL | gtm_thread::STATE_IRREVOCABLE, &o_serial_mg)
 | |
|   { }
 | |
| 
 | |
|  protected:
 | |
|   serialirr_dispatch(bool ro, bool wt, bool uninstrumented,
 | |
|       bool closed_nesting, uint32_t requires_serial, method_group* mg) :
 | |
|     abi_dispatch(ro, wt, uninstrumented, closed_nesting, requires_serial, mg)
 | |
|   { }
 | |
| 
 | |
|   // Transactional loads and stores simply access memory directly.
 | |
|   // These methods are static to avoid indirect calls, and will be used by the
 | |
|   // virtual ABI dispatch methods or by static direct-access methods created
 | |
|   // below.
 | |
|   template <typename V> static V load(const V* addr, ls_modifier mod)
 | |
|   {
 | |
|     return *addr;
 | |
|   }
 | |
|   template <typename V> static void store(V* addr, const V value,
 | |
|       ls_modifier mod)
 | |
|   {
 | |
|     *addr = value;
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   static void memtransfer_static(void *dst, const void* src, size_t size,
 | |
|       bool may_overlap, ls_modifier dst_mod, ls_modifier src_mod)
 | |
|   {
 | |
|     if (!may_overlap)
 | |
|       ::memcpy(dst, src, size);
 | |
|     else
 | |
|       ::memmove(dst, src, size);
 | |
|   }
 | |
| 
 | |
|   static void memset_static(void *dst, int c, size_t size, ls_modifier mod)
 | |
|   {
 | |
|     ::memset(dst, c, size);
 | |
|   }
 | |
| 
 | |
|   CREATE_DISPATCH_METHODS(virtual, )
 | |
|   CREATE_DISPATCH_METHODS_MEM()
 | |
| 
 | |
|   virtual gtm_restart_reason begin_or_restart() { return NO_RESTART; }
 | |
|   virtual bool trycommit(gtm_word& priv_time) { return true; }
 | |
|   virtual void rollback(gtm_transaction_cp *cp) { abort(); }
 | |
| 
 | |
|   virtual abi_dispatch* closed_nesting_alternative()
 | |
|   {
 | |
|     // For nested transactions with an instrumented code path, we can do
 | |
|     // undo logging.
 | |
|     return GTM::dispatch_serial();
 | |
|   }
 | |
| };
 | |
| 
 | |
| class serial_dispatch : public abi_dispatch
 | |
| {
 | |
| protected:
 | |
|   static void log(const void *addr, size_t len)
 | |
|   {
 | |
|     gtm_thread *tx = gtm_thr();
 | |
|     tx->undolog.log(addr, len);
 | |
|   }
 | |
| 
 | |
|   template <typename V> static V load(const V* addr, ls_modifier mod)
 | |
|   {
 | |
|     return *addr;
 | |
|   }
 | |
|   template <typename V> static void store(V* addr, const V value,
 | |
|       ls_modifier mod)
 | |
|   {
 | |
|     if (mod != WaW)
 | |
|       log(addr, sizeof(V));
 | |
|     *addr = value;
 | |
|   }
 | |
| 
 | |
| public:
 | |
|   static void memtransfer_static(void *dst, const void* src, size_t size,
 | |
|       bool may_overlap, ls_modifier dst_mod, ls_modifier src_mod)
 | |
|   {
 | |
|     if (dst_mod != WaW && dst_mod != NONTXNAL)
 | |
|       log(dst, size);
 | |
|     if (!may_overlap)
 | |
|       ::memcpy(dst, src, size);
 | |
|     else
 | |
|       ::memmove(dst, src, size);
 | |
|   }
 | |
| 
 | |
|   static void memset_static(void *dst, int c, size_t size, ls_modifier mod)
 | |
|   {
 | |
|     if (mod != WaW)
 | |
|       log(dst, size);
 | |
|     ::memset(dst, c, size);
 | |
|   }
 | |
| 
 | |
|   virtual gtm_restart_reason begin_or_restart() { return NO_RESTART; }
 | |
|   virtual bool trycommit(gtm_word& priv_time) { return true; }
 | |
|   // Local undo will handle this.
 | |
|   // trydropreference() need not be changed either.
 | |
|   virtual void rollback(gtm_transaction_cp *cp) { }
 | |
| 
 | |
|   CREATE_DISPATCH_METHODS(virtual, )
 | |
|   CREATE_DISPATCH_METHODS_MEM()
 | |
| 
 | |
|   serial_dispatch() : abi_dispatch(false, true, false, true,
 | |
|       gtm_thread::STATE_SERIAL, &o_serial_mg)
 | |
|   { }
 | |
| };
 | |
| 
 | |
| 
 | |
| // Like serialirr_dispatch but does not requests serial-irrevocable mode until
 | |
| // the first write in the transaction. Can be useful for read-mostly workloads
 | |
| // and testing, but is likely too simple to be of general purpose.
 | |
| class serialirr_onwrite_dispatch : public serialirr_dispatch
 | |
| {
 | |
|  public:
 | |
|   serialirr_onwrite_dispatch() :
 | |
|     serialirr_dispatch(false, true, false, false, 0, &o_serial_mg) { }
 | |
| 
 | |
|  protected:
 | |
|   static void pre_write()
 | |
|   {
 | |
|     gtm_thread *tx = gtm_thr();
 | |
|     if (!(tx->state & (gtm_thread::STATE_SERIAL
 | |
| 	| gtm_thread::STATE_IRREVOCABLE)))
 | |
|       tx->serialirr_mode();
 | |
|   }
 | |
| 
 | |
|   // Transactional loads access memory directly.
 | |
|   // Transactional stores switch to serial mode first.
 | |
|   template <typename V> static void store(V* addr, const V value,
 | |
|       ls_modifier mod)
 | |
|   {
 | |
|     pre_write();
 | |
|     serialirr_dispatch::store(addr, value, mod);
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   static void memtransfer_static(void *dst, const void* src, size_t size,
 | |
|       bool may_overlap, ls_modifier dst_mod, ls_modifier src_mod)
 | |
|   {
 | |
|     pre_write();
 | |
|     serialirr_dispatch::memtransfer_static(dst, src, size, may_overlap,
 | |
| 	dst_mod, src_mod);
 | |
|   }
 | |
| 
 | |
|   static void memset_static(void *dst, int c, size_t size, ls_modifier mod)
 | |
|   {
 | |
|     pre_write();
 | |
|     serialirr_dispatch::memset_static(dst, c, size, mod);
 | |
|   }
 | |
| 
 | |
|   CREATE_DISPATCH_METHODS(virtual, )
 | |
|   CREATE_DISPATCH_METHODS_MEM()
 | |
| 
 | |
|   virtual void rollback(gtm_transaction_cp *cp)
 | |
|   {
 | |
|     gtm_thread *tx = gtm_thr();
 | |
|     if (tx->state & gtm_thread::STATE_IRREVOCABLE)
 | |
|       abort();
 | |
|   }
 | |
| };
 | |
| 
 | |
| // This group is pure HTM with serial mode as a fallback.  There is no
 | |
| // difference to serial_mg except that we need to enable or disable the HTM
 | |
| // fastpath.  See gtm_thread::begin_transaction.
 | |
| struct htm_mg : public method_group
 | |
| {
 | |
|   virtual void init()
 | |
|   {
 | |
|     // Enable the HTM fastpath if the HW is available.  The fastpath is
 | |
|     // initially disabled.
 | |
| #ifdef USE_HTM_FASTPATH
 | |
|     htm_fastpath = htm_init();
 | |
| #endif
 | |
|   }
 | |
|   virtual void fini()
 | |
|   {
 | |
|     // Disable the HTM fastpath.
 | |
|     htm_fastpath = 0;
 | |
|   }
 | |
| };
 | |
| 
 | |
| static htm_mg o_htm_mg;
 | |
| 
 | |
| // We just need the subclass to associate it with the HTM method group that
 | |
| // sets up the HTM fast path.  This will use serial_dispatch as fallback for
 | |
| // transactions that might get canceled; it has a different method group, but
 | |
| // this is harmless for serial dispatchs because they never abort.
 | |
| class htm_dispatch : public serialirr_dispatch
 | |
| {
 | |
|  public:
 | |
|   htm_dispatch() : serialirr_dispatch(false, true, false, false,
 | |
|       gtm_thread::STATE_SERIAL | gtm_thread::STATE_IRREVOCABLE, &o_htm_mg)
 | |
|   { }
 | |
| };
 | |
| 
 | |
| } // anon namespace
 | |
| 
 | |
| static const serialirr_dispatch o_serialirr_dispatch;
 | |
| static const serial_dispatch o_serial_dispatch;
 | |
| static const serialirr_onwrite_dispatch o_serialirr_onwrite_dispatch;
 | |
| static const htm_dispatch o_htm_dispatch;
 | |
| 
 | |
| abi_dispatch *
 | |
| GTM::dispatch_serialirr ()
 | |
| {
 | |
|   return const_cast<serialirr_dispatch *>(&o_serialirr_dispatch);
 | |
| }
 | |
| 
 | |
| abi_dispatch *
 | |
| GTM::dispatch_serial ()
 | |
| {
 | |
|   return const_cast<serial_dispatch *>(&o_serial_dispatch);
 | |
| }
 | |
| 
 | |
| abi_dispatch *
 | |
| GTM::dispatch_serialirr_onwrite ()
 | |
| {
 | |
|   return
 | |
|       const_cast<serialirr_onwrite_dispatch *>(&o_serialirr_onwrite_dispatch);
 | |
| }
 | |
| 
 | |
| abi_dispatch *
 | |
| GTM::dispatch_htm ()
 | |
| {
 | |
|   return const_cast<htm_dispatch *>(&o_htm_dispatch);
 | |
| }
 | |
| 
 | |
| // Put the transaction into serial-irrevocable mode.
 | |
| 
 | |
| void
 | |
| GTM::gtm_thread::serialirr_mode ()
 | |
| {
 | |
|   struct abi_dispatch *disp = abi_disp ();
 | |
| 
 | |
| #if defined(USE_HTM_FASTPATH)
 | |
|   // HTM fastpath.  If we are executing a HW transaction, don't go serial but
 | |
|   // continue.  See gtm_thread::begin_transaction.
 | |
|   if (likely(htm_fastpath && !gtm_thread::serial_lock.is_write_locked()))
 | |
|     return;
 | |
| #endif
 | |
| 
 | |
|   if (this->state & STATE_SERIAL)
 | |
|     {
 | |
|       if (this->state & STATE_IRREVOCABLE)
 | |
| 	return;
 | |
| 
 | |
|       // Try to commit the dispatch-specific part of the transaction, as we
 | |
|       // would do for an outermost commit.
 | |
|       // We're already serial, so we don't need to ensure privatization safety
 | |
|       // for other transactions here.
 | |
|       gtm_word priv_time = 0;
 | |
|       bool ok = disp->trycommit (priv_time);
 | |
|       // Given that we're already serial, the trycommit better work.
 | |
|       assert (ok);
 | |
|     }
 | |
|   else if (serial_lock.write_upgrade (this))
 | |
|     {
 | |
|       this->state |= STATE_SERIAL;
 | |
|       // Try to commit the dispatch-specific part of the transaction, as we
 | |
|       // would do for an outermost commit.
 | |
|       // We have successfully upgraded to serial mode, so we don't need to
 | |
|       // ensure privatization safety for other transactions here.
 | |
|       // However, we are still a reader (wrt. privatization safety) until we
 | |
|       // have either committed or restarted, so finish the upgrade after that.
 | |
|       gtm_word priv_time = 0;
 | |
|       if (!disp->trycommit (priv_time))
 | |
|         restart (RESTART_SERIAL_IRR, true);
 | |
|       gtm_thread::serial_lock.write_upgrade_finish(this);
 | |
|     }
 | |
|   else
 | |
|     restart (RESTART_SERIAL_IRR, false);
 | |
| 
 | |
|   this->state |= (STATE_SERIAL | STATE_IRREVOCABLE);
 | |
|   set_abi_disp (dispatch_serialirr ());
 | |
| }
 | |
| 
 | |
| void ITM_REGPARM
 | |
| _ITM_changeTransactionMode (_ITM_transactionState state)
 | |
| {
 | |
|   assert (state == modeSerialIrrevocable);
 | |
|   gtm_thr()->serialirr_mode ();
 | |
| }
 |