인자 전달 규약

인자 전달 규약 (Argument passing conventions)

gfortran이 함수·서브루틴의 인자를 어떻게 전달하는지(참조·값·숨은 인자 등) 자세히 다뤄요. C와 연동하거나 ABI를 이해할 때 꼭 필요한 내용이에요.

출처: Argument passing conventions

본문

서브루틴은 값(C99의 void에 해당)을 반환하지 않아요. 반면 함수는 플랫폼 ABI가 정한 대로 값을 반환하거나, 결과 변수를 **숨은 인자(hidden argument)**로 함수에 넘기고 아무 결과도 반환하지 않아요. 숨은 결과 변수를 쓰는 경우는 결과 변수가 배열이거나 CHARACTER 타입일 때예요.

인자는 플랫폼 ABI에 따라 전달돼요. 특히 복소수 인자는 실수부·허수부를 가진 두 실수로 된 struct와 호환되지 않을 수 있어요. 인자 전달은 C99의 _Complex와 일치해요. 스칼라 복소수 결과 변수를 가진 함수는 값을 반환하고 참조 인자를 쓰지 않아요. 단, -ff2c 옵션을 쓰면 인자 전달이 수정되어 더 이상 플랫폼 ABI와 완전히 일치하지 않게 돼요. 일부 다른 포트란 컴파일러는 기본적으로 f2c 의미를 쓰는데, 이는 상호운용성에 문제를 일으킬 수 있어요.

GNU Fortran은 대부분의 인자를 참조로 전달해요 — 즉 데이터에 대한 포인터를 넘기는 방식이에요. 의미상 필요하다면 컴파일러가 실제 인자를 복사해 넣는 임시 변수를 쓸 수도 있어요(copy-in/copy-out).

ALLOCATABLE·POINTER 속성(프로시저 포인터 포함)을 가진 인자의 경우, 포인터에 대한 포인터를 전달해서 프로시저 안에서 포인터 주소를 수정할 수 있게 해요.

VALUE 속성을 가진 가상 인자의 경우: INTEGER, LOGICAL, REAL, COMPLEX 타입의 스칼라 인자는 플랫폼 ABI에 따라 값으로 전달돼요. (벤더 확장으로, 프로시저 호출에서 %VAL()을 쓰는 것도 같은 효과를 줘요. 다만 권장되진 않아요.) TYPE(C_PTR)와 프로시저 포인터는 포인터 자체를 전달해서, 호출자를 건드리지 않고 수정할 수 있게 해요.

부울(LOGICAL) 인자에 관해서는, GCC는 정수 값 0과 1만 기대한다는 점을 알아둬야 해요. GNU Fortran의 LOGICAL 변수에 다른 정수 값이 들어 있으면 결과가 정의되지 않아요. 일부 다른 포트란 컴파일러는 .TRUE.-1을 쓰기 때문에, 값을 INTEGER로 전달하는 등 각별히 주의해야 해요. (같은 값 제한은 GCC의 다른 프론트엔드(C99 컴파일러의 _Bool, Ada 컴파일러의 Boolean)에도 적용돼요.)

CHARACTER 타입 인자의 경우, 대응하는 가상 인자가 TYPE(*)로 선언되지 않았다면 문자 길이가 인자 목록 끝에 숨은 인자로 전달돼요. 지연 길이(deferred-length) 문자열은 값이 참조로 전달되고, 그 외에는 값으로 전달돼요. 문장 길이의 C 타입은 size_t(포트란에서는 INTEGER(kind=C_SIZE_T))예요. 참고로 이건 옛 GNU Fortran 버전(숨은 문자 길이 인자의 타입이 C int였던)과 다르니, 옛 버전과의 호환성을 유지하려면 예를 들어 다음 포트란 프로시저에 대해

subroutine fstrlen (s, a)
   character(len=*) :: s
   integer :: a
   print*, len(s)
end subroutine fstrlen

C 프로토타입을 이렇게 정의하면 돼요.

#if __GNUC__ > 7
typedef size_t fortran_charlen_t;
#else
typedef int fortran_charlen_t;
#endif

void fstrlen_ (char*, int*, fortran_charlen_t);

이런 컴파일러별 세부사항을 피하려면, 새 코드는 대신 ISO_C_BINDING 기능을 쓰는 게 권장돼요.

C 바인딩의 경우 CHARACTER(len=1) 결과 변수는 플랫폼 ABI에 따라 반환되고 가상 인자에는 숨은 길이 인자가 없어요. VALUE가 붙으면 그 변수들은 값으로 전달돼요.

OPTIONAL 가상 인자의 경우, 없는 인자는 NULL 포인터로 표시돼요. 단, VALUE 속성을 가진 내장 타입·파생 타입(but not CLASS)의 스칼라 가상 인자는 예외예요. 그런 인자에는 숨은 부울 인자(logical(kind=C_bool),value)로 인자가 존재하는지 표시해요.

assumed-shape·assumed-rank·deferred-rank 배열이나, -fcoarray=lib일 때의 할당 가능한 스칼라 코어레이(coarray)는 **배열 디스크립터(array descriptor)**를 사용해요. 나머지 배열은 배열 첫 요소의 주소를 전달해요. -fcoarray=lib에서는 비할당 코어레이 가상 인자에 속한 토큰(token)과 오프셋(offset)이 문자 길이 숨은 인자와 함께 숨은 인자로 전달돼요. 토큰은 코어레이를 식별하는 불투명 포인터이고, 오프셋은 코어레이의 기준 주소와 전달된 스칼라(또는 전달된 배열의 첫 요소) 사이의 바이트 오프셋을 나타내는 C_PTRDIFF_T 종류의 값 전달 정수예요.

인자는 다음 순서로 전달돼요.

  • 결과 변수 (함수 결과를 참조로 전달할 때)
  • 함수 결과의 문자 길이 (결과가 CHARACTER 타입이고 C 바인딩을 쓰지 않을 때)
  • 포트란 선언에 나타나는 순서의 인자들
  • value 속성을 가진 선택적 인자의 존재 여부 (내부적으로 값으로 전달됨)
  • CHARACTER이거나 비할당 코어레이 가상 인자인 첫 인자의 문자 길이 및/또는 코어레이 토큰·오프셋. 그다음 같은 타입의 다음 가상 인자의 숨은 인자들

더 알아보기

  • ISO_C_BINDING을 통한 C 상호운용
  • VALUE·OPTIONAL 속성
  • f2c 호환 옵션 (-ff2c)