0

64ビットを処理するためにこの関数を変更する方法を知っている人はいますか?

{
  unsigned int prev;

  __asm__ __volatile__ (
          " lock; cmpxchgl %1,%2; "
          : "=a"(prev)
          : "q"(new_value), "m"(*(int *)ptr), "0"(old_value)
          : "memory");

  return prev;
}

Brett Hale が親切に提案した代わりにunsigned long prev;andを使用すると、次のエラーが発生します。cmpxchgqcmpxchgl

include/cs.h: Assembler messages:
include/cs.h:26: Error: incorrect register `%esi' used with `q'    suffix
include/cs.h:26: Error: incorrect register `%esi' used with `q' suffix
include/cs.h:26: Error: incorrect register `%esi' used with `q' suffix
include/cs.h:26: Error: incorrect register `%r13d' used with `q' suffix
error: command 'gcc' failed with exit status 1

ブレットの提案がうまくいかなかった理由がわかったと思います。関数入力の変数の型を から に変更する必要がありましintlong。完全を期すために、ここに追加します。

#ifndef __cs__include
#define __cs__include

static inline unsigned int CS(volatile void *ptr,
                              unsigned long old_value, /* was int */
                              unsigned long new_value) /* was int too */
{
  unsigned long prev; /* result */
  volatile unsigned long *vptr = (volatile unsigned long *) ptr;

  __asm__ __volatile__ (

          " lock; cmpxchgq %2, %1; "
          : "=a" (prev), "+m" (*vptr)
          : "r" (new_value), "0" (old_value)
          : "memory");

  return prev;
}

コードはエラーなしでコンパイルされます (ただし、多くの警告があります)。ただし、残念ながらこのプログラムは 64 ビットではまだ動作しません。

4

2 に答える 2

2

組み込みバージョン (__sync スタイル) は次のようになります。

#include <stdint.h>
#include <stdio.h>

uint64_t cas(uint64_t* ptr, uint64_t old_value, uint64_t new_value)
{
    return __sync_val_compare_and_swap(ptr, old_value, new_value);
}

int main()
{
    uint64_t foo = 42;
    uint64_t old = cas(&foo, 42, 1);
    printf("foo=%llu old=%llu\n", (unsigned long long)foo, (unsigned long long)old);
    return 0;
}

これの優れた点は、多くのアーキテクチャで機能することです。x86 では、32 ビット モードで cmpxchg8b を使用し、64 ビット モードで cmpxchgq を使用します。

あなたの質問はあまり明確ではありませんでした.64ビットモード用にコンパイルしている間、32ビット操作を維持するつもりだったのかもしれません. その場合は、uint64_t の代わりに uint32_t を使用してください。

于 2013-06-13T18:31:32.690 に答える
0

まず、おそらくLP64データ モデルを扱っているunsigned long prev;ため、64 ビットの量には次を使用します。cmpxchgl次に、cmpxchgq(64 ビット命令) に置き換えます。

  • オペランド%1には"q"制約があり、選択が%eax%ebx%ecx、または%edxIA32 に制限されています。この制限は x86-64 には適用されません。これによるとのままでも"q"いいのですが、 で記述したほうがいい"r"です。

  • オペランド%2はメモリ入力であり、これも にプロモートする必要がありますunsigned long。おそらく、volatileフェッチとしてもマークする必要があります。これにより、コンパイラがいつメモリをフェッチ/更新するかを事前に決定できなくなります。

  • オペランドは、それが入力であると同時に出力である%3ことを意味し%raxます。変更する必要はありません。


{
  unsigned long prev; /* result */
  volatile unsigned long *vptr = (volatile unsigned long *) ptr;

  __asm__ __volatile__ (

          " lock; cmpxchgq %1,%2; "
          : "=a"(prev)
          : "r"(new_value), "m"(*vptr), "0"(old_value)
          : "memory");

  return prev;
}

ただし、命令"m"によって更新される可能性があるため、入力制約での使用は技術的に正しくありません。gcc のドキュメントcmpxchgqには長年のバグがありましたが、現在は修正されており、(入力と出力の両方としてのメモリ) は許可されていませんでした。より正しい表現は次のとおりです。"+m"

  __asm__ __volatile__ (

          " lock; cmpxchgq %2, %1; " /* notice reversed operand order! */
          : "=a" (prev), "+m" (*vptr)
          : "r" (new_value), "0" (old_value)
          : "memory");
于 2013-06-13T16:38:46.803 に答える