public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [RFC][gomp4] Offloading patches (2/3): Add tables generation
@ 2013-12-17 11:40 Michael V. Zolotukhin
  2014-01-16 11:37 ` Michael Zolotukhin
                   ` (2 more replies)
  0 siblings, 3 replies; 62+ messages in thread
From: Michael V. Zolotukhin @ 2013-12-17 11:40 UTC (permalink / raw)
  To: Jakub Jelinek, Thomas Schwinge, Bernd Schmidt, Richard Biener,
	Kirill Yukhin, Ilya Verbin, Andrey Turetskiy, Ilya Tocar, gcc

Hi everybody,

Here is a patch 2/3: Add tables generation.

This patch is just a slightly modified patch sent a couple of weeks ago.  When
compiling with '-fopenmp' compiler generates a special symbol, containing
addresses and sizes of globals/omp_fn-functions, and places it into a special
section.  Later, at linking, these sections are merged together and we get a
single table with all addresses/sizes for entire binary.  Also, in this patch we
start to pass '__OPENMP_TARGET__' symbol to GOMP_target calls.

Thanks,
Michael


---
 gcc/omp-low.c |  119 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 gcc/omp-low.h |    1 +
 gcc/toplev.c  |    3 +
 3 files changed, 115 insertions(+), 8 deletions(-)

diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index e0f7d1d..f860204 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "optabs.h"
 #include "cfgloop.h"
 #include "target.h"
+#include "common/common-target.h"
 #include "omp-low.h"
 #include "gimple-low.h"
 #include "tree-cfgcleanup.h"
@@ -8371,19 +8372,22 @@ expand_omp_target (struct omp_region *region)
     }
 
   gimple g;
-  /* FIXME: This will be address of
-     extern char __OPENMP_TARGET__[] __attribute__((visibility ("hidden")))
-     symbol, as soon as the linker plugin is able to create it for us.  */
-  tree openmp_target = build_zero_cst (ptr_type_node);
+  tree openmp_target
+    = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		  get_identifier ("__OPENMP_TARGET__"), ptr_type_node);
+  TREE_PUBLIC (openmp_target) = 1;
+  DECL_EXTERNAL (openmp_target) = 1;
   if (kind == GF_OMP_TARGET_KIND_REGION)
     {
       tree fnaddr = build_fold_addr_expr (child_fn);
-      g = gimple_build_call (builtin_decl_explicit (start_ix), 7,
-			     device, fnaddr, openmp_target, t1, t2, t3, t4);
+      g = gimple_build_call (builtin_decl_explicit (start_ix), 7, device,
+			     fnaddr, build_fold_addr_expr (openmp_target),
+			     t1, t2, t3, t4);
     }
   else
-    g = gimple_build_call (builtin_decl_explicit (start_ix), 6,
-			   device, openmp_target, t1, t2, t3, t4);
+    g = gimple_build_call (builtin_decl_explicit (start_ix), 6, device,
+			   build_fold_addr_expr (openmp_target),
+			   t1, t2, t3, t4);
   gimple_set_location (g, gimple_location (entry_stmt));
   gsi_insert_before (&gsi, g, GSI_SAME_STMT);
   if (kind != GF_OMP_TARGET_KIND_REGION)
@@ -12379,4 +12383,103 @@ make_pass_omp_simd_clone (gcc::context *ctxt)
   return new pass_omp_simd_clone (ctxt);
 }
 
