guile-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Guile-commits] 136/437: Update code to build and pass test cases in the


From: Andy Wingo
Subject: [Guile-commits] 136/437: Update code to build and pass test cases in the arm port.
Date: Mon, 2 Jul 2018 05:14:03 -0400 (EDT)

wingo pushed a commit to branch lightning
in repository guile.

commit 1c9088beca341765f5260a1ae6f3289b494d4eb8
Author: pcpa <address@hidden>
Date:   Mon Dec 3 14:27:27 2012 -0200

    Update code to build and pass test cases in the arm port.
    
        * configure.ac, include/lightning/jit_private.h, lib/jit_arm-cpu.c,
        lib/jit_arm-swf.c, lib/jit_arm.c, check/Makefile.am: Correct
        implementation of the arm backend port to build and pass the
        current test cases. Tested on armv7 with softfp abi.
    
        * lib/jit_disasm.c: Rename and change prototype of static
        disassemble function as in the arm backend it is required
        to access state information stored in the jit_state_t object.
    
        * check/3to2.tst, check/add.tst: Correct test case code assuming
        JIT_RO and JIT_RET are the same, and even if they are the same,
        the logic was incorrect because it must always call jit_retval*
        to fetch a function call return before any other instruction.
        The arm backend hash a special condition if jit_retval is not
        called, because "r0" is not JIT_R0, but is JIT_RET and *also*
        the first argument for a called function, so JIT_RET must be
        only used as an argument to jit_retval.
    
        * TODO: New file listing important tasks to be resolved.
---
 ChangeLog                       | 20 +++++++++++++
 TODO                            | 11 +++++++
 check/3to2.tst                  |  6 ++--
 check/Makefile.am               |  2 +-
 check/add.tst                   |  1 +
 configure.ac                    |  4 +++
 include/lightning/jit_private.h | 10 ++++++-
 lib/jit_arm-cpu.c               | 14 +++++----
 lib/jit_arm-swf.c               |  6 +++-
 lib/jit_arm.c                   | 65 ++++++++++++++++++++++++-----------------
 lib/jit_disasm.c                | 32 ++++++++++----------
 11 files changed, 119 insertions(+), 52 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 760d29e..87da374 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,25 @@
 2012-12-03 Paulo Andrade <address@hidden>
 
+       * configure.ac, include/lightning/jit_private.h, lib/jit_arm-cpu.c,
+       lib/jit_arm-swf.c, lib/jit_arm.c, check/Makefile.am: Correct
+       implementation of the arm backend port to build and pass the
+       current test cases. Tested on armv7 with softfp abi.
+
+       * lib/jit_disasm.c: Rename and change prototype of static
+       disassemble function as in the arm backend it is required
+       to access state information stored in the jit_state_t object.
+
+       * check/3to2.tst, check/add.tst: Correct test case code assuming
+       JIT_RO and JIT_RET are the same, and even if they are the same,
+       the logic was incorrect because it must always call jit_retval*
+       to fetch a function call return before any other instruction.
+       The arm backend hash a special condition if jit_retval is not
+       called, because "r0" is not JIT_R0, but is JIT_RET and *also*
+       the first argument for a called function, so JIT_RET must be
+       only used as an argument to jit_retval.
+
+2012-12-03 Paulo Andrade <address@hidden>
+
        * check/all.tst, check/lightning.c: Only declare or use 64 bit
        interfaces on 64 bit builds.
 
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..8cb2657
--- /dev/null
+++ b/TODO
@@ -0,0 +1,11 @@
+       * Remove JIT_RET and JIT_FRET. Only interface to these should
+       be jit_retval, jit_retval_f and jit_retval_d, otherwise one
+       may use JIT_RET and/or JIT_FRET as argument to other jit calls,
+       what may cause problems at least on the arm backend.
+
+       * Update documentation to match new implementation.
+
+       * Make an sparc port to not remove previous functionality.
+
+       * Test and correct the ppc and mips ports, after the import and
+       adaptation of the code to lightning.
diff --git a/check/3to2.tst b/check/3to2.tst
index 5804ab6..10de7f8 100644
--- a/check/3to2.tst
+++ b/check/3to2.tst
@@ -22,9 +22,10 @@ test_double_##a##_##b##_##c:                 \
                pushargi_d x                    \
                pushargi_d y                    \
        finishi test_double_##a##_##b##_##c     \
