기술 문서

Delphi _ftol 인라인 어셈블리를 64비트 빌드로 이식하기

· Delphi 프로그래밍

오래된 Delphi 코드에는 부동소수점 값을 정수로 변환하는 작은 _ftol 도우미가 포함된 경우가 있습니다. 32비트 프로젝트에서는 특히 오래된 컴파일러에 맞게 조정된 그래픽 및 PDF 코드에서 이런 도우미를 inline assembler로 작성하는 일이 흔했습니다.

1
2
3
4
5
6
7
8
function _ftol( f: double) : Integer; cdecl;
begin
  asm
    lea   eax, f
    fstp  qword ptr [eax]
  end;
  result := Trunc(f);
end;

같은 유닛을 64비트 대상으로 컴파일하면 이 방식은 깨집니다. Delphi 64비트 컴파일러는 E1025 Unsupported language feature: 'ASM' 를 보고합니다. DCC64는 일반 Pascal 루틴 안에서 Pascal과 inline assembly를 섞는 기능을 지원하지 않기 때문입니다.

64비트 컴파일러가 이를 거부하는 이유

DCC64는 64비트 assembly 루틴을 어셈블할 수 있지만, 루틴은 64비트 호출 규약을 염두에 둔 완전한 assembler 루틴으로 작성되어야 합니다. 32비트 inline assembler를 Pascal 함수 본문에 복사해 놓고 컴파일러가 레지스터 사용, 스택 레이아웃 또는 x87 동작을 변환해 주리라 기대할 수 없습니다.

완전한 assembler 버전은 가능하며, 다음과 같은 코드는 값이 이미 FPU 스택에 있을 때 필요할 수 있는 control-word 처리의 종류를 보여 줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function _ftol: Integer; cdecl;
// Assumes double value is in FPU stack on entry
// Make a truncation to integer and put it into function result
var
  TmpVal: Int64;
  SaveCW, ScratchCW: word;
 
asm
  .NOFRAME
  fnstcw word ptr [SaveCW]
  fnstcw word ptr [ScratchCW]
  or word ptr [ScratchCW], 0F00h  ;// trunc toward zero, full precision
  fldcw word ptr [ScratchCW]
  fistp qword ptr [TmpVal]
  fldcw word ptr [SaveCW]
  mov rax, TmpVal
end;

목표가 단순한 절삭이라면 Pascal을 선호

대부분의 애플리케이션 코드에는 assembler 루틴이 필요 없습니다. 실제 요구가 Double 를 0 방향으로 절삭해 정수 값을 반환하는 것이라면, 명확하고 이식 가능한 버전은 단순히 다음과 같습니다.

1
2
3
4
function _ftol(f: double): integer; cdecl;
begin
  Result := Trunc(f);
end;

Trunc 는 의도를 직접 표현하고, 32비트와 64비트 대상 모두에서 컴파일되며, 아키텍처별 assembler의 유지보수 비용을 피합니다. 또한 이후 유지보수자가 범위 검사와 오버플로 동작을 이해하기 쉽습니다.

마이그레이션 지침

  • 호출 지점을 제어하고 필요한 것이 숫자 변환뿐이라면 Pascal 구현을 사용하십시오.
  • 특정 심벌과 호출 규약이 바이너리 호환성에 필요할 때만 assembler export를 유지하십시오.
  • 호출 규약과 데이터 폭을 검토하지 않고 32비트 레지스터 코드를 64비트 루틴으로 복사하지 마십시오.
  • 음수, 큰 값, 대상 정수 범위를 벗어난 값 같은 경계 사례를 테스트하십시오.

범위와 동작 고려 사항

assembler를 Trunc 로 바꾸는 일도 테스트가 필요한 동작 변경으로 다루어야 합니다. 음수, 정수 경계 근처의 소수, Integer범위를 벗어난 값에 대해 코드가 어떻게 동작해야 하는지 확인하십시오. 오래된 assembler는 FPU control-word 상태에 의존했을 수 있으며, Pascal 버전은 대상 컴파일러의 RTL 구현을 따릅니다.

도우미가 PDF 또는 그래픽 컴포넌트 안에 있다면 회전된 텍스트, 스케일된 좌표, 변환된 그리기 명령을 사용하는 출력을 테스트하십시오. 이런 영역은 단순한 예제보다 부동소수점-정수 변환 차이를 더 잘 드러냅니다.

권장 마이그레이션 경로

먼저 가능한 곳에서는 private 변환 도우미를 명확한 Pascal 코드로 바꾸십시오. 둘째, 여전히 필요할 때만 원래 32비트 구현을 32비트 조건 뒤에 유지하십시오. 셋째, 측정된 성능 요구나 바이너리 호환성 요구가 있을 때만 별도의 64비트 분기를 추가하십시오. 이렇게 하면 이식 가능한 경로를 단순하게 유지하고 아키텍처별 경로를 기본값이 아니라 예외로 만들 수 있습니다.