mirror of git://gcc.gnu.org/git/gcc.git
366 lines
10 KiB
C++
366 lines
10 KiB
C++
/* Copyright (C) 2008, 2009, 2011 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;
|
|
|
|
static void
|
|
do_memcpy (uintptr_t idst, uintptr_t isrc, size_t size,
|
|
abi_dispatch::lock_type W, abi_dispatch::lock_type R)
|
|
{
|
|
abi_dispatch *disp = abi_disp();
|
|
// The position in the destination cacheline where *IDST starts.
|
|
uintptr_t dofs = idst & (CACHELINE_SIZE - 1);
|
|
// The position in the source cacheline where *ISRC starts.
|
|
uintptr_t sofs = isrc & (CACHELINE_SIZE - 1);
|
|
const gtm_cacheline *src
|
|
= reinterpret_cast<const gtm_cacheline *>(isrc & -CACHELINE_SIZE);
|
|
gtm_cacheline *dst
|
|
= reinterpret_cast<gtm_cacheline *>(idst & -CACHELINE_SIZE);
|
|
const gtm_cacheline *sline;
|
|
abi_dispatch::mask_pair dpair;
|
|
|
|
if (size == 0)
|
|
return;
|
|
|
|
// If both SRC and DST data start at the same position in the cachelines,
|
|
// we can easily copy the data in tandem, cacheline by cacheline...
|
|
if (dofs == sofs)
|
|
{
|
|
// We copy the data in three stages:
|
|
|
|
// (a) Copy stray bytes at the beginning that are smaller than a
|
|
// cacheline.
|
|
if (sofs != 0)
|
|
{
|
|
size_t sleft = CACHELINE_SIZE - sofs;
|
|
size_t min = (size <= sleft ? size : sleft);
|
|
|
|
dpair = disp->write_lock(dst, W);
|
|
sline = disp->read_lock(src, R);
|
|
*dpair.mask |= (((gtm_cacheline_mask)1 << min) - 1) << sofs;
|
|
memcpy (&dpair.line->b[sofs], &sline->b[sofs], min);
|
|
dst++;
|
|
src++;
|
|
size -= min;
|
|
}
|
|
|
|
// (b) Copy subsequent cacheline sized chunks.
|
|
while (size >= CACHELINE_SIZE)
|
|
{
|
|
dpair = disp->write_lock(dst, W);
|
|
sline = disp->read_lock(src, R);
|
|
*dpair.mask = -1;
|
|
*dpair.line = *sline;
|
|
dst++;
|
|
src++;
|
|
size -= CACHELINE_SIZE;
|
|
}
|
|
|
|
// (c) Copy anything left over.
|
|
if (size != 0)
|
|
{
|
|
dpair = disp->write_lock(dst, W);
|
|
sline = disp->read_lock(src, R);
|
|
*dpair.mask |= ((gtm_cacheline_mask)1 << size) - 1;
|
|
memcpy (dpair.line, sline, size);
|
|
}
|
|
}
|
|
// ... otherwise, we must copy the data in disparate hunks using
|
|
// temporary storage.
|
|
else
|
|
{
|
|
gtm_cacheline c;
|
|
size_t sleft = CACHELINE_SIZE - sofs;
|
|
|
|
sline = disp->read_lock(src, R);
|
|
|
|
// As above, we copy the data in three stages:
|
|
|
|
// (a) Copy stray bytes at the beginning that are smaller than a
|
|
// cacheline.
|
|
if (dofs != 0)
|
|
{
|
|
size_t dleft = CACHELINE_SIZE - dofs;
|
|
size_t min = (size <= dleft ? size : dleft);
|
|
|
|
dpair = disp->write_lock(dst, W);
|
|
*dpair.mask |= (((gtm_cacheline_mask)1 << min) - 1) << dofs;
|
|
|
|
// If what's left in the source cacheline will fit in the
|
|
// rest of the destination cacheline, straight up copy it.
|
|
if (min <= sleft)
|
|
{
|
|
memcpy (&dpair.line->b[dofs], &sline->b[sofs], min);
|
|
sofs += min;
|
|
}
|
|
// Otherwise, we need more bits from the source cacheline
|
|
// that are available. Piece together what we need from
|
|
// contiguous (source) cachelines, into temp space, and copy
|
|
// it over.
|
|
else
|
|
{
|
|
memcpy (&c, &sline->b[sofs], sleft);
|
|
sline = disp->read_lock(++src, R);
|
|
sofs = min - sleft;
|
|
memcpy (&c.b[sleft], sline, sofs);
|
|
memcpy (&dpair.line->b[dofs], &c, min);
|
|
}
|
|
sleft = CACHELINE_SIZE - sofs;
|
|
|
|
dst++;
|
|
size -= min;
|
|
}
|
|
|
|
// (b) Copy subsequent cacheline sized chunks.
|
|
while (size >= CACHELINE_SIZE)
|
|
{
|
|
// We have a full (destination) cacheline where to put the
|
|
// data, but to get to the corresponding cacheline sized
|
|
// chunk in the source, we have to piece together two
|
|
// contiguous source cachelines.
|
|
|
|
memcpy (&c, &sline->b[sofs], sleft);
|
|
sline = disp->read_lock(++src, R);
|
|
memcpy (&c.b[sleft], sline, sofs);
|
|
|
|
dpair = disp->write_lock(dst, W);
|
|
*dpair.mask = -1;
|
|
*dpair.line = c;
|
|
|
|
dst++;
|
|
size -= CACHELINE_SIZE;
|
|
}
|
|
|
|
// (c) Copy anything left over.
|
|
if (size != 0)
|
|
{
|
|
dpair = disp->write_lock(dst, W);
|
|
*dpair.mask |= ((gtm_cacheline_mask)1 << size) - 1;
|
|
// If what's left to copy is entirely in the remaining
|
|
// source cacheline, do it.
|
|
if (size <= sleft)
|
|
memcpy (dpair.line, &sline->b[sofs], size);
|
|
// Otherwise, piece together the remaining bits, and copy.
|
|
else
|
|
{
|
|
memcpy (&c, &sline->b[sofs], sleft);
|
|
sline = disp->read_lock(++src, R);
|
|
memcpy (&c.b[sleft], sline, size - sleft);
|
|
memcpy (dpair.line, &c, size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_memmove (uintptr_t idst, uintptr_t isrc, size_t size,
|
|
abi_dispatch::lock_type W, abi_dispatch::lock_type R)
|
|
{
|
|
abi_dispatch *disp = abi_disp();
|
|
uintptr_t dleft, sleft, sofs, dofs;
|
|
const gtm_cacheline *sline;
|
|
abi_dispatch::mask_pair dpair;
|
|
|
|
if (size == 0)
|
|
return;
|
|
|
|
/* The co-aligned memmove below doesn't work for DST == SRC, so filter
|
|
that out. It's tempting to just return here, as this is a no-op move.
|
|
However, our caller has the right to expect the locks to be acquired
|
|
as advertized. */
|
|
if (__builtin_expect (idst == isrc, 0))
|
|
{
|
|
/* If the write lock is already acquired, nothing to do. */
|
|
if (W == abi_dispatch::WaW)
|
|
return;
|
|
/* If the destination is protected, acquire a write lock. */
|
|
if (W != abi_dispatch::NOLOCK)
|
|
R = abi_dispatch::RfW;
|
|
/* Notice serial mode, where we don't acquire locks at all. */
|
|
if (R == abi_dispatch::NOLOCK)
|
|
return;
|
|
|
|
idst = isrc + size;
|
|
for (isrc &= -CACHELINE_SIZE; isrc < idst; isrc += CACHELINE_SIZE)
|
|
disp->read_lock(reinterpret_cast<const gtm_cacheline *>(isrc), R);
|
|
return;
|
|
}
|
|
|
|
/* Fall back to memcpy if the implementation above can handle it. */
|
|
if (idst < isrc || isrc + size <= idst)
|
|
{
|
|
do_memcpy (idst, isrc, size, W, R);
|
|
return;
|
|
}
|
|
|
|
/* What remains requires a backward copy from the end of the blocks. */
|
|
idst += size;
|
|
isrc += size;
|
|
dofs = idst & (CACHELINE_SIZE - 1);
|
|
sofs = isrc & (CACHELINE_SIZE - 1);
|
|
dleft = CACHELINE_SIZE - dofs;
|
|
sleft = CACHELINE_SIZE - sofs;
|
|
|
|
gtm_cacheline *dst
|
|
= reinterpret_cast<gtm_cacheline *>(idst & -CACHELINE_SIZE);
|
|
const gtm_cacheline *src
|
|
= reinterpret_cast<const gtm_cacheline *>(isrc & -CACHELINE_SIZE);
|
|
if (dofs == 0)
|
|
dst--;
|
|
if (sofs == 0)
|
|
src--;
|
|
|
|
if (dofs == sofs)
|
|
{
|
|
/* Since DST and SRC are co-aligned, and we didn't use the memcpy
|
|
optimization above, that implies that SIZE > CACHELINE_SIZE. */
|
|
if (sofs != 0)
|
|
{
|
|
dpair = disp->write_lock(dst, W);
|
|
sline = disp->read_lock(src, R);
|
|
*dpair.mask |= ((gtm_cacheline_mask)1 << sleft) - 1;
|
|
memcpy (dpair.line, sline, sleft);
|
|
dst--;
|
|
src--;
|
|
size -= sleft;
|
|
}
|
|
|
|
while (size >= CACHELINE_SIZE)
|
|
{
|
|
dpair = disp->write_lock(dst, W);
|
|
sline = disp->read_lock(src, R);
|
|
*dpair.mask = -1;
|
|
*dpair.line = *sline;
|
|
dst--;
|
|
src--;
|
|
size -= CACHELINE_SIZE;
|
|
}
|
|
|
|
if (size != 0)
|
|
{
|
|
size_t ofs = CACHELINE_SIZE - size;
|
|
dpair = disp->write_lock(dst, W);
|
|
sline = disp->read_lock(src, R);
|
|
*dpair.mask |= (((gtm_cacheline_mask)1 << size) - 1) << ofs;
|
|
memcpy (&dpair.line->b[ofs], &sline->b[ofs], size);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gtm_cacheline c;
|
|
|
|
sline = disp->read_lock(src, R);
|
|
if (dofs != 0)
|
|
{
|
|
size_t min = (size <= dofs ? size : dofs);
|
|
|
|
if (min <= sofs)
|
|
{
|
|
sofs -= min;
|
|
memcpy (&c, &sline->b[sofs], min);
|
|
}
|
|
else
|
|
{
|
|
size_t min_ofs = min - sofs;
|
|
memcpy (&c.b[min_ofs], sline, sofs);
|
|
sline = disp->read_lock(--src, R);
|
|
sofs = CACHELINE_SIZE - min_ofs;
|
|
memcpy (&c, &sline->b[sofs], min_ofs);
|
|
}
|
|
|
|
dofs = dleft - min;
|
|
dpair = disp->write_lock(dst, W);
|
|
*dpair.mask |= (((gtm_cacheline_mask)1 << min) - 1) << dofs;
|
|
memcpy (&dpair.line->b[dofs], &c, min);
|
|
|
|
sleft = CACHELINE_SIZE - sofs;
|
|
dst--;
|
|
size -= min;
|
|
}
|
|
|
|
while (size >= CACHELINE_SIZE)
|
|
{
|
|
memcpy (&c.b[sleft], sline, sofs);
|
|
sline = disp->read_lock(--src, R);
|
|
memcpy (&c, &sline->b[sofs], sleft);
|
|
|
|
dpair = disp->write_lock(dst, W);
|
|
*dpair.mask = -1;
|
|
*dpair.line = c;
|
|
|
|
dst--;
|
|
size -= CACHELINE_SIZE;
|
|
}
|
|
|
|
if (size != 0)
|
|
{
|
|
dofs = CACHELINE_SIZE - size;
|
|
|
|
memcpy (&c.b[sleft], sline, sofs);
|
|
if (sleft > dofs)
|
|
{
|
|
sline = disp->read_lock(--src, R);
|
|
memcpy (&c, &sline->b[sofs], sleft);
|
|
}
|
|
|
|
dpair = disp->write_lock(dst, W);
|
|
*dpair.mask |= (gtm_cacheline_mask)-1 << dofs;
|
|
memcpy (&dpair.line->b[dofs], &c.b[dofs], size);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define ITM_MEM_DEF(NAME, READ, WRITE) \
|
|
void ITM_REGPARM _ITM_memcpy##NAME(void *dst, const void *src, size_t size) \
|
|
{ \
|
|
do_memcpy ((uintptr_t)dst, (uintptr_t)src, size, \
|
|
abi_dispatch::WRITE, abi_dispatch::READ); \
|
|
} \
|
|
void ITM_REGPARM _ITM_memmove##NAME(void *dst, const void *src, size_t size) \
|
|
{ \
|
|
do_memmove ((uintptr_t)dst, (uintptr_t)src, size, \
|
|
abi_dispatch::WRITE, abi_dispatch::READ); \
|
|
}
|
|
|
|
ITM_MEM_DEF(RnWt, NOLOCK, W)
|
|
ITM_MEM_DEF(RnWtaR, NOLOCK, WaR)
|
|
ITM_MEM_DEF(RnWtaW, NOLOCK, WaW)
|
|
|
|
ITM_MEM_DEF(RtWn, R, NOLOCK)
|
|
ITM_MEM_DEF(RtWt, R, W)
|
|
ITM_MEM_DEF(RtWtaR, R, WaR)
|
|
ITM_MEM_DEF(RtWtaW, R, WaW)
|
|
|
|
ITM_MEM_DEF(RtaRWn, RaR, NOLOCK)
|
|
ITM_MEM_DEF(RtaRWt, RaR, W)
|
|
ITM_MEM_DEF(RtaRWtaR, RaR, WaR)
|
|
ITM_MEM_DEF(RtaRWtaW, RaR, WaW)
|
|
|
|
ITM_MEM_DEF(RtaWWn, RaW, NOLOCK)
|
|
ITM_MEM_DEF(RtaWWt, RaW, W)
|
|
ITM_MEM_DEF(RtaWWtaR, RaW, WaR)
|
|
ITM_MEM_DEF(RtaWWtaW, RaW, WaW)
|