+       retval_d %f0                            \
        prepare 1                               \
                pushargi dfmt                   \
-               pushargr_d %fret                \
+               pushargr_d %f0                  \
        finishi @printf
 
 #define def_test_int(a, b, c)                  \
@@ -42,9 +43,10 @@ test_int_##a##_##b##_##c:                    \
                pushargi x                      \
                pushargi y                      \
        finishi test_int_##a##_##b##_##c        \
+       retval %r0                              \
        prepare 1                               \
                pushargi ifmt                   \
-               pushargr %ret                   \
+               pushargr %r0                    \
        finishi @printf
 
 def_test_double(f0, f0, f0)
diff --git a/check/Makefile.am b/check/Makefile.am
index 6445b6e..450cee9 100644
--- a/check/Makefile.am
+++ b/check/Makefile.am
@@ -16,7 +16,7 @@ AM_CFLAGS = -I$(top_srcdir)/include -D_GNU_SOURCE
 
 check_PROGRAMS = lightning
 
-lightning_LDADD = $(top_builddir)/lib/liblightning.la -ldl
+lightning_LDADD = $(top_builddir)/lib/liblightning.la -lm -ldl
 
 $(top_builddir)/lib/liblightning.la:
        cd $(top_builddir)/lib; $(MAKE) $(AM_MAKEFLAGS) liblightning.la
diff --git a/check/add.tst b/check/add.tst
index 61500cd..03a6962 100644
--- a/check/add.tst
+++ b/check/add.tst
@@ -21,6 +21,7 @@ main:
                pushargi 5
                pushargi 4
        finishi test
+       retval %r0
        prepare 1
                pushargi fmt
                pushargi 5
diff --git a/configure.ac b/configure.ac
index bac8e05..de78e10 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,6 +71,10 @@ AM_CONDITIONAL(cpu_arm,  [test cpu-$cpu = cpu-arm])
 AM_CONDITIONAL(cpu_mips, [test cpu-$cpu = cpu-mips])
 AM_CONDITIONAL(cpu_ppc,  [test cpu-$cpu = cpu-ppc])
 AM_CONDITIONAL(cpu_x86,  [test cpu-$cpu = cpu-x86])
+if test $cpu = arm; then
+    AC_CHECK_LIB(m, sqrtf, ,
+       [AC_MSG_ERROR([sqrtf required but not available])])
+fi
 
 AC_SUBST([LIGHTNING_CFLAGS])
 
