public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v3]  LoongArch:Implement 128-bit floating point functions in gcc.
@ 2023-08-15 10:39 chenxiaolong
  2023-08-15 10:48 ` Xi Ruoyao
  2023-08-15 20:03 ` Joseph Myers
  0 siblings, 2 replies; 13+ messages in thread
From: chenxiaolong @ 2023-08-15 10:39 UTC (permalink / raw)
  To: gcc-patches; +Cc: xry111, i, xuchenghua, chenglulu, chenxiaolong

	In the implementation process, the "q" suffix function is
        Re-register and associate the "__float128" type with the
        "long double" type so that the compiler can handle the
        corresponding function correctly. The functions implemented
        include __builtin_{huge_valq infq, fabsq, copysignq, nanq,nansq}.
        On the LoongArch architecture, __builtin_{fabsq,copysignq} can
        be implemented with the instruction "bstrins.d", so that its
        optimization effect reaches the optimal value.

gcc/ChangeLog:

	* config/loongarch/loongarch-builtins.cc (DEF_LARCH_FTYPE):
	(enum loongarch_builtin_type):Increases the type of the function.
	(FLOAT_BUILTIN_HIQ):__builtin_{huge_valq,infq}.
	(FLOAT_BUILTIN_FCQ):__builtin_{fabsq,copysignq}.
	(FLOAT_BUILTIN_NNQ):__builtin_{nanq,nansq}.
	(loongarch_init_builtins):
	(loongarch_fold_builtin):
	(loongarch_expand_builtin):
	* config/loongarch/loongarch-protos.h (loongarch_fold_builtin):
	(loongarch_c_mode_for_suffix):Add the declaration of the function.
	* config/loongarch/loongarch.cc (loongarch_c_mode_for_suffix):Add
        the definition of the function.
	(TARGET_FOLD_BUILTIN):
	(TARGET_C_MODE_FOR_SUFFIX):
	* config/loongarch/loongarch.md (infq):Add an instruction template
        to the machine description file to generate information such as
        the icode used by the function and the constructor.
	(<mathq_pattern>):
	(fabsq):
	(copysignq):

libgcc/ChangeLog:

	* config/loongarch/t-softfp-tf:
	* config/loongarch/tf-signs.c: New file.
---
 gcc/config/loongarch/loongarch-builtins.cc | 168 ++++++++++++++++++++-
 gcc/config/loongarch/loongarch-protos.h    |   2 +
 gcc/config/loongarch/loongarch.cc          |  14 ++
 gcc/config/loongarch/loongarch.md          |  69 +++++++++
 libgcc/config/loongarch/t-softfp-tf        |   3 +
 libgcc/config/loongarch/tf-signs.c         |  59 ++++++++
 6 files changed, 313 insertions(+), 2 deletions(-)
 create mode 100644 libgcc/config/loongarch/tf-signs.c

diff --git a/gcc/config/loongarch/loongarch-builtins.cc b/gcc/config/loongarch/loongarch-builtins.cc
index b929f224dfa..2fb0fde0e3f 100644
--- a/gcc/config/loongarch/loongarch-builtins.cc
+++ b/gcc/config/loongarch/loongarch-builtins.cc
@@ -36,6 +36,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "fold-const.h"
 #include "expr.h"
 #include "langhooks.h"
+#include "calls.h"
+#include "explow.h"
 
 /* Macros to create an enumeration identifier for a function prototype.  */
 #define LARCH_FTYPE_NAME1(A, B) LARCH_##A##_FTYPE_##B
@@ -48,9 +50,18 @@ enum loongarch_function_type
 #define DEF_LARCH_FTYPE(NARGS, LIST) LARCH_FTYPE_NAME##NARGS LIST,
 #include "config/loongarch/loongarch-ftypes.def"
 #undef DEF_LARCH_FTYPE
