2013-02-21 2 views
2

오픈 소스 소프트웨어 프로젝트에서 gcc atomic builtins : __sync_add_and_fetch 및 __sync_sub_and_fetch를 호출하여 특정 변수에 대한 원자 증분 및 감소를 구현합니다. 나는 정기적으로 내 코드를 컴파일하려고 누군가로부터 이메일을 얻을 수 있지만, 다음과 같은 링커 오류 얻을 : 뒷조사 후__sync_add_and_fetch가 정의되지 않음

refcountobject.cpp:(.text+0xb5): undefined reference to `__sync_sub_and_fetch_4' 
refcountobject.cpp:(.text+0x115): undefined reference to `__sync_add_and_fetch_4' 

을, 나는 사실 근본 원인을 좁혀 그 GCC 자신의 이전 버전 (4.1) 기본값은 i386의 대상 아키텍처입니다. 그리고 분명히 gcc는 실제로 80386에 원자 추가 기능을위한 내장 함수를 가지고 있지 않으므로 암시 적으로 정의되지 않은 __sync_add_and_fetch_4 호출을 여기에 삽입합니다. 이 작동 방식에 대한 훌륭한 설명은 here입니다.

here으로 설명한 쉬운 해결 방법은 컴파일러 플래그 중 하나로 -march = pentium을 추가하도록 Makefile을 수정하도록 알려주는 것입니다. 그리고 모두 좋다.

사용자가 직접 Makefile을 수정하지 않아도되는 장기적인 수정 사항은 무엇입니까?

내가 고려하고 몇 가지 아이디어 :

나는 메이크로 컴파일러 플래그로 = 펜티엄를 -march 하드 코드 싶지 않아요. 나는 그것이 인텔 기반이 아닌 것을 망칠 것이라고 추측하고있다. 그러나 Makefile에 기본 대상이 i386이라는 것을 감지하는 규칙이있는 경우 확실히 추가 할 수있었습니다. 나는 Makefile에서 gcc -dumpmachine을 호출하고 첫 번째 삼중 항을 분석하는 스크립트 인 규칙을 생각하고있다. 문자열이 i386이면 컴파일러 플래그를 추가합니다. 아무도 실제로 80386 머신을 만들지 않을 것이라고 가정합니다.

다른 대안은 링커가 다시 사용하도록 __sync_add_and_fetch_4에 대한 구현을 실제로 제공하는 것입니다. 그것은 정의되고있는 GCC_HAVE_SYNC_COMPARE_AND_SWAP 매크로의 존재를 기반으로 조건부로 컴파일 될 수도 있습니다. 필자는 전역 pthread_mutex를 사용하여 구현을 프로토 타이핑했다. 가장 좋은 성능은 아닐지 모르지만 작동하고 문제를 훌륭하게 해결합니다. x86 용으로 컴파일하는 경우 구현을 위해 "lock xadd"를 호출하는 인라인 어셈블리를 직접 작성하는 것이 더 좋은 아이디어 일 수 있습니다.

답변

1

이것은 다른 해결책입니다. 그것은 특정 상황에서 그것의 장소를 가지고 있을지도 모르지만 위의 makefile + 스크립트 솔루션을 선택했습니다.

이 솔루션은 _sync_add_and_fetch_4, _sync_fetch_and_add_4, _sync_sub_and_fetch_4 및 _sync_fetch_and_sub_4에 대한 로컬 정의를 별도의 소스 파일에 제공합니다. 컴파일러가 기본적으로 생성 할 수없는 경우에만 링크됩니다. 일부 어셈블리가 필요했지만 모든 장소의 Wikipedia은 내가 참조 할 수있는 합리적인 구현을 가졌습니다. (필자는 컴파일러가 다른 것들이 모두 올바른지 추론하기 위해 컴파일러가 일반적으로 생성 한 것을 역 어셈블했다.)

#if defined(__i386) || defined(i386) || defined(__i386__) 
extern "C" unsigned int xadd_4(volatile void* pVal, unsigned int inc) 
{ 

    unsigned int result; 
    unsigned int* pValInt = (unsigned int*)pVal; 

    asm volatile( 
     "lock; xaddl %%eax, %2;" 
     :"=a" (result) 
     : "a" (inc), "m" (*pValInt) 
     :"memory"); 

    return (result); 

} 

extern "C" unsigned int __sync_add_and_fetch_4(volatile void* pVal, unsigned int inc) 
{ 
    return (xadd_4(pVal, inc) + inc); 
} 

extern "C" unsigned int __sync_sub_and_fetch_4(volatile void* pVal, unsigned int inc) 
{ 
    return (xadd_4(pVal, -inc) - inc); 
} 

extern "C" unsigned int __sync_fetch_and_add_4(volatile void* pVal, unsigned int inc) 
{ 
    return xadd_4(pVal, inc); 
} 

extern "C" unsigned int __sync_fetch_and_sub_4(volatile void* pVal, unsigned int inc) 
{ 
    return xadd_4(pVal, -inc); 
} 

#endif 
+0

코드에서 값이 오버플로되면 조건 플래그가 발생하지 않는다고 잘못 가정합니다. clobbered register리스트에'cc '를 추가하십시오. –

0

답글을 쓰지 않고 직접 풀어 봤습니다.

두 가지 가능한 해결책이 있습니다.

먼저 다음 스크립트, getfixupflags.sh를 Makefile과 동일한 디렉토리에 추가하십시오. 이 스크립트는 컴파일러가 이라면 i386을 타겟으로하는을 탐지하고, 그렇다면 출력으로 "-march = pentium"을 출력합니다.

#!/bin/bash 

_cxx=$1 
_fixupflags= 
_regex_i386='^i386' 

if [[ ! -n $_cxx ]]; then echo "_cxx var is empty - exiting" >&2; exit; fi 

_target=`$_cxx -dumpmachine` 
if [[ $_target =~ $_regex_i386 ]]; then 
    _fixupflags="$_fixupflags -march=pentium" 
fi 

if [[ -n $_fixupflags ]]; then echo $_fixupflags; fi 

이제이 스크립트를 사용하도록 Makefile을 수정하십시오. 그런 다음 코드를 컴파일 할 때 FIXUP_FLAGS를 포함하도록 메이크 컴파일러 지시어를 수정 메이크

FIXUP_FLAGS := $(shell getfixupflags.sh $(CXX)) 

에 다음 줄을 추가합니다. 예 :

%.o: %.cpp 
    $(COMPILE.cpp) $(FIXUP_FLAGS) $^ 
+0

대다수가 필요로하지 않는 추가 코드로 핵심 C/C++ 코드 기반을 오염시키지 않으므로이 솔루션이 약간 더 좋습니다. – selbie

+0

그러나 결국, 나는 내 마음을 바꾸고 다른 해결책으로 갔다. – selbie