+/* Helper function for omp_finish_file routine.
+   Takes decls from V_DECLS and adds their addresses and sizes to
+   constructor-vector V_CTOR.  It will be later used as DECL_INIT for decl
+   representing a global symbol for OpenMP descriptor.
+   If IS_FUNCTION is true, we use 1 for size.  */
+static void
+add_decls_addresses_to_decl_constructor (vec<tree, va_gc> *v_decls,
+					 vec<constructor_elt, va_gc> *v_ctor,
+					 bool is_function)
+{
+  unsigned int len = 0, i;
+  tree size, it;
+  len = vec_safe_length (v_decls);
+  for (i = 0; i < len; i++)
+    {
+      /* Decls are placed in reversed order in fat-objects, so we need to
+	 revert them back if we compile target.  */
+      if (!flag_openmp_target)
+	it = (*v_decls)[i];
+      else
+	it = (*v_decls)[len - i - 1];
+      size = is_function ? integer_one_node : DECL_SIZE (it);
+      CONSTRUCTOR_APPEND_ELT (v_ctor, NULL_TREE, build_fold_addr_expr (it));
+      CONSTRUCTOR_APPEND_ELT (v_ctor, NULL_TREE,
+			      fold_convert (const_ptr_type_node,
+					    size));
+    }
+}
+
+/* Create new symbol containing (address, size) pairs for omp-marked
+   functions and global variables.  */
+void
+omp_finish_file (void)
+{
+  struct cgraph_node *node;
+  struct varpool_node *vnode;
+  const char *section_name = ".offload_func_table_section";
+  tree new_decl, new_decl_type;
+  vec<constructor_elt, va_gc> *v;
+  vec<tree, va_gc> *v_func, *v_var;
+  tree ctor;
+  int num = 0;
+
+  if (!targetm_common.have_named_sections)
+    return;
+
+  vec_alloc (v_func, 0);
+  vec_alloc (v_var, 0);
+
+  /* Collect all omp-target functions.  */
+  FOR_EACH_DEFINED_FUNCTION (node)
+    {
+      /* TODO: This check could fail on functions, created by omp
+	 parallel/task pragmas.  It's better to name outlined for offloading
+	 functions in some different way and to check here the function name.
+	 It could be something like "*_omp_tgtfn" in contrast with "*_omp_fn"
+	 for functions from omp parallel/task pragmas.  */
+      if (!lookup_attribute ("omp declare target",
+			     DECL_ATTRIBUTES (node->decl))
+	  || !DECL_ARTIFICIAL (node->decl))
+	continue;
+      vec_safe_push (v_func, node->decl);
+      num ++;
+    }
+  /* Collect all omp-target global variables.  */
+  FOR_EACH_DEFINED_VARIABLE (vnode)
+    {
+      if (!lookup_attribute ("omp declare target",
+			     DECL_ATTRIBUTES (vnode->decl))
+	  || TREE_CODE (vnode->decl) != VAR_DECL
+	  || DECL_SIZE (vnode->decl) == 0)
+	continue;
+
+      vec_safe_push (v_var, vnode->decl);
+      num ++;
+    }
+
+  if (num == 0)
+    return;
+
+  vec_alloc (v, num * 2);
+
+  add_decls_addresses_to_decl_constructor (v_func, v, true);
+  add_decls_addresses_to_decl_constructor (v_var, v, false);
+
+  new_decl_type = build_array_type_nelts (pointer_sized_int_node, num * 2);
+  ctor = build_constructor (new_decl_type, v);
+  TREE_CONSTANT (ctor) = 1;
+  TREE_STATIC (ctor) = 1;
+  new_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			 get_identifier (".omp_table"), new_decl_type);
+  TREE_STATIC (new_decl) = 1;
+  DECL_INITIAL (new_decl) = ctor;
+  DECL_SECTION_NAME (new_decl) = build_string (strlen (section_name),
+					       section_name);
+
+  varpool_assemble_decl (varpool_node_for_decl (new_decl));
+}
+
 #include "gt-omp-low.h"
diff --git a/gcc/omp-low.h b/gcc/omp-low.h
index 6b5a2ff..813189d 100644
--- a/gcc/omp-low.h
+++ b/gcc/omp-low.h
@@ -27,5 +27,6 @@ extern void omp_expand_local (basic_block);
 extern void free_omp_regions (void);
 extern tree omp_reduction_init (tree, tree);
 extern bool make_gimple_omp_edges (basic_block, struct omp_region **);
+extern void omp_finish_file (void);
 
 #endif /* GCC_OMP_LOW_H */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 5fedcea..af010ff 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -78,6 +78,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-color.h"
 #include "context.h"
 #include "pass_manager.h"