+  LARCH_BUILTIN_HUGE_VALQ,
+  LARCH_BUILTIN_INFQ,
+  LARCH_BUILTIN_FABSQ,
+  LARCH_BUILTIN_COPYSIGNQ,
+  LARCH_BUILTIN_NANQ,
+  LARCH_BUILTIN_NANSQ,
   LARCH_MAX_FTYPE_MAX
 };
 
+/* Count the number of functions with "q" as the suffix.  */
+const int MATHQ_NUMS =	(int)LARCH_MAX_FTYPE_MAX - (int)LARCH_BUILTIN_HUGE_VALQ;
+
 /* Specifies how a built-in function should be converted into rtl.  */
 enum loongarch_builtin_type
 {
@@ -63,6 +74,15 @@ enum loongarch_builtin_type
      value and the arguments are mapped to operands 0 and above.  */
   LARCH_BUILTIN_DIRECT_NO_TARGET,
 
+ /* The function corresponds to  __builtin_{huge_valq,infq}.  */
+  LARCH_BUILTIN_HIQ_DIRECT,
+
+ /* The function corresponds to  __builtin_{fabsq,copysignq}.  */
+  LARCH_BUILTIN_FCQ_DIRECT,
+
+  /* Define the type of the __builtin_{nanq,nansq} function.  */
+  LARCH_BUILTIN_NNQ_DIRECT
+
 };
 
 /* Declare an availability predicate for built-in functions that require
@@ -136,6 +156,24 @@ AVAIL_ALL (hard_float, TARGET_HARD_FLOAT_ABI)
   LARCH_BUILTIN (INSN, #INSN, LARCH_BUILTIN_DIRECT_NO_TARGET, \
 		 FUNCTION_TYPE, AVAIL)
 
+/* Define an float to do funciton {huge_valq,infq}.  */
+#define FLOAT_BUILTIN_HIQ (INSN, FUNCTION_TYPE)	  \
+    { CODE_FOR_ ## INSN,			  \
+    "__builtin_" #INSN,  LARCH_BUILTIN_HIQ_DIRECT,    \
+    FUNCTION_TYPE, loongarch_builtin_avail_default }
+
+/* Define an float to do funciton {fabsq,copysignq}.  */
+#define FLOAT_BUILTIN_FCQ (INSN, FUNCTION_TYPE)	  \
+    { CODE_FOR_ ## INSN,			  \
+    "__builtin_" #INSN,  LARCH_BUILTIN_FCQ_DIRECT,    \
+    FUNCTION_TYPE, loongarch_builtin_avail_default }
+
+/* Define an float to do funciton {nanq,nansq}.  */
+#define FLOAT_BUILTIN_NNQ (INSN, FUNCTION_TYPE)      \
+    { CODE_FOR_ ## INSN,				\
+    "__builtin_" #INSN,  LARCH_BUILTIN_NNQ_DIRECT,	  \
+    FUNCTION_TYPE, loongarch_builtin_avail_default }
+
 static const struct loongarch_builtin_description loongarch_builtins[] = {
 #define LARCH_MOVFCSR2GR 0
   DIRECT_BUILTIN (movfcsr2gr, LARCH_USI_FTYPE_UQI, hard_float),
@@ -183,6 +221,14 @@ static const struct loongarch_builtin_description loongarch_builtins[] = {
   DIRECT_NO_TARGET_BUILTIN (asrtgt_d, LARCH_VOID_FTYPE_DI_DI, default),
   DIRECT_NO_TARGET_BUILTIN (syscall, LARCH_VOID_FTYPE_USI, default),
   DIRECT_NO_TARGET_BUILTIN (break, LARCH_VOID_FTYPE_USI, default),
+
+  FLOAT_BUILTIN_HIQ (huge_valq, LARCH_BUILTIN_HUGE_VALQ),
+  FLOAT_BUILTIN_HIQ (infq, LARCH_BUILTIN_INFQ),
+  FLOAT_BUILTIN_FCQ (fabsq, LARCH_BUILTIN_FABSQ),
+  FLOAT_BUILTIN_FCQ (copysignq, LARCH_BUILTIN_COPYSIGNQ),
+  FLOAT_BUILTIN_NNQ (nanq, LARCH_BUILTIN_NANQ),
+  FLOAT_BUILTIN_NNQ (nansq, LARCH_BUILTIN_NANSQ),
+
 };
 
 /* Index I is the function declaration for loongarch_builtins[I], or null if
@@ -255,10 +301,13 @@ loongarch_init_builtins (void)
   const struct loongarch_builtin_description *d;
   unsigned int i;
   tree type;
+  tree const_string_type
+     =build_pointer_type (build_qualified_type (char_type_node,
+						TYPE_QUAL_CONST));
 
   /* Iterate through all of the bdesc arrays, initializing all of the
      builtin functions.  */
-  for (i = 0; i < ARRAY_SIZE (loongarch_builtins); i++)
+  for (i = 0; i < ARRAY_SIZE (loongarch_builtins)-MATHQ_NUMS; i++)
     {
       d = &loongarch_builtins[i];
       if (d->avail ())
@@ -270,6 +319,63 @@ loongarch_init_builtins (void)
 	  loongarch_get_builtin_decl_index[d->icode] = i;
 	}
     }
