비스 님이 쓰신 글 :
: VS2013 프리뷰가 나와서 잠깐 돌려봤는데 IDE가 더 막강해졌더군요
: 디버깅할때 함수호출관계를 그래프로 보여주는 기능은 환상적이던데
:
: 비쥬얼스튜디오는 IDE가 점점 더 막강해지는데, 그에 비하면 RAD IDE는
: 솔직이 메모장 수준 밖에 안되죠.
:
: 저야 실력이 없어서 엄두도 못내지만 RAD bin 디렉토리에 있는 coreide180.bpl
: 같은 것을 이용해서 비쥬얼 스튜디오 IDE에 RAD XE4를 통합할수 있다면
: 굉장히 사용하기 편할거라는 생각이드는데 이런것도 가능할까요?
:
BDS.EXE를 사용하지 않고 MS사의 Visual Studio IDE에 RAD XE4를 통합하는 것은 가능하기는 하나 델파이 쪽에서 닭짓을 해놔서
실현성이 없다고 봐야 함. Tools API, COREIDE180 등을 활용하려고 해도 델파이에서 기본적으로 사용되는 calling convention이
__fastcall 방식인데, __fastcall 호출방식은 컴파일러 업계 표준이 아니고, 컴파일러 벤더마다 방식이 다름.
예를 들어서 Visual C++ 에서
void __fastcall foo(int a, int b, int c, int d, int e)
{
}
은
오른쪽 부터 스택에 e, d, c 순으로 값을 넣고 b와 a는 각기 edx, ecx 레지스터를 이용하는 반면
델파이의 경우는
implementation
procedure foo(a, b, c, d, e: Integer);
begin
end;
스택에 d, e 순으로 값을 넣고 c,b,a를 각기 ecx, edx, eax 레지스터를 이용하고 있음
Visual Studio IDE 핵심 코어는 ATL을 이용한 C++ COM으로 구현되어 있고
예를 들어 IServiceProvider 인터페이스를 예로 들면
typedef struct IServiceProviderVtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
__RPC__in IServiceProvider * This,
/* [in] */ __RPC__in REFIID riid,
/* [annotation][iid_is][out] */
_COM_Outptr_ void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
__RPC__in IServiceProvider * This);
ULONG ( STDMETHODCALLTYPE *Release )(
__RPC__in IServiceProvider * This);
/* [local] */ HRESULT ( STDMETHODCALLTYPE *QueryService )(
IServiceProvider * This,
/* [annotation][in] */
_In_ REFGUID guidService,
/* [annotation][in] */
_In_ REFIID riid,
/* [annotation][out] */
_Outptr_ void **ppvObject);
END_INTERFACE
} IServiceProviderVtbl;
위와 같이 COM 호출방식 표준인 __stdcall 방식으로 되어 있기 때문에
델파이 코드와 인터페이스를 취하기 위해선 __fastcall 호출방식으로 되어 있는 델파이쪽의 모든 프로시져와 펑션들을
__stdcall 로 바꾸기 위한 Thunk 코드를 일일히 구현해 줘야 함. 프로시져와 펑션이 한두개도 아니고 이건 실현성이 없음.
Tools API의 경우도 델파이 랭귀지에선 클래스 다중 상속이 지원되지 않기 때문에 COM Interface 방식으로 구현되어 있는데
멍청한 놈들이 Tools API 마져 calling convention을 COM 표준인 __stdcall을 사용하지 않고, 디폴트 방식인 __fastcall 방식으로
닭짓을 해놓았기 때문에 이 또한 호출방식을 변경하는 Thunk코드를 일일히 구현해 줘야함.
델파이가 __fastcall 방식을 사용한 데는 컴파일러 구현이론에 대한 비하인드 스토리가 있음.
델파이가 처음 나왔을 당시 Code Optimizing 기술은 원시적인 수준이었고, 레지스터를 이용하는 __fastcall 호출방식이
역할을 하기도 했지만, Optimizing 기법이 발달한 지금에 와서는 오히려 __fastcall 방식은 최적화를 방해하는 요소가 돼 버림.
왜냐하면 파라미터 전달로 특정 레지스터 사용이 정해져 버리면, 옵티마이져가 코드를 최적화 할때 레지스터 풀로 사용할
리소스가 제한되어 글로벌 옵티마이징에 방해가 되기 때문임.
void foo()
{
register int a, b, c;
}
옛날엔 실행속도를 높이기 위해서 스택변수를 레지스터를 이용하라는 식으로 가르치기도 했지만, 위에서 언급한 대로
이런 방식은 글로벌 옵티마이징을 오히려 방해하는 요인으로 작용하기 때문에 레지스터를 할당하는 register 같은 키워드도 이젠
역사속으로 사라져 버렸음.
__fastcall 방식은 간단한 루프에서나 효과가 있을 뿐, 펑션 또는 프로시져의 알고리즘이 복잡하면 복잡해질수록 옵티마이징에서는
더 불리한 결과를 초래하게 됨.
그리고 __fastcall 방식의 경우 디버깅 모드에서 최적화 하지도 않는데도 불구하고, 가끔 디버깅시 코드가 최적화 되어서
파라미터를 인스펙트 할수 없다고 나오는데, 이건 엄밀하게 보면 최적화 되어서 그런게 아니고 파라미터와 생성된 코드에서
사용되는 레지스터가 Confilct가 일어나서 디버거가 심볼을 Resoving 할수 없기 때문이지 최적화 때문이 아님.
사족으로 닷넷에 대한 얘기도 보이던데, 닷넷은 결코 느리지 않음.
닷넷이 느리지 않은 이유는 네이티브 컴파일러로는 불가능한 지터의 독특한 내부 동작방식 때문인데,
이건 다음 기회에 다뤄 보겠음. ㅋ
|