CPU별 어셈블리어 의존성에 관한 질문

CPU별로 사용하는 명령어 셋이 조금씩 다르기 때문에, 프로그램 빌드 시 어셈블러가 현재 로컬의 CPU에 알맞는 기계어로 치환해준다고 들었습니다.

따라서 A사의 CPU를 이용해서 빌드한 프로그램은 B사의 CPU를 사용하고있는 환경에서는 올바르게 실행이 되지 않기때문에 B사의 CPU 환경에 맞게 다시 컴파일해야한다고 들었습니다.

그렇다면 현재 흔하게 배포되어 다운받을 수 있는 설치 프로그램들은 (ex. 각종 게임들, 카카오톡 메신저, 기타 드라이버)

어떻게 이용자들의 CPU에 알맞는, 적절하게 매칭된 설치 프로그램을 넘겨줄 수 있는 걸까요?

시중에 배포되어있는 프로그램들을 보면 OS별로는 나뉘어져는 있으나, 각 CPU별로는 보통 구분이 안되어 있지 않습니까?

너무 궁금합니다. 지식을 나눠주셨으면 합니다.

1 Like

각 아키텍처에 맞게 컴파일된 바이너리를 모두 포함하거나, 모든 아키텍처에서 동일한 동작을 하게끔 제작된 층을 거치거나(예: JVM), 소스파일을 받아 사용자가 자신의 환경에 맞게 컴파일해서 쓰도록 하는 방법 등이 있습니다.

1 Like

높은 추상도를 가진 프로그램은 서로 다른 OS에서도 실행 가능합니다. 이를 크로스 플랫폼이라고 합니다.

흔히 쓰는 애플리케이션들은 운영체제가 제공하는 API 위에서 동작합니다.

윈도우에서 동작되는 프로그램들은 모두 Windows API 위에서 동작하기 때문에 윈도우만 설치될 수 있다면 특별한 경우가 아닌 이상 기기에 대한 의존성을 고려할 필요가 없습니다.

1 Like

다른 분들이 API쪽이나 언어/런타임쪽을 말씀해주셨으니, 저는 명령어쪽으로 약간 뇌피셜 + 잡지식을 덧붙여보겠습니다

1. 컴파일러

(편의를 위해 컴파일러를 C->어셈블리어, 어셈블러를 어셈블리어->기계어 변환기로 정의합니다)

컴파일러에서 어셈블리 코드를 생성할 때, CPU의 특정 기능을 쓰는걸 옵션으로 허용/제한할 수 있습니다. 옵션들의 기본값들은 배포판의 종류, 시간의 흐름에 따라 바뀌기도 합니다.

# without SSE2 instructions
$ gcc -S -o a.s a.c -mno-sse2

SSE2 - 위키백과: 요즘은 거의 어디서나 쓰이는 명령어 집합이라서, 아마 기본적으로 켜져있읍니다

경우에 따라, 루틴을 어셈블리어로 작성하는 경우도 있습니다. 기계어 변환의 바로 앞 단에서 이루어지는 단계인만큼, 어셈블리 코드 한줄한줄이 어셈블러에 의해 각각 1:1 대응되는 기계어로 번역됩니다.
제가 아는 선에서는 어셈블러 단에서 명령어를 임의로 바꾸는 경우는 없지만, 링커 단에서 안 쓰이는 기계어 코드 조각 파일들을 생략하는 경우는 봤던 것 같습니다.

(.c, .s 둘다 가능)
$ gcc -c memcpy-intel.c -o memcpy-intel.o
$ gcc -c memcpy-amd.c -o memcpy-amd.o

# 링크 시
$ ld a.o b.o ... memcpy-[intel/amd].o -o ./program
$ ./program

요러면 링커에서 어떤 오브젝트 파일을 선택하느냐에 따라 다른 memcpy를 쓰는 프로그램이 나오게 됩니다.

2. 런타임

런타임에서 CPU의 기능 지원 여부에 따라 함수를 골라서 실행하는 경우도 보였습니다. 프로그램 시작 시 CPU에서 제공하는 명령어의 결과에 따라 (ex. x86의 cpuid) 최적화된 memset을 사용하는 경우가 있었습니다.

int cpu_info;

void detect_cpu() {
  asm { cpuid; mov cpu_info, eax; }
}

void memcpy(void *dest, void *src, size_t n) {
  if(cpu_info & SUPPORTS_SSE2)
    return memcpy_sse2_optimized(dest, src, n);
  else
    return memcpy_slow(dest, src, n);
}

int main() {
  detect_cpu();
  // ...
  memcpy(dest, src, n);
}

뭔가 그래도, CPU의 기능들이 모델마다 다르더라도 일반 PC용 CPU들은 어느정도 하위호환성이 유지되는 것 같습니다. 개인적으로 컴파일러 옵션으로 어느정도 맞춰준 후 런타임에서 최적화하는 패턴이 좀 보였던거같습니다. 사용자의 코드가 아닌, 공유 라이브러리단에서 수행하는 경우도 있었는데, 뭔가 이런류도 기기마다 빡세게 튜닝을 돌리는 곳이 있을지 궁금하군요…

2 Likes

질문하신 내용에 대하서만 답을 하자면, 그 배포된 프로그램들은 모두 같은 CPU 환경에서 실행될 것으로 가정하고 배포되는 것입니다. PC에서 쓰이는 x86_64 또는 x86 환경에서요.
x86_64는 흔히 쓰이는 64비트 CPU 환경이고 x86은 32비트 환경이죠.

물론 세상에는 이외에도 많은 CPU 종류가 있고, 운영체제도 고려 요소이므로 여러 환경을 지원하는 소프트웨어는 이것들을 다 명시하거나, 소스코드로 배포됩니다.

2 Likes