emutls.c 4.78 KB
Newer Older
1
/* TLS emulation.
2
   Copyright (C) 2006, 2008, 2009 Free Software Foundation, Inc.
3 4 5 6 7 8
   Contributed by Jakub Jelinek <jakub@redhat.com>.

This file is part of GCC.

GCC 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
9
Software Foundation; either version 3, or (at your option) any later
10 11 12 13 14 15 16
version.

GCC 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.

17 18 19 20 21 22 23 24
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/>.  */
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

#include "tconfig.h"
#include "tsystem.h"
#include "coretypes.h"
#include "tm.h"
#include "gthr.h"

typedef unsigned int word __attribute__((mode(word)));
typedef unsigned int pointer __attribute__((mode(pointer)));

struct __emutls_object
{
  word size;
  word align;
  union {
    pointer offset;
    void *ptr;
  } loc;
  void *templ;
};

46 47 48 49 50 51
struct __emutls_array
{
  pointer size;
  void **data[];
};

52 53 54
void *__emutls_get_address (struct __emutls_object *);
void __emutls_register_common (struct __emutls_object *, word, word, void *);

55 56 57 58 59 60 61 62 63 64 65 66
#ifdef __GTHREADS
#ifdef __GTHREAD_MUTEX_INIT
static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT;
#else
static __gthread_mutex_t emutls_mutex;
#endif
static __gthread_key_t emutls_key;
static pointer emutls_size;

static void
emutls_destroy (void *ptr)
{
67 68 69 70 71
  struct __emutls_array *arr = ptr;
  pointer size = arr->size;
  pointer i;

  for (i = 0; i < size; ++i)
72
    {
73 74
      if (arr->data[i])
	free (arr->data[i][-1]);
75
    }
76

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  free (ptr);
}

static void
emutls_init (void)
{
#ifndef __GTHREAD_MUTEX_INIT
  __GTHREAD_MUTEX_INIT_FUNCTION (&emutls_mutex);
#endif
  if (__gthread_key_create (&emutls_key, emutls_destroy) != 0)
    abort ();
}
#endif

static void *
emutls_alloc (struct __emutls_object *obj)
{
  void *ptr;
  void *ret;

  /* We could use here posix_memalign if available and adjust
     emutls_destroy accordingly.  */
  if (obj->align <= sizeof (void *))
    {
      ptr = malloc (obj->size + sizeof (void *));
      if (ptr == NULL)
	abort ();
      ((void **) ptr)[0] = ptr;
      ret = ptr + sizeof (void *);
    }
  else
    {
      ptr = malloc (obj->size + sizeof (void *) + obj->align - 1);
      if (ptr == NULL)
	abort ();
      ret = (void *) (((pointer) (ptr + sizeof (void *) + obj->align - 1))
		      & ~(pointer)(obj->align - 1));
      ((void **) ret)[-1] = ptr;
    }

  if (obj->templ)
    memcpy (ret, obj->templ, obj->size);
  else
    memset (ret, 0, obj->size);

  return ret;
}

void *
__emutls_get_address (struct __emutls_object *obj)
{
  if (! __gthread_active_p ())
    {
      if (__builtin_expect (obj->loc.ptr == NULL, 0))
	obj->loc.ptr = emutls_alloc (obj);
      return obj->loc.ptr;
    }

#ifndef __GTHREADS
  abort ();
#else
138
  pointer offset = obj->loc.offset;
139

140
  if (__builtin_expect (offset == 0, 0))
141 142 143 144
    {
      static __gthread_once_t once = __GTHREAD_ONCE_INIT;
      __gthread_once (&once, emutls_init);
      __gthread_mutex_lock (&emutls_mutex);
145 146 147 148 149 150
      offset = obj->loc.offset;
      if (offset == 0)
	{
	  offset = ++emutls_size;
	  obj->loc.offset = offset;
	}
151 152 153
      __gthread_mutex_unlock (&emutls_mutex);
    }

154
  struct __emutls_array *arr = __gthread_getspecific (emutls_key);
155 156 157
  if (__builtin_expect (arr == NULL, 0))
    {
      pointer size = offset + 32;
158
      arr = calloc (size + 1, sizeof (void *));
159 160
      if (arr == NULL)
	abort ();
161
      arr->size = size;
162 163
      __gthread_setspecific (emutls_key, (void *) arr);
    }
164
  else if (__builtin_expect (offset > arr->size, 0))
165
    {
166
      pointer orig_size = arr->size;
167
      pointer size = orig_size * 2;
168
      if (offset > size)
169
	size = offset + 32;
170
      arr = realloc (arr, (size + 1) * sizeof (void *));
171 172
      if (arr == NULL)
	abort ();
173
      arr->size = size;
174
      memset (arr->data + orig_size, 0,
175
	      (size - orig_size) * sizeof (void *));
176 177 178
      __gthread_setspecific (emutls_key, (void *) arr);
    }

179
  void *ret = arr->data[offset - 1];
180 181 182
  if (__builtin_expect (ret == NULL, 0))
    {
      ret = emutls_alloc (obj);
183
      arr->data[offset - 1] = ret;
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
    }
  return ret;
#endif
}

void
__emutls_register_common (struct __emutls_object *obj,
			  word size, word align, void *templ)
{
  if (obj->size < size)
    {
      obj->size = size;
      obj->templ = NULL;
    }
  if (obj->align < align)
    obj->align = align;
  if (templ && size == obj->size)
    obj->templ = templ;
}