+   /* Register the type long_double_type_node as a built-in type and
+     give it an alias "__float128".  */
+  (*lang_hooks.types.register_builtin_type) (long_double_type_node,
+					    "__float128");
+
+      type = build_function_type_list (long_double_type_node, NULL_TREE);
+      d = &loongarch_builtins[i];
+      loongarch_builtin_decls[i]
+	=add_builtin_function ("__builtin_huge_valq", type,
+			     i,	BUILT_IN_MD, NULL, NULL_TREE);
+      loongarch_get_builtin_decl_index[d->icode]=i++;
+
+      type = build_function_type_list (long_double_type_node, NULL_TREE);
+      d = &loongarch_builtins[i];
+      loongarch_builtin_decls[i]
+	=add_builtin_function ("__builtin_infq",  type,
+				i,  BUILT_IN_MD,  NULL,	NULL_TREE);
+      loongarch_get_builtin_decl_index[d->icode]=i++;
+
+      type = build_function_type_list (long_double_type_node,
+				       long_double_type_node,
+				       NULL_TREE);
+      d = &loongarch_builtins[i];
+      loongarch_builtin_decls[i]
+	=add_builtin_function ("__builtin_fabsq", type,
+			       i,  BUILT_IN_MD,  NULL,  NULL_TREE);
+      TREE_READONLY (loongarch_builtin_decls[i]) =1;
+      loongarch_get_builtin_decl_index[d->icode]=i++;
+
+      type = build_function_type_list (long_double_type_node,
+				       long_double_type_node,
+				       long_double_type_node,
+				       NULL_TREE);
+      d = &loongarch_builtins[i];
+      loongarch_builtin_decls[i]
+	=add_builtin_function ("__builtin_copysignq", type,
+			       i,  BUILT_IN_MD,  NULL,  NULL_TREE);
+      TREE_READONLY (loongarch_builtin_decls[i]) =1;
+      loongarch_get_builtin_decl_index[d->icode]=i++;
+
+      type=build_function_type_list (long_double_type_node,
+				     const_string_type,
+				     NULL_TREE);
+      d = &loongarch_builtins[i];
+      loongarch_builtin_decls[i]
+	=add_builtin_function ("__builtin_nanq", type,
+			       i, BUILT_IN_MD,  "__nanq", NULL_TREE);
+      TREE_READONLY (loongarch_builtin_decls[i]) =1;
+      loongarch_get_builtin_decl_index[d->icode]=i++;
+
+      d = &loongarch_builtins[i];
+      loongarch_builtin_decls[i]
+	=add_builtin_function ("__builtin_nansq", type,
+			       i, BUILT_IN_MD,  "__nansq",  NULL_TREE);
+      TREE_READONLY (loongarch_builtin_decls[i]) =1;
+      loongarch_get_builtin_decl_index[d->icode]=i;
+
 }
 
 /* Implement TARGET_BUILTIN_DECL.  */