diff --git a/include/lightning/jit_private.h b/include/lightning/jit_private.h
index fca88ee..afbfd10 100644
--- a/include/lightning/jit_private.h
+++ b/include/lightning/jit_private.h
@@ -261,10 +261,18 @@ struct jit_state {
 #  if DISASSEMBLER
     struct {
        jit_data_info_t  *ptr;
-       it_word_t         offset;
+       jit_word_t        offset;
        jit_word_t        length;
     } data_info;                       /* constant pools information */
 #  endif
+    /* Note that this field is somewhat hackish, but required by most
+     * ways to implement jit, unless implementing a pure one function
+     * per jit, as most times it needs to start the jit buffer with a
+     * jump where the "main" prolog starts, and because the initial
+     * code is in "arm mode", need to make an "arm mode" patch on that
+     * jump. A good example is the test suite assembler, where most
+     * test cases start with a "jmpi main" call. */
+    jit_uword_t                  thumb;
     struct {
        jit_uint8_t      *data;         /* pointer to code */
        jit_word_t        size;         /* size data */
diff --git a/lib/jit_arm-cpu.c b/lib/jit_arm-cpu.c
index 089ad38..ff2b319 100644
--- a/lib/jit_arm-cpu.c
+++ b/lib/jit_arm-cpu.c
@@ -203,8 +203,8 @@ extern unsigned     __aeabi_uidivmod(unsigned, unsigned);
 #  define THUMB2_CMNI                  0xf1100000
 #  define ARM_TST                      0x01100000
 #  define THUMB_TST                        0x4200
-#  define THUMB2_TST                   0xea100f00
-#  define THUMB2_TSTI                  0xf0100f00
+#  define THUMB2_TST                   0xea100000
+#  define THUMB2_TSTI                  0xf0100000
 #  define ARM_TEQ                      0x01300000
 /* branch */
 #  define ARM_BX                       0x012fff10
@@ -2429,7 +2429,8 @@ _jmpi(jit_state_t *_jit, jit_word_t i0)
     jit_word_t         w;
     jit_word_t         d;
     w = _jit->pc.w;
-    if (jit_thumb_p()) {
+    /* if thumb and in thumb mode */
+    if (jit_thumb_p() && _jit->thumb) {
        d = ((i0 - w) >> 1) - 2;
        if (d >= -1024 && d <= 1023)
            T1_B(d & 0x7ff);
@@ -2460,7 +2461,8 @@ _jmpi_p(jit_state_t *_jit, jit_word_t i0)
     jit_word_t         w;
     jit_word_t         d;
     w = _jit->pc.w;
-    if (jit_thumb_p()) {
+    /* if thumb and in thumb mode */
+    if (jit_thumb_p() && _jit->thumb) {
        d = ((i0 - w) >> 1) - 2;
        assert(_s24P(d));
        T2_B(encode_thumb_jump(d));
@@ -3569,6 +3571,8 @@ _prolog(jit_state_t *_jit, jit_node_t *node)
         * a pointer to a jit function) */
        ADDI(_R12_REGNO, _R15_REGNO, 1);
        BX(_R12_REGNO);
+       if (!_jit->thumb)
+           _jit->thumb = _jit->pc.w;
        if (jit_cpu.abi) {
            T2_PUSH(0x3f0|(1<<_FP_REGNO)|(1<<_LR_REGNO));
            VPUSH_F64(_D8_REGNO, 8);
@@ -3617,7 +3621,7 @@ _patch_at(jit_state_t *_jit,
     } u;
     u.w = instr;
     if (kind == arm_patch_jump) {
-       if (jit_thumb_p()) {
+       if (jit_thumb_p() && instr >= _jit->thumb) {
            code2thumb(thumb.s[0], thumb.s[1], u.s[0], u.s[1]);
            if ((thumb.i & THUMB2_B) == THUMB2_B) {
                d = ((label - instr) >> 1) - 2;
diff --git a/lib/jit_arm-swf.c b/lib/jit_arm-swf.c
index 9155ff8..cc311df 100644
--- a/lib/jit_arm-swf.c
+++ b/lib/jit_arm-swf.c
@@ -18,6 +18,8 @@
 #if PROTO
 /* match vfpv3 result */
 #define NAN_TO_INT_IS_ZERO             1
+extern float   sqrtf(float);
+extern double  sqrt(double);
 extern float   __addsf3(float, float);
 extern double  __adddf3(double, double);
 extern float   __aeabi_fsub(float, float);
@@ -47,7 +49,7 @@ extern int    __aeabi_dcmpun(double, double);
 #  define swf_ff(i0,r0,r1)             _swf_ff(_jit,i0,r0,r1)
 static void
 _swf_ff(jit_state_t*,float(*)(float),jit_int32_t,jit_int32_t) maybe_unused;
-#  define swf_dd(i0,r0,r1)             _swf_id(_jit,i0,r0,r1)
+#  define swf_dd(i0,r0,r1)             _swf_dd(_jit,i0,r0,r1)
 static void
 _swf_dd(jit_state_t*,double(*)(double),jit_int32_t,jit_int32_t) maybe_unused;
 #  define swf_fff(i0,r0,r1,r2)         _swf_fff(_jit,i0,r0,r1,r2)
@@ -138,6 +140,8 @@ static void 
_swf_absr_d(jit_state_t*,jit_int32_t,jit_int32_t);
 static void _swf_negr_f(jit_state_t*,jit_int32_t,jit_int32_t);
 #  define swf_negr_d(r0,r1)            _swf_negr_d(_jit,r0,r1)
 static void _swf_negr_d(jit_state_t*,jit_int32_t,jit_int32_t);
+#  define swf_sqrtr_f(r0,r1)           swf_ff(sqrtf,r0,r1)
+#  define swf_sqrtr_d(r0,r1)           swf_dd(sqrt,r0,r1)
 #  define swf_addr_f(r0,r1,r2)         swf_fff(__addsf3,r0,r1,r2)
 #  define swf_addi_f(r0,r1,i0)         swf_fff_(__addsf3,r0,r1,i0)
 #  define swf_addr_d(r0,r1,r2)         swf_ddd(__adddf3,r0,r1,r2)
diff --git a/lib/jit_arm.c b/lib/jit_arm.c
index d75136a..319703e 100644
--- a/lib/jit_arm.c
+++ b/lib/jit_arm.c
@@ -189,7 +189,7 @@ void
 _jit_init(jit_state_t *_jit)
 {
     jit_int32_t                regno;
-    _jit->reglen = esize(_rvs) - 1;
+    _jit->reglen = jit_size(_rvs) - 1;
     /* jit_get_cpu() should have been already called, and only once */
     if (!jit_cpu.vfp) {
        /* cause register to never be allocated, because simple
@@ -212,7 +212,7 @@ _jit_prolog(jit_state_t *_jit)
        jit_epilog();
     assert(jit_regset_cmp_ui(_jit->regarg, 0) == 0);
     jit_regset_set_ui(_jit->regsav, 0);
-    offset = _jit->functions->offset;
+    offset = _jit->functions.offset;
     if (offset >= _jit->functions.length) {
        _jit->functions.ptr = realloc(_jit->functions.ptr,
                                      (_jit->functions.length + 16) *
@@ -320,7 +320,6 @@ void
 _jit_epilog(jit_state_t *_jit)
 {
     assert(_jit->function);
-
     _jit->function->stack = ((_jit->function->self.alen -
                              /* align stack at 8 bytes */
                              _jit->function->self.aoff) + 7) & -8;
@@ -344,7 +343,7 @@ _jit_arg(jit_state_t *_jit)
     return (offset);
 }
 
-ebool_t
+jit_bool_t
 _jit_arg_reg_p(jit_state_t *_jit, jit_int32_t offset)
 {
     return (offset >= 0 && offset < 4);
@@ -367,7 +366,7 @@ _jit_arg_f(jit_state_t *_jit)
     return (offset);
 }
 
-ebool_t
+jit_bool_t
 _jit_arg_f_reg_p(jit_state_t *_jit, jit_int32_t offset)
 {
     return (jit_arg_reg_p(offset));
@@ -402,7 +401,7 @@ _jit_arg_d(jit_state_t *_jit)
     return (offset);
 }
 
-ebool_t
+jit_bool_t
 _jit_arg_d_reg_p(jit_state_t *_jit, jit_int32_t offset)
 {
     return (jit_arg_reg_p(offset));
@@ -543,7 +542,7 @@ _jit_pushargr_f(jit_state_t *_jit, jit_int32_t u)
 }
 
 void
-_jit_pushargi_f(jit_state_t *_jit, efloat32_t u)
+_jit_pushargi_f(jit_state_t *_jit, jit_float32_t u)
 {
     jit_int32_t                regno;
 
@@ -598,7 +597,7 @@ _jit_pushargr_d(jit_state_t *_jit, jit_int32_t u)
 }
 
 void
-_jit_pushargi_d(jit_state_t *_jit, efloat64_t u)
+_jit_pushargi_d(jit_state_t *_jit, jit_float64_t u)
 {
     jit_int32_t                regno;
 
@@ -746,22 +745,24 @@ _jit_emit(jit_state_t *_jit)
        jit_node_t      *node;
        jit_uint8_t     *data;
        jit_word_t       word;
+       jit_uword_t      thumb;
        jit_int32_t      info_offset;
        jit_int32_t      const_offset;
        jit_int32_t      patch_offset;
     } undo;
 
-    jit_epilog();
+    if (_jit->function)
+       jit_epilog();
     jit_optimize();
 
     _jit->emit = 1;
 
-    _jit->code_length = 16 * 1024 * 1024;
-    _jit->code = mmap(NULL, _jit->code_length,
-                     PROT_EXEC | PROT_READ | PROT_WRITE,
-                     MAP_PRIVATE | MAP_ANON, -1, 0);
+    _jit->code.length = 16 * 1024 * 1024;
+    _jit->code.ptr = mmap(NULL, _jit->code.length,
+                         PROT_EXEC | PROT_READ | PROT_WRITE,
+                         MAP_PRIVATE | MAP_ANON, -1, 0);
     assert(_jit->code.ptr != MAP_FAILED);
-    _jit->pc.uc = _jit->code;
+    _jit->pc.uc = _jit->code.ptr;
 
     /* clear jit_flag_patch from label nodes if reallocating buffer
      * and starting over
@@ -774,6 +775,7 @@ _jit_emit(jit_state_t *_jit)
     undo.word = 0;
     undo.node = NULL;
     undo.data = NULL;
+    undo.thumb = 0;
     undo.info_offset = undo.const_offset = undo.patch_offset = 0;
 #  define assert_data(node)            /**/
 #define case_rr(name, type)                                            \
@@ -1147,6 +1149,7 @@ _jit_emit(jit_state_t *_jit)
                case_vvf(div);
                case_vv(abs, _f);
                case_vv(neg, _f);
+               case_vv(sqrt, _f);
                case_vv(ext, _f);
                case_vv(ld, _f);
                case_vw(ld, _f);
@@ -1231,6 +1234,7 @@ _jit_emit(jit_state_t *_jit)
                case_vvd(div);
                case_vv(abs, _d);
                case_vv(neg, _d);
+               case_vv(sqrt, _d);
                case_vv(ext, _d);
                case_vv(ld, _d);
                case_vw(ld, _d);
@@ -1344,9 +1348,10 @@ _jit_emit(jit_state_t *_jit)
                undo.node = node;
                undo.word = _jit->pc.w;
                undo.data = _jit->consts.data;
+               undo.thumb = _jit->thumb;
                undo.const_offset = _jit->consts.offset;
                undo.patch_offset = _jit->patches.offset;
-               if (_jit->data_info)
+               if (_jit->data_info.ptr)
                    undo.info_offset = _jit->data_info.offset;
            restart_function:
                _jit->again = 0;
@@ -1365,9 +1370,10 @@ _jit_emit(jit_state_t *_jit)
                    _jit->pc.w = undo.word;
                    invalidate_consts();
                    _jit->consts.data = undo.data;
+                   _jit->thumb = undo.thumb;
                    _jit->consts.offset = undo.const_offset;
                    _jit->patches.offset = undo.patch_offset;
-                   if (_jit->data_info)
+                   if (_jit->data_info.ptr)
                        _jit->data_info.offset = undo.info_offset;
                    goto restart_function;
                }
@@ -1423,9 +1429,9 @@ _jit_emit(jit_state_t *_jit)
 
     flush_consts();
     for (offset = 0; offset < _jit->patches.offset; offset++) {
-       assert(patches[offset] & arm_patch_node);
+       assert(_jit->patches.ptr[offset].kind & arm_patch_node);
        node = _jit->patches.ptr[offset].node;
-       word = _jit->patches[offset].inst;
+       word = _jit->patches.ptr[offset].inst;
        if (node->code == jit_code_movi) {
            if (jit_thumb_p())
                value = node->v.n->u.w;
@@ -1446,7 +1452,7 @@ _jit_emit(jit_state_t *_jit)
        }
        else
            value = node->u.n->u.w;
-       patch_at(patches[offset] & ~arm_patch_node, word, value);
+       patch_at(_jit->patches.ptr[offset].kind & ~arm_patch_node, word, value);
     }
 
     __clear_cache(_jit->code.ptr, _jit->pc.uc);
@@ -1618,7 +1624,6 @@ _flush_consts(jit_state_t *_jit)
 {
     jit_word_t          word;
     jit_int32_t                 offset;
-    jit_word_t         *vector;
 
     /* if no forward constants */
     if (!_jit->consts.length)
@@ -1632,12 +1637,18 @@ _flush_consts(jit_state_t *_jit)
     memcpy(_jit->consts.data, _jit->consts.values, _jit->consts.size);
     _jit->pc.w += _jit->consts.size;
 
-    if (_jit->data_info) {
-       if (_jit->data_info->offset + 2 >= _jit->data_info->length)
-           erenew_vector(_jit->data_info, _jit->data_info->length + 1024);
-       vector = _jit->data_info->v.obj;
-       vector[_jit->data_info->offset++] = word;
-       vector[_jit->data_info->offset++] = _jit->consts.size;
+    if (_jit->data_info.ptr) {
+       if (_jit->data_info.offset >= _jit->data_info.length) {
+           _jit->data_info.ptr = realloc(_jit->data_info.ptr,
+                                         (_jit->data_info.length + 1024) *
+                                         sizeof(jit_data_info_t));
+           memset(_jit->data_info.ptr + _jit->data_info.length, 0,
+                  1024 * sizeof(jit_data_info_t));
+           _jit->data_info.length += 1024;
+       }
+       _jit->data_info.ptr[_jit->data_info.offset].code = word;
+       _jit->data_info.ptr[_jit->data_info.offset].length = _jit->consts.size;
+       ++_jit->data_info.offset;
     }
 
     for (offset = 0; offset < _jit->consts.offset; offset += 2)
@@ -1687,7 +1698,7 @@ _patch(jit_state_t *_jit, jit_word_t instr, jit_node_t 
*node)
        _jit->patches.length += 1024;
     }
     _jit->patches.ptr[_jit->patches.offset].kind = kind;
-    _jit->patches.ptr[_jit->patches.offset].instr = instr;
+    _jit->patches.ptr[_jit->patches.offset].inst = instr;
     _jit->patches.ptr[_jit->patches.offset].node = node;
     ++_jit->patches.offset;
 }
diff --git a/lib/jit_disasm.c b/lib/jit_disasm.c
index 625419b..c0a5079 100644
--- a/lib/jit_disasm.c
+++ b/lib/jit_disasm.c
@@ -29,8 +29,9 @@ disasm_compare_symbols(const void *ap, const void *bp);
 static void
 disasm_print_address(bfd_vma addr, struct disassemble_info *info);
 
+#define disassemble(u, v)      _disassemble(_jit, u, v)
 static void
-disassemble(jit_pointer_t code, jit_int32_t length);
+_disassemble(jit_state_t *_jit, jit_pointer_t code, jit_int32_t length);
 #endif
 
 /*
@@ -238,19 +239,20 @@ disasm_print_address(bfd_vma addr, struct 
disassemble_info *info)
 }
 
 static void
-disassemble(jit_pointer_t code, jit_int32_t length)
+_disassemble(jit_state_t *_jit, jit_pointer_t code, jit_int32_t length)
 {
-    int                         bytes;
+    int                        bytes;
 #if __arm__
-    jit_data_info_t    *data_info;
-    jit_int32_t                 data_offset;
+    jit_int32_t                offset;
+    jit_bool_t         data_info;
+    jit_int32_t                data_offset;
 #endif
-    bfd_vma             pc = (jit_uword_t)code;
-    bfd_vma             end = (jit_uword_t)code + length;
-    char                buffer[address_buffer_length];
+    bfd_vma            pc = (jit_uword_t)code;
+    bfd_vma            end = (jit_uword_t)code + length;
+    char               buffer[address_buffer_length];
 
 #if __arm__
-    data_info = _jit->data_info;
+    data_info = 1;
     data_offset = 0;
 #endif
     disasm_info.buffer = code;
@@ -260,16 +262,16 @@ disassemble(jit_pointer_t code, jit_int32_t length)
 #if __arm__
     again:
        if (data_info) {
-           while (data_info.ptr[data_offset].code < pc) {
+           while (_jit->data_info.ptr[data_offset].code < pc) {
                data_offset += 2;
-               if (data_offset >= data_info.length) {
-                   data_info = NULL;
+               if (data_offset >= _jit->data_info.length) {
+                   data_info = 0;
                    goto again;
                }
            }
-           if (pc == data_info.ptr[data_offset].code) {
-               line = data_info.ptr[data_offset].length;
-               for (; line >= 4; line -= 4, pc += 4) {
+           if (pc == _jit->data_info.ptr[data_offset].code) {
+               offset = _jit->data_info.ptr[data_offset].length;
+               for (; offset >= 4; offset -= 4, pc += 4) {
                    bytes = sprintf(buffer, address_buffer_format, pc);
                    (*disasm_info.fprintf_func)(disasm_stream,
                                                "%*c0x%s\t.data\t0x%08x\n",



reply via email to

[Prev in Thread] Current Thread [Next in Thread]