Hi Richard,

Segment registers %fs and %gs are special in 64bit mode.  For a memory
operand "%fs:address", its effective address is the base address of %fs +
address.  The base address of %fs are hidden and "mov %fs, %ax" will only
access the visible part of %fs, which is the 16bit segment selector.

In 64bit mode, UNSPEC_TP refers to the base address of %fs.  To access the
base address of %fs, we can use system call:

        int arch_prctl(int code, unsigned long addr);
        int arch_prctl(int code, unsigned long *addr);

              Set the 64-bit base for the FS register to addr.

              Return the 64-bit base value for the FS register of the
              current thread in the unsigned long pointed to by addr.

we must use the system call to update the base address of %fs,  To read
the base address of %fs, OS arranges that the base address of %fs points
to a struct:

typedef struct
  void *tcb;            /* Pointer to the TCB.  Not necessarily the
                           thread descriptor used by libpthread.  */

and sets up "tcb" == the base address of %fs so that the address of "%fs:0"
is the address of the tcb field.  For x32, the base address of %fs is
between 0 and 0xffffffff.  We can use

"mov{l}\t{%%fs:0, %k0|%k0, DWORD PTR fs:0}"

to move the base address of %fs into %r32 and %r64 directly.  In case
of %r32, we are loading "tcb", which is a 32bit memory.  For %r64, we
are loading "tcb" and zero-extend it to 64bit.  This patch is tested on
Linux/x32 with GCC and glibc with both -maddress-mode=long and
-maddress-mode=short.  OK for trunk?


2012-03-27  H.J. Lu  <hongjiu...@intel.com>

        * config/i386/i386.c (legitimize_pic_address): Load UNSPEC_TP
        into tp_mode register directly.

        * config/i386/i386.md (*load_tp_x32): Removed.
        (*load_tp_x32_zext): Likewise.
        (*load_tp_x32_<mode>): New.

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index a21f2da..14c4056 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -12491,15 +12485,7 @@ legitimize_pic_address (rtx orig, rtx reg)
 static rtx
 get_thread_pointer (enum machine_mode tp_mode, bool to_reg)
-  rtx tp = gen_rtx_UNSPEC (ptr_mode, gen_rtvec (1, const0_rtx), UNSPEC_TP);
-  if (GET_MODE (tp) != tp_mode)
-    {
-      gcc_assert (GET_MODE (tp) == SImode);
-      gcc_assert (tp_mode == DImode);
-      tp = gen_rtx_ZERO_EXTEND (tp_mode, tp);
-    }
+  rtx tp = gen_rtx_UNSPEC (tp_mode, gen_rtvec (1, const0_rtx), UNSPEC_TP);
   if (to_reg)
     tp = copy_to_mode_reg (tp_mode, tp);
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 2d20a52..ac6124e 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -12748,20 +12748,9 @@
 (define_mode_attr tp_seg [(SI "gs") (DI "fs")])
 ;; Load and add the thread base pointer from %<tp_seg>:0.
-(define_insn "*load_tp_x32"
-  [(set (match_operand:SI 0 "register_operand" "=r")
-       (unspec:SI [(const_int 0)] UNSPEC_TP))]
-  "TARGET_X32"
-  "mov{l}\t{%%fs:0, %0|%0, DWORD PTR fs:0}"
-  [(set_attr "type" "imov")
-   (set_attr "modrm" "0")
-   (set_attr "length" "7")
-   (set_attr "memory" "load")
-   (set_attr "imm_disp" "false")])
-(define_insn "*load_tp_x32_zext"
-  [(set (match_operand:DI 0 "register_operand" "=r")
-       (zero_extend:DI (unspec:SI [(const_int 0)] UNSPEC_TP)))]
+(define_insn "*load_tp_x32_<mode>"
+  [(set (match_operand:SWI48x 0 "register_operand" "=r")
+       (unspec:SWI48x [(const_int 0)] UNSPEC_TP))]
   "mov{l}\t{%%fs:0, %k0|%k0, DWORD PTR fs:0}"
   [(set_attr "type" "imov")

Reply via email to