@@ -282,6 +388,42 @@ loongarch_builtin_decl (unsigned int code, bool initialize_p ATTRIBUTE_UNUSED)
   return loongarch_builtin_decls[code];
 }
 
+tree
+loongarch_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED,
+			tree *args, bool ignore ATTRIBUTE_UNUSED)
+{
+  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+    {
+      enum loongarch_function_type fn_code
+	 = (enum loongarch_function_type) DECL_MD_FUNCTION_CODE (fndecl);
+      switch (fn_code)
+	{
+	  case LARCH_BUILTIN_NANQ:
+	  case LARCH_BUILTIN_NANSQ:
+	    {
+	      tree type = TREE_TYPE (TREE_TYPE (fndecl));
+	      const char *str = c_getstr (*args);
+	      int quiet = fn_code == LARCH_BUILTIN_NANQ;
+	      REAL_VALUE_TYPE real;
+
+	      if (str && real_nan (&real, str, quiet, TYPE_MODE (type)))
+		return build_real (type, real);
+	      return NULL_TREE;
+	    }
+
+	  default:
+	    break;
+	}
+      }
+
+#ifdef SUBTARGET_FOLD_BUILTIN
+  return SUBTARGET_FOLD_BUILTIN (fndecl, n_args, args, ignore);
+#endif
+
+  return NULL_TREE;
+}
+
+
 /* Take argument ARGNO from EXP's argument list and convert it into
    an expand operand.  Store the operand in *OP.  */
 
@@ -362,11 +504,33 @@ loongarch_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   switch (d->builtin_type)
     {
     case LARCH_BUILTIN_DIRECT:
+    case LARCH_BUILTIN_FCQ_DIRECT:
       return loongarch_expand_builtin_direct (d->icode, target, exp, true);
 
     case LARCH_BUILTIN_DIRECT_NO_TARGET:
       return loongarch_expand_builtin_direct (d->icode, target, exp, false);
-    }
+
+    case LARCH_BUILTIN_NNQ_DIRECT:
+      return expand_call ( exp ,target , ignore);
+
+    case LARCH_BUILTIN_HIQ_DIRECT:
+      {
+	machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
+	REAL_VALUE_TYPE inf;
+	rtx tmp;
+
+	real_inf (&inf);
+	tmp = const_double_from_real_value (inf, target_mode);
+
+	tmp=validize_mem (force_const_mem (target_mode,tmp));
+
+	if (target ==0)
+	    target =gen_reg_rtx (target_mode);
+	emit_move_insn (target,tmp);
+
+	return target;
+      }
+  }
   gcc_unreachable ();
 }
 
diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h
index b71b188507a..35fc2ad7def 100644
--- a/gcc/config/loongarch/loongarch-protos.h
+++ b/gcc/config/loongarch/loongarch-protos.h
@@ -175,11 +175,13 @@ extern void loongarch_register_frame_header_opt (void);
 /* Routines implemented in loongarch-c.c.  */
 void loongarch_cpu_cpp_builtins (cpp_reader *);
 
+extern tree loongarch_fold_builtin (tree, int, tree*, bool);
 extern void loongarch_init_builtins (void);
 extern void loongarch_atomic_assign_expand_fenv (tree *, tree *, tree *);
 extern tree loongarch_builtin_decl (unsigned int, bool);
 extern rtx loongarch_expand_builtin (tree, rtx, rtx subtarget ATTRIBUTE_UNUSED,
 				     machine_mode, int);
 extern tree loongarch_build_builtin_va_list (void);
+extern machine_mode loongarch_c_mode_for_suffix (char suffix);
 
 #endif /* ! GCC_LOONGARCH_PROTOS_H */
diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
index 86d58784113..7a8358c9630 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -6790,6 +6790,16 @@ loongarch_set_handled_components (sbitmap components)
 	cfun->machine->reg_is_wrapped_separately[regno] = true;
 }
 