+#include "omp-low.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -576,6 +577,8 @@ compile_file (void)
       if (flag_sanitize & SANITIZE_THREAD)
 	tsan_finish_file ();
 
+      omp_finish_file ();
+
       output_shared_constant_pool ();
       output_object_blocks ();
       finish_tm_clone_pairs ();
-- 
1.7.1

^ permalink raw reply	[flat|nested] 62+ messages in thread
* [RFC][gomp4] Offloading: Add device initialization and host->target function mapping
@ 2013-12-20 17:18 Ilya Verbin
  2013-12-26 13:37 ` Ilya Verbin
  0 siblings, 1 reply; 62+ messages in thread
From: Ilya Verbin @ 2013-12-20 17:18 UTC (permalink / raw)
  To: Jakub Jelinek, Richard Biener, Thomas Schwinge, Bernd Schmidt,
	Kirill Yukhin, Michael V. Zolotukhin, Andrey Turetskiy,
	Ilya Tocar, gcc-patches

Hi Jakub,

Could you please take a look at this patch for libgomp?

It adds new function GOMP_register_lib, that should be called from every
exec/lib with target regions (that was done in patch [1]).  This function
maintains the array of pointers to the target shared library descriptors.

Also this patch adds target device initialization into GOMP_target and
GOMP_target_data.  At first, it calls "device_init" function from the plugin.
This function takes array of target-images as input, and returns the array of
target-side addresses.  Currently, it always uses the first target-image from
the descriptor, this should be generalized later.  Then libgomp reads the tables
from host-side exec/libs.  After that, it inserts host->target address mapping
into the splay tree.

[1] http://gcc.gnu.org/ml/gcc-patches/2013-12/msg01486.html

Thanks,
    -- Ilya

---
 libgomp/libgomp.map |    1 +
 libgomp/target.c    |  154 ++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 146 insertions(+), 9 deletions(-)

diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 2b64d05..792047f 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -208,6 +208,7 @@ GOMP_3.0 {
 
 GOMP_4.0 {
   global:
+	GOMP_register_lib;
 	GOMP_barrier_cancel;
 	GOMP_cancel;
 	GOMP_cancellation_point;
diff --git a/libgomp/target.c b/libgomp/target.c
index d84a1fa..a37819a 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -84,6 +84,19 @@ struct splay_tree_key_s {
   bool copy_from;
 };
 
+enum library_descr {
+  DESCR_TABLE_START,
+  DESCR_TABLE_END,
+  DESCR_IMAGE_START,
+  DESCR_IMAGE_END
+};
+
+/* Array of pointers to target shared library descriptors.  */
+static void **libraries;
+
+/* Total number of target shared libraries.  */
+static int num_libraries;
+
 /* Array of descriptors of all available devices.  */
 static struct gomp_device_descr *devices;
 
@@ -117,11 +130,16 @@ struct gomp_device_descr
      TARGET construct.  */
   int id;
 
+  /* Set to true when device is initialized.  */
+  bool is_initialized;
+
   /* Plugin file handler.  */
   void *plugin_handle;
 
   /* Function handlers.  */
-  bool (*device_available_func) (void);
+  bool (*device_available_func) (int);
+  void (*device_init_func) (void **, int *, int, void ***, int *);
+  void (*device_run_func) (void *, uintptr_t);
 
   /* Splay tree containing information about mapped memory regions.  */
   struct splay_tree_s dev_splay_tree;
@@ -466,6 +484,89 @@ gomp_update (struct gomp_device_descr *devicep, size_t mapnum,
   gomp_mutex_unlock (&devicep->dev_env_lock);
 }
 
+void
+GOMP_register_lib (const void *openmp_target)
+{
+  libraries = realloc (libraries, (num_libraries + 1) * sizeof (void *));
+
+  if (libraries == NULL)
+    return;
+
+  libraries[num_libraries] = (void *) openmp_target;
+
+  num_libraries++;
+}
+
+static void
+gomp_init_device (struct gomp_device_descr *devicep)
+{
+  void **target_images = malloc (num_libraries * sizeof (void *));
+  int *target_img_sizes = malloc (num_libraries * sizeof (int));
+  if (target_images == NULL || target_img_sizes == NULL)
+    gomp_fatal ("Can not allocate memory");
+
+  /* Collect target images from the library descriptors and calculate the total
+     size of host address table.  */
+  int i, host_table_size = 0;
+  for (i = 0; i < num_libraries; i++)
+    {
+      void **lib = libraries[i];
+      void **host_table_start = lib[DESCR_TABLE_START];
+      void **host_table_end = lib[DESCR_TABLE_END];
+      /* FIXME: Select the proper target image.  */
+      target_images[i] = lib[DESCR_IMAGE_START];
+      target_img_sizes[i] = lib[DESCR_IMAGE_END] - lib[DESCR_IMAGE_START];
+      host_table_size += host_table_end - host_table_start;
+    }
+
+  /* Initialize the target device and receive the address table from target.  */
+  void **target_table = NULL;
+  int target_table_size = 0;
+  devicep->device_init_func (target_images, target_img_sizes, num_libraries,
+			     &target_table, &target_table_size);
+  free (target_images);
+  free (target_img_sizes);
+
+  if (host_table_size != target_table_size)
+    gomp_fatal ("Can't map target objects");
+
+  /* Initialize the mapping data structure.  */
+  void **target_entry = target_table;
+  for (i = 0; i < num_libraries; i++)
+    {
+      void **lib = libraries[i];
+      void **host_table_start = lib[DESCR_TABLE_START];
+      void **host_table_end = lib[DESCR_TABLE_END];
+      void **host_entry;
+      for (host_entry = host_table_start; host_entry < host_table_end;
+	   host_entry += 2, target_entry += 2)
+	{
+	  struct target_mem_desc *tgt = gomp_malloc (sizeof (*tgt));
+	  tgt->refcount = 1;
+	  tgt->array = gomp_malloc (sizeof (*tgt->array));
+	  tgt->tgt_start = (uintptr_t) *target_entry;
+	  tgt->tgt_end = tgt->tgt_start + (uint64_t) *(target_entry+1);
+	  tgt->to_free = NULL;
+	  tgt->list_count = 0;
+	  tgt->device_descr = devicep;
+	  splay_tree_node node = tgt->array;
+	  splay_tree_key k = &node->key;
+	  k->host_start = (uintptr_t) *host_entry;
+	  k->host_end = k->host_start + (uint64_t) *(host_entry+1);
+	  k->tgt = tgt;
+	  node->left = NULL;
+	  node->right = NULL;
+	  splay_tree_insert (&devicep->dev_splay_tree, node);
+	}
+    }
+
+  free (libraries);
+  num_libraries = 0;
+  free (target_table);
+  target_table_size = 0;
+  devicep->is_initialized = true;
+}
+
 /* Called when encountering a target directive.  If DEVICE
    is -1, it means use device-var ICV.  If it is -2 (or any other value
    larger than last available hw device, use host fallback.
@@ -482,7 +583,8 @@ GOMP_target (int device, void (*fn) (void *), const void *openmp_target,
 	     unsigned char *kinds)
 {
   struct gomp_device_descr *devicep = resolve_device (device);
-  if (devicep == NULL)
+  if (openmp_target == NULL || devicep == NULL
+      || !devicep->device_available_func (devicep->id))
     {
       /* Host fallback.  */
       struct gomp_thread old_thr, *thr = gomp_thread ();
@@ -499,7 +601,18 @@ GOMP_target (int device, void (*fn) (void *), const void *openmp_target,
       return;
     }
 
-  struct target_mem_desc *tgt
+  if (!devicep->is_initialized)
+    gomp_init_device (devicep);
+
+  splay_tree_node node = gomp_malloc (sizeof (*node));
+  splay_tree_key k = &node->key;
+  k->host_start = (uintptr_t) fn;
+  k->host_end = k->host_start + 1;
+  splay_tree_key tgt_fn = splay_tree_lookup (&devicep->dev_splay_tree, k);
+  if (tgt_fn == NULL)
+    gomp_fatal ("Target function wasn't mapped");
+
+  struct target_mem_desc *tgt_vars
     = gomp_map_vars (devicep, mapnum, hostaddrs, sizes, kinds, true);
   struct gomp_thread old_thr, *thr = gomp_thread ();
   old_thr = *thr;
@@ -509,10 +622,11 @@ GOMP_target (int device, void (*fn) (void *), const void *openmp_target,
       thr->place = old_thr.place;
       thr->ts.place_partition_len = gomp_places_list_len;
     }
-  fn ((void *) tgt->tgt_start);
+  devicep->device_run_func ((void *) tgt_fn->tgt->tgt_start,
+			    tgt_vars->tgt_start);
   gomp_free_thread (thr);
   *thr = old_thr;
-  gomp_unmap_vars (tgt);
+  gomp_unmap_vars (tgt_vars);
 }
 
 void
@@ -520,7 +634,8 @@ GOMP_target_data (int device, const void *openmp_target, size_t mapnum,
 		  void **hostaddrs, size_t *sizes, unsigned char *kinds)
 {
   struct gomp_device_descr *devicep = resolve_device (device);
-  if (devicep == NULL)
+  if (openmp_target == NULL || devicep == NULL
+      || !devicep->device_available_func (devicep->id))
     {
       /* Host fallback.  */
       struct gomp_task_icv *icv = gomp_icv (false);
@@ -538,6 +653,9 @@ GOMP_target_data (int device, const void *openmp_target, size_t mapnum,
       return;
     }
 
+  if (!devicep->is_initialized)
+    gomp_init_device (devicep);
+
   struct target_mem_desc *tgt
     = gomp_map_vars (devicep, mapnum, hostaddrs, sizes, kinds, false);
   struct gomp_task_icv *icv = gomp_icv (true);
@@ -562,9 +680,13 @@ GOMP_target_update (int device, const void *openmp_target, size_t mapnum,
 		    void **hostaddrs, size_t *sizes, unsigned char *kinds)
 {
   struct gomp_device_descr *devicep = resolve_device (device);
-  if (devicep == NULL)
+  if (openmp_target == NULL || devicep == NULL
+      || !devicep->device_available_func (devicep->id))
     return;
 
+  if (!devicep->is_initialized)
+    gomp_fatal ("Target device wasn't initialized");
+
   gomp_update (devicep, mapnum, hostaddrs, sizes, kinds);
 }
 
@@ -619,8 +741,7 @@ gomp_load_plugin_for_device (struct gomp_device_descr *device,
   dlerror ();
 
   /* Check if all required functions are available in the plugin and store
-     their handlers.
-     TODO: check for other routines as well.  */
+     their handlers.  */
   device->device_available_func = dlsym (device->plugin_handle,
 					 "device_available");
   if (dlerror () != NULL)
@@ -629,6 +750,20 @@ gomp_load_plugin_for_device (struct gomp_device_descr *device,
       return false;
     }
 
+  device->device_init_func = dlsym (device->plugin_handle, "device_init");
+  if (dlerror () != NULL)
+    {
+      dlclose (device->plugin_handle);
+      return false;
+    }
+
+  device->device_run_func = dlsym (device->plugin_handle, "device_run");
+  if (dlerror () != NULL)
+    {
+      dlclose (device->plugin_handle);
+      return false;
+    }
+
   return true;
 }
 
@@ -680,6 +815,7 @@ gomp_find_available_plugins (void)
 
       devices[num_devices] = current_device;
       devices[num_devices].id = num_devices + 1;
+      devices[num_devices].is_initialized = false;
       devices[num_devices].dev_splay_tree.root = NULL;
       gomp_mutex_init (&devices[num_devices].dev_env_lock);
       num_devices++;
-- 
1.7.1

^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2014-09-08  9:42 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-17 11:40 [RFC][gomp4] Offloading patches (2/3): Add tables generation Michael V. Zolotukhin
2014-01-16 11:37 ` Michael Zolotukhin
2014-01-28 11:20 ` Bernd Schmidt
     [not found]   ` <CADG=Z0GQ8ORLe1XRUU7VMYeLhwuWisMyCcGLQj-nY_bhkbD_1Q@mail.gmail.com>
