linux/arch/tile/lib/memset_64.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 Tilera Corporation. All Rights Reserved.
   3 *
   4 *   This program is free software; you can redistribute it and/or
   5 *   modify it under the terms of the GNU General Public License
   6 *   as published by the Free Software Foundation, version 2.
   7 *
   8 *   This program is distributed in the hope that it will be useful, but
   9 *   WITHOUT ANY WARRANTY; without even the implied warranty of
  10 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  11 *   NON INFRINGEMENT.  See the GNU General Public License for
  12 *   more details.
  13 */
  14
  15#include <arch/chip.h>
  16
  17#include <linux/types.h>
  18#include <linux/string.h>
  19#include <linux/module.h>
  20
  21#undef memset
  22
  23void *memset(void *s, int c, size_t n)
  24{
  25        uint64_t *out64;
  26        int n64, to_align64;
  27        uint64_t v64;
  28        uint8_t *out8 = s;
  29
  30        /* Experimentation shows that a trivial tight loop is a win up until
  31         * around a size of 20, where writing a word at a time starts to win.
  32         */
  33#define BYTE_CUTOFF 20
  34
  35#if BYTE_CUTOFF < 7
  36        /* This must be at least at least this big, or some code later
  37         * on doesn't work.
  38         */
  39#error "BYTE_CUTOFF is too small"
  40#endif
  41
  42        if (n < BYTE_CUTOFF) {
  43                /* Strangely, this turns out to be the tightest way to
  44                 * write this loop.
  45                 */
  46                if (n != 0) {
  47                        do {
  48                                /* Strangely, combining these into one line
  49                                 * performs worse.
  50                                 */
  51                                *out8 = c;
  52                                out8++;
  53                        } while (--n != 0);
  54                }
  55
  56                return s;
  57        }
  58
  59        /* Align 'out8'. We know n >= 7 so this won't write past the end. */
  60        while (((uintptr_t) out8 & 7) != 0) {
  61                *out8++ = c;
  62                --n;
  63        }
  64
  65        /* Align 'n'. */
  66        while (n & 7)
  67                out8[--n] = c;
  68
  69        out64 = (uint64_t *) out8;
  70        n64 = n >> 3;
  71
  72        /* Tile input byte out to 64 bits. */
  73        /* KLUDGE */
  74        v64 = 0x0101010101010101ULL * (uint8_t)c;
  75
  76        /* This must be at least 8 or the following loop doesn't work. */
  77#define CACHE_LINE_SIZE_IN_DOUBLEWORDS (CHIP_L2_LINE_SIZE() / 8)
  78
  79        /* Determine how many words we need to emit before the 'out32'
  80         * pointer becomes aligned modulo the cache line size.
  81         */
  82        to_align64 = (-((uintptr_t)out64 >> 3)) &
  83                (CACHE_LINE_SIZE_IN_DOUBLEWORDS - 1);
  84
  85        /* Only bother aligning and using wh64 if there is at least
  86         * one full cache line to process.  This check also prevents
  87         * overrunning the end of the buffer with alignment words.
  88         */
  89        if (to_align64 <= n64 - CACHE_LINE_SIZE_IN_DOUBLEWORDS) {
  90                int lines_left;
  91
  92                /* Align out64 mod the cache line size so we can use wh64. */
  93                n64 -= to_align64;
  94                for (; to_align64 != 0; to_align64--) {
  95                        *out64 = v64;
  96                        out64++;
  97                }
  98
  99                /* Use unsigned divide to turn this into a right shift. */
 100                lines_left = (unsigned)n64 / CACHE_LINE_SIZE_IN_DOUBLEWORDS;
 101
 102                do {
 103                        /* Only wh64 a few lines at a time, so we don't
 104                         * exceed the maximum number of victim lines.
 105                         */
 106                        int x = ((lines_left < CHIP_MAX_OUTSTANDING_VICTIMS())
 107                                  ? lines_left
 108                                  : CHIP_MAX_OUTSTANDING_VICTIMS());
 109                        uint64_t *wh = out64;
 110                        int i = x;
 111                        int j;
 112
 113                        lines_left -= x;
 114
 115                        do {
 116                                __insn_wh64(wh);
 117                                wh += CACHE_LINE_SIZE_IN_DOUBLEWORDS;
 118                        } while (--i);
 119
 120                        for (j = x * (CACHE_LINE_SIZE_IN_DOUBLEWORDS / 4);
 121                             j != 0; j--) {
 122                                *out64++ = v64;
 123                                *out64++ = v64;
 124                                *out64++ = v64;
 125                                *out64++ = v64;
 126                        }
 127                } while (lines_left != 0);
 128
 129                /* We processed all full lines above, so only this many
 130                 * words remain to be processed.
 131                 */
 132                n64 &= CACHE_LINE_SIZE_IN_DOUBLEWORDS - 1;
 133        }
 134
 135        /* Now handle any leftover values. */
 136        if (n64 != 0) {
 137                do {
 138                        *out64 = v64;
 139                        out64++;
 140                } while (--n64 != 0);
 141        }
 142
 143        return s;
 144}
 145EXPORT_SYMBOL(memset);
 146