+/* Target hook for c_mode_for_suffix.  */
+machine_mode
+loongarch_c_mode_for_suffix (char suffix)
+{
+  if (suffix == 'q')
+    return TFmode;
+
+  return VOIDmode;
+}
+
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
 #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
@@ -6901,6 +6911,10 @@ loongarch_set_handled_components (sbitmap components)
 #define TARGET_BUILTIN_DECL loongarch_builtin_decl
 #undef TARGET_EXPAND_BUILTIN
 #define TARGET_EXPAND_BUILTIN loongarch_expand_builtin
+#undef TARGET_FOLD_BUILTIN
+#define TARGET_FOLD_BUILTIN loongarch_fold_builtin
+#undef TARGET_C_MODE_FOR_SUFFIX
+#define TARGET_C_MODE_FOR_SUFFIX loongarch_c_mode_for_suffix
 
 /* The generic ELF target does not always have TLS support.  */
 #ifdef HAVE_AS_TLS
diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md
index b37e070660f..9fe3faf8b92 100644
--- a/gcc/config/loongarch/loongarch.md
+++ b/gcc/config/loongarch/loongarch.md
@@ -44,6 +44,13 @@ (define_c_enum "unspec" [
   UNSPEC_FSCALEB
   UNSPEC_FLOGB
 
+  UNSPEC_INFQ
+  UNSPEC_HUGE_VALQ
+  UNSPEC_FABSQ
+  UNSPEC_COPYSIGNQ
+  UNSPEC_NANQ
+  UNSPEC_NANSQ
+
   ;; Override return address for exception handling.
   UNSPEC_EH_RETURN
 
@@ -563,6 +570,15 @@ (define_int_attr bytepick_imm [(8 "1")
 				 (48 "6")
 				 (56 "7")])
 
+;; mathq function
+(define_int_iterator MATHQ[UNSPEC_INFQ	 UNSPEC_HUGE_VALQ
+			    UNSPEC_NANQ UNSPEC_NANSQ])
+(define_int_attr mathq_pattern[(UNSPEC_INFQ "infq")
+				(UNSPEC_HUGE_VALQ "huge_valq")
+				(UNSPEC_NANQ "nanq")
+				(UNSPEC_NANSQ "nansq")]
+)
+
 ;;
 ;;  ....................
 ;;
@@ -2008,6 +2024,59 @@ (define_insn "movfcc"
   ""
   "movgr2cf\t%0,$r0")
 
+;; Implements functions with a "q" suffix
+
+(define_insn "<mathq_pattern>"
+  [(unspec:SI[(const_int 0)] MATHQ)]
+  ""
+  "")
+
+;;Implement __builtin_fabsq function.
+
+(define_insn_and_split   "fabsq"
+  [(set (match_operand:TF  0 "register_operand" "=r")
+	(unspec:TF[(match_operand:TF 1 "register_operand" "rG")]
+		    UNSPEC_FABSQ))]
+  ""
+  "#"
+  "reload_completed"
+[(set (zero_extract:DI (match_operand:DI 0 "register_operand")
+			(match_operand:SI 3 "const_int_operand")
+			(match_operand:SI 4 "const_int_operand"))
+      (match_operand:DI 2 "const_int_operand"))]
+{
+	operands[0] = gen_rtx_REG (Pmode, REGNO (operands[0])+1);
+	operands[2] = GEN_INT (0);
+	operands[3] = GEN_INT (1);
+	operands[4] = GEN_INT (63);
+}
+)
+
+;;Implement __builtin_copysignq function.
+
+(define_insn_and_split  "copysignq"
+  [(set (match_operand:TF  0 "register_operand" "=r")
+	(unspec:TF[(match_operand:TF 1 "register_operand" "rG")
+		    (match_operand:TF 2 "register_operand" "rG")]
+		      UNSPEC_COPYSIGNQ))]
+ ""
+ "#"
+ "reload_completed"
+  [(set (match_operand:DI 2 "register_operand")
+	(lshiftrt :DI (match_operand:DI 2 "register_operand")
+	(match_operand:SI 4 "arith_operand")))
+   (set (zero_extract:DI (match_operand:DI 0 "register_operand")
+			  (match_operand:SI 3 "const_int_operand")
+			  (match_operand:SI 4 "const_int_operand"))
+	(match_operand:DI 2 "register_operand"))]
+{
+   operands[0] = gen_rtx_REG (Pmode,REGNO (operands[0])+1);
+   operands[2] = gen_rtx_REG (Pmode,REGNO (operands[2])+1);
+   operands[3] = GEN_INT (1);
+   operands[4] = GEN_INT (63);
+}
+)
+
 ;; Conditional move instructions.
 
 (define_insn "*sel<code><GPR:mode>_using_<GPR2:mode>"
diff --git a/libgcc/config/loongarch/t-softfp-tf b/libgcc/config/loongarch/t-softfp-tf
index 306677b1255..0e7c2b4cabe 100644
--- a/libgcc/config/loongarch/t-softfp-tf
+++ b/libgcc/config/loongarch/t-softfp-tf
@@ -1,3 +1,6 @@
 softfp_float_modes += tf
 softfp_extensions += sftf dftf
 softfp_truncations += tfsf tfdf
+#Used to implement a special 128-bit function with a q suffix
+LIB2ADD += $(srcdir)/config/loongarch/tf-signs.c
+
diff --git a/libgcc/config/loongarch/tf-signs.c b/libgcc/config/loongarch/tf-signs.c
new file mode 100644
index 00000000000..68db1729372
--- /dev/null
+++ b/libgcc/config/loongarch/tf-signs.c
@@ -0,0 +1,59 @@
+/* Copyright (C) 2008-2023 Free Software Foundation, Inc.
+
+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
+Software Foundation; either version 3, or (at your option) any later
+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.
+
+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/>.  */
+
+union _FP_UNION_Q
+{
+   __float128 flt;
+   struct
+   {
+      unsigned long frac0 : 64;
+      unsigned long frac1 : 48;
+      unsigned exp : 15;
+      unsigned sign : 1;
+   } bits __attribute__((packed));
+};
+
+__float128 __nanq (const char *str);
+__float128 __nansq (const char *str);
+
+__float128 __nanq (const char * str)
+{
+  union _FP_UNION_Q nan;
+  nan.bits.frac0 = 0;
+  nan.bits.frac1 = 0;
+  nan.bits.exp = 0x7FFF;
+  nan.bits.sign = 0;
+
+  return nan.flt;
+}
+__float128 __nansq (const char *str)
+{
+  union _FP_UNION_Q nan;
+  nan.bits.frac0 = 0xFFFFFFFFFFFFFFFFUL;
+  nan.bits.frac1 = 0x0000FFFFFFFFFFFFUL;
+  nan.bits.exp = 0x7FFF;
+  nan.bits.sign = 0;
+
+  return nan.flt;
+}
+
-- 
2.20.1


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

end of thread, other threads:[~2023-08-18  7:52 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-15 10:39 [PATCH v3] LoongArch:Implement 128-bit floating point functions in gcc chenxiaolong
2023-08-15 10:48 ` Xi Ruoyao
2023-08-17  6:56   ` chenxiaolong
2023-08-15 20:03 ` Joseph Myers
2023-08-16 10:08   ` chenxiaolong
2023-08-16 13:16     ` Joseph Myers
2023-08-17  3:44   ` Xi Ruoyao
2023-08-17 15:08     ` Joseph Myers
2023-08-18  6:39       ` chenxiaolong
2023-08-18  6:58         ` Xi Ruoyao
2023-08-18  7:05           ` Xi Ruoyao
2023-08-18  7:19             ` Xi Ruoyao
2023-08-18  7:52               ` chenxiaolong

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