2014-01-28 12:53     ` Fwd: " Ilya Verbin
2014-01-29 14:43       ` Bernd Schmidt
2014-01-29 16:06         ` Ilya Verbin
2014-02-14 14:49           ` Ilya Verbin
2014-02-14 15:02             ` Bernd Schmidt
2014-02-14 15:12               ` Jakub Jelinek
2014-02-14 15:50                 ` Bernd Schmidt
2014-02-14 16:06                   ` Jakub Jelinek
2014-02-20 18:27             ` Bernd Schmidt
2014-02-21 15:17               ` Ilya Verbin
2014-02-21 15:41                 ` Bernd Schmidt
2014-02-21 18:00                   ` Ilya Verbin
2014-02-28 16:09               ` Ilya Verbin
2014-02-28 16:23                 ` Bernd Schmidt
2014-02-28 21:41                   ` Bernd Schmidt
2014-05-27 10:18                     ` Ilya Verbin
2014-05-27 10:59                       ` Bernd Schmidt
2014-05-27 11:11                         ` Ilya Verbin
2014-05-27 11:16                           ` Bernd Schmidt
2014-05-27 15:33                             ` Ilya Verbin
2014-05-27 19:03                               ` Bernd Schmidt
2014-02-28 22:10                   ` Ilya Verbin
2014-03-05 17:15                   ` Ilya Verbin
2014-03-06  8:48                     ` Bernd Schmidt
2014-03-06 11:11                       ` Ilya Verbin
2014-03-06 11:54                         ` Bernd Schmidt
2014-03-06 12:52                           ` Ilya Verbin
2014-03-08 15:39                             ` Ilya Verbin
2014-03-12 14:14                               ` Bernd Schmidt
2014-03-12 14:52                                 ` Ilya Verbin
2014-03-20 17:41                                   ` Bernd Schmidt
2014-06-17 18:20                   ` Ilya Verbin
2014-06-17 19:23                     ` Bernd Schmidt
2014-06-18 14:14                       ` Ilya Verbin
2014-06-18 14:23                         ` Bernd Schmidt
2014-06-19 10:19                           ` Ilya Verbin
2014-06-27  7:33                             ` Bernd Schmidt
2014-07-07 14:51                               ` Ilya Verbin
2014-07-07 15:04                                 ` Bernd Schmidt
2014-07-07 15:14                                   ` Jakub Jelinek
2014-07-07 15:22                                     ` Jakub Jelinek
2014-07-10 18:24                                   ` Ilya Verbin
2014-07-10 18:28                                     ` Bernd Schmidt
2014-07-23 14:18                                 ` Bernd Schmidt
2014-07-23 14:40                                   ` Ilya Verbin
2014-08-04 21:00                                     ` Bernd Schmidt
2014-08-07 17:35                                       ` Thomas Schwinge
2014-09-05 16:36                                         ` [gomp4] Revert more old Makefile/configure bits Bernd Schmidt
2014-09-08  9:42                                           ` Thomas Schwinge
2014-07-24 15:58                                   ` Fwd: [RFC][gomp4] Offloading patches (2/3): Add tables generation Ilya Verbin
2014-05-08  9:44 ` [gomp4] Mark __OPENMP_TARGET__ as addressable (was: Offloading patches (2/3): Add tables generation) Thomas Schwinge
2013-12-20 17:18 [RFC][gomp4] Offloading: Add device initialization and host->target function mapping Ilya Verbin
2013-12-26 13:37 ` Ilya Verbin
2014-01-15 15:09   ` Ilya Verbin
2014-03-12 18:21     ` Ilya Verbin
2014-03-17 13:10       ` Ilya Verbin
2014-03-17 15:12       ` Fwd: [RFC][gomp4] Offloading patches (2/3): Add tables generation Thomas Schwinge
2014-03-17 16:49         ` Jakub Jelinek
2014-03-18 17:36         ` Ilya Verbin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).