重复字符串指令是否使用额外的段(ES)?

时间:2017-01-08 15:19:50

标签: pthreads thread-local-storage

我正在调查rep字符串指令的工作原理。关于指令的描述,rep movsl例如具有以下助记符。

rep movsl   5+4*(E)CX   Move (E)CX dwords from [(E)SI] to ES:[(E)DI]

其中ES是额外的段寄存器,应该包含一些偏移到存储器中额外段的开头。

操作的伪代码如下所示

while (CX != 0) {
        *(ES*16 + DI) = *(DS*16 + SI);
        SI++;
        DI++;
        CX--;
}

但是在平坦的内存模型中,似乎rep字符串与额外的段一起运行是不正确的。

例如,我创建了一个测试,创建了2个线程,使用rep movs将数组复制到TLS(线程本地存储)数组。逻辑上这不应该起作用,因为TLS数据保存在GS段中,而不是ES中。但是有效。至少看到运行测试的正确结果。 英特尔编译器生成以下代码用于处理。

movl      %gs:0, %eax                                   #27.18
movl      $1028, %ecx                                   #27.18
movl      32(%esp), %esi                                #27.18
lea       TLS_data1@NTPOFF(%eax), %edi                  #27.18
movl      %ecx, %eax                                    #27.18
shrl      $2, %ecx                                      #27.18
rep                                                     #27.18
movsl                                                   #27.18
movl      %eax, %ecx                                    #27.18
andl      $3, %ecx                                      #27.18
rep                                                     #27.18
movsb                                                   #27.18

这里%edi指向TLS数组并且rep movs存储在那里。如果rep mov隐式使用ES偏移(我怀疑),那么这样的代码不应该产生正确的结果。

我在这里想念一下吗?

我创建了测试:

#define _MULTI_THREADED
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define                 NUMTHREADS   2 
#define                 N  257 

typedef struct {
    int   data1[N];
} threadparm_t; 

__thread threadparm_t TLS_data1;

void foo();

void *theThread(void *parm)
{
    int               rc;
    threadparm_t     *gData;

    pthread_t self = pthread_self();
    printf("Thread %u: Entered\n", self);

    gData = (threadparm_t *)parm;

    TLS_data1 = *gData;

    foo();
    return NULL;
}

void foo() {
    int i;
    pthread_t self = pthread_self();
    printf("\nThread %u: foo()\n", self/1000000);
    for (i=0; i<N; i++) {
        printf("%d ", TLS_data1.data1[i]);
    }
    printf("\n\n");
}


int main(int argc, char **argv)
{
    pthread_t             thread[NUMTHREADS];
    int                   rc=0;
    int                   i,j;
    threadparm_t          gData[NUMTHREADS];

    printf("Enter Testcase - %s\n", argv[0]);

    printf("Create/start threads\n");
    for (i=0; i < NUMTHREADS; i++) { 
    /* Create per-thread TLS data and pass it to the thread */
        for (j=0; j < N; j++) { 
           gData[i].data1[j] = i+1;
        }
      rc = pthread_create(&thread[i], NULL, theThread, &gData[i]);
   }
   printf("Wait for the threads to complete, and release their resources\n");
   for (i=0; i < NUMTHREADS; i++) {
      rc = pthread_join(thread[i], NULL);
   }

   printf("Main completed\n");
   return 0;
}

1 个答案:

答案 0 :(得分:0)

你缺少的是这些操作:

movl      %gs:0, %eax
...
lea       TLS_data1@NTPOFF(%eax), %edi

将线程本地TLS_data1的从零开始的地址加载到%edi,这将与零基ES段一起正常工作。