코드 생성 관례용 옵션(Options for code generation conventions)
코드 생성 관례용 옵션(Options for code generation conventions)
Fortran 소스를 머신 코드로 바꿀 때, 어떤 호출 관례와 저장 방식, 초기화 정책을 쓸지 정해주는 옵션들을 모아둔 문서예요. 대부분은 컴파일러가 만든 바이너리가 다른 코드(특히 C, g77/f2c)와 어떻게 맞물리는지에 영향을 줘요.
본문
이 옵션들은 머신에 독립적이면서, 코드 생성에 쓰이는 인터페이스 관례를 제어해요. 대부분은 긍정·부정 두 형태를 가지는데, -ffoo의 부정형은 -fno-foo예요. 아래 표에는 두 형태 중 기본값이 아닌 쪽만 나열되어 있어요. 반대 형태는 no-를 붙이거나 떼면 알 수 있어요.
-fno-automatic
프로그램 단위를(단, RECURSIVE로 표시된 것 제외) 안에 참조된 모든 지역 변수·배열에 SAVE 문이 붙어 있는 것처럼 취급해요. common 블록에는 영향을 주지 않아요. (일부 Fortran 컴파일러는 이 옵션을 -static이나 -save라는 이름으로 제공해요.)
기본값인 -fautomatic은 -fmax-stack-var-size가 주는 값보다 작은 지역 변수를 스택에 두고, -frecursive를 쓰면 정적 메모리를 전혀 쓰지 않아요. 명시적 SAVE 속성이 있는 지역 변수·배열은 -pedantic을 추가하지 않는 한 조용히 무시돼요.
-ff2c
g77과 f2c가 만든 코드와 호환되도록 설계된 코드를 생성해요. g77이 쓰는 호출 관례(원래 f2c에서 구현됨)는, 기본 REAL을 반환하는 함수가 실제로는 C의 double을 반환하고, COMPLEX를 반환하는 함수는 반환값을 저장할 위치를 가리키는 추가 인자를 거쳐 값을 돌려줘야 해요. 기본 GNU 호출 관례에서는 그런 함수가 GNU C에서처럼 그냥 결과를 돌려줘요. 기본 REAL 함수는 C의 float를, COMPLEX 함수는 GNU C의 complex 타입을 반환하죠.
이 옵션은 -fno-second-underscore를 명시하지 않는 한 -fsecond-underscore를 함께 켜요. libgfortran 라이브러리와 인터페이스하는 코드 생성에는 영향이 없어요.
주의: -ff2c로 컴파일한 Fortran 코드를 기본 -fno-f2c 호출 관례의 코드와 섞는 건 좋지 않아요. 서로 다른 호출 관례로 컴파일된 프로그램 부분 사이에서 COMPLEX나 기본 REAL 함수를 호출하면 실행 시점에 깨질 수 있어요. 또 이 옵션은 기본 REAL/COMPLEX 타입의 내장 함수를 실제 인자로 넘기는 코드를 깨뜨리는데, 라이브러리 구현이 -fno-f2c 호출 관례를 쓰기 때문이에요.
-fno-underscoring
Fortran 소스에 지정된 엔티티 이름에 밑줄을 붙이지 않도록 해요. -funderscoring이 켜져 있으면 GNU Fortran은 외부 이름에 밑줄을 하나 붙여요. 이는 많은 UNIX Fortran 컴파일러가 만든 코드와의 호환을 보장하기 위해서예요. 단, C 바인딩으로 선언된 이름이나 모듈 안의 이름에는 적용되지 않아요.
주의: GNU Fortran의 기본 동작은 f2c와 g77과 호환되지 않아요. GNU Fortran으로 컴파일한 오브젝트 파일을 그 도구들이 만든 오브젝트 코드와 호환되게 하려면 -ff2c를 쓰세요.
-fno-underscoring은 GNU Fortran을 기존 시스템 환경(라이브러리, 도구 등)에 통합하는 실험을 할 때가 아니라면 권장되지 않아요.
예를 들어 -funderscoring과 함께, j()와 max_count()가 외부 함수이고 my_var와 lvar는 지역 변수라고 한다면, I = J() + MAX_COUNT (MY_VAR, LVAR) 같은 Fortran 문은 대략 아래 C 코드처럼 구현돼요.
i = j_() + max_count_(&my_var, &lvar);
-fno-underscoring을 쓰면 같은 문이 이렇게 구현돼요.
i = j() + max_count(&my_var, &lvar);
-fno-underscoring은 디버깅하거나 다른 언어와 연동할 때 사용자 정의 이름을 직접 지정할 수 있게 해줘요. 다만 이름이 맞다고 해서 GNU Fortran이 구현하는 인터페이스가 다른 언어가 같은 이름에 구현하는 인터페이스와 꼭 맞는다는 뜻은 아니에요. 또 밑줄을 붙이지 않으면 사용자 정의 외부 이름이 시스템 라이브러리의 이름과 충돌할 가능성이 생기고, 이 경우 미해결 참조 버그를 찾기 어려워질 수 있어요(런타임에 잘못된 동작으로만 나타날 수 있어요).
이 절의 명령줄 옵션보다는 기호를 bind(C)로 선언하는 편이 다른 언어나 다른 Fortran 컴파일러와 연동할 때 더 견고해요.
-fsecond-underscore
기본적으로 GNU Fortran은 외부 이름에 밑줄을 하나 붙여요. 이 옵션을 쓰면, 밑줄이 있는 이름에는 밑줄을 둘, 없는 이름에는 하나를 붙여요. 예를 들어 MAX_COUNT는 max_count_ 대신 max_count__라는 링크 시점 외부 기호로 구현돼요. g77·f2c 호환에 필요하며 -ff2c를 쓰면 함께 켜져요. -fno-underscoring이 켜져 있으면 효과가 없어요.
-fcoarray=<keyword>
none— coarray 지원을 끔. coarray 선언이나 이미지 제어 문을 쓰면 컴파일 타임 오류가 나요. (기본값)single— 단일 이미지 모드. 즉num_images()가 항상 1이에요.lib— 라이브러리 기반 coarray 병렬화. 적절한 GNU Fortran coarray 라이브러리(예: http://opencoarrays.org)를 링크해야 해요. 또는 GCC의libcaf_single라이브러리를 링크할 수도 있지만, 이건 단일 이미지만 지원해요.
-fcheck=<keyword>
런타임 검사 코드 생성을 켜요. 인자는 쉼표로 구분된 키워드 목록이에요. 앞에 no-를 붙이면 이전 지정으로 활성화된 검사를 끌 수 있어요.
all—-fcheck의 모든 런타임 검사를 켬.array-temps— 실제 인자를 넘길 때 임시 배열을 생성해야 했다면 런타임에 경고. 이 정보는 최적화 때 그런 임시 배열을 피하는 데 유용할 때가 있어요. 경고는 위치당 한 번만 출력돼요.bits— 비트 조작 내장 함수에 유효하지 않은 인자가 전달될 때 런타임 검사.bounds— 배열 첨자와 선언된 최소·최대값에 대한 런타임 검사. 가정·지연 형상 배열의 인덱스를 실제 할당된 경계와 비교하고, 명시적 타입 지정이 없는 문자 배열 생성자에서 모든 문자열 길이가 같도록 보장해요. 일부 검사는 주 프로그램을-fcheck=bounds로 컴파일해야 해요. 앞으로 부분 문자열 참조 검사 같은 다른 형태도 포함될 수 있어요.do— 루프 반복 변수의 무효한 변경에 대한 런타임 검사.mem— 메모리 할당에 대한 런타임 검사.ALLOCATE문의 명시적 할당에는 영향을 주지 않아요(그건 항상 검사됨).pointer— 포인터와 allocatable에 대한 런타임 검사.recursion—recursive로 표시되지 않은 서브루틴·함수가 재귀적으로 호출될 때 런타임 검사. OpenMP 프로그램에서는 동작하지 않으며-frecursive·-fopenmp와 함께 쓰면 꺼져요.
예시:
gfortran -fcheck=all,no-array-temps foo.f90
이 명령은 위에 나온 모든 검사를 켜되, 생성된 배열 임시에 대한 경고만 뺀 채 컴파일해요.
-fbounds-check
-fcheck=bounds의 더 이상 사용되지 않는(deprecated) 별칭이에요.
-ftail-call-workaround
-ftail-call-workaround=n 형태도 있어요. 일부 Fortran 코드에 대한 C 인터페이스가 숨겨진 문자 길이 인자를 생략해서 gfortran ABI를 위반해요. 이로 인해 꼬리 호출 인자 푸시가 스택을 넘칠 수 있어요. 기존 바이너리 패키지를 위한 우회책으로, 이 옵션은 문자 인자를 가진 gfortran 프로시저의 꼬리 호출 최적화를 꺼요. 값에 따라: =2는 문자 인자를 가진 모든 gfortran 프로시저에서 끄고, =1(또는 옵션만 쓸 때)은 암시적으로 프로토타입된 프로시저를 호출하는 문자 인자 gfortran 프로시저에서만 꺼요.
이 옵션은 스택 공간 부족으로 인한 크래시 같은 문제를 일으킬 수 있어요. 해당 코드를 고치는 것이 매우 강력히 권장돼요. -fc-prototypes-external로 gfortran ABI에 맞는 프로토타입을 생성해 소스에 포함시킬 수 있어요. 이 옵션 지원은 향후 릴리스에서 제거될 가능성이 커요. 부정형 -fno-tail-call-workaround 또는 -ftail-call-workaround=0으로 끌 수 있어요. 기본값은 현재 -ftail-call-workaround이며 향후 바뀔 거예요.
-fcheck-array-temporaries
-fcheck=array-temps의 deprecated 별칭이에요.
-fmax-array-constructor=n
배열 생성자에서 허용되는 상한을 늘릴 때 써요. 아래 코드는 컴파일 타임에 배열을 확장하기 위해 이 옵션이 필요해요.
program test
implicit none
integer j
integer, parameter :: n = 100000
integer, parameter :: i(n) = (/ (2*j, j = 1, n) /)
print '(10(I0,1X))', i
end program test
주의: 이 옵션은 컴파일 시간이 길어지고 오브젝트 파일이 지나치게 커질 수 있어요. n의 기본값은 65535예요.
-fmax-stack-var-size=n
스택에 놓는 가장 큰 배열의 크기(바이트)를 지정해요. 이 크기를 넘으면 정적 메모리를 사용해요(RECURSIVE로 표시된 프로시저 제외). RECURSIVE 속성이 없는 재귀 프로시저나 병렬 프로그램을 허용하려면 -frecursive를, 스택을 절대 쓰지 않으려면 -fno-automatic을 써요. 현재 이 옵션은 상수 경계로 선언된 지역 배열에만 영향을 주고, 모든 문자 변수에 적용되지는 않을 수 있어요. n의 기본값은 65536이에요.
-fstack-arrays
크기가 알려지지 않은 모든 배열과 배열 임시를 스택 메모리에 두게 해요. 매우 큰 지역 배열을 쓰면 운영체제에 따라 스택 메모리 런타임 한도를 늘려야 할 수 있어요. 이 플래그는 -fmax-stack-var-size를 지정하지 않으면 최적화 수준 -Ofast에서 기본적으로 켜져요.
-fpack-derived
파생 타입의 멤버를 최대한 빽빽하게 배치하라고 지시해요. 이 옵션으로 컴파일한 코드는 이 옵션 없이 컴파일한 코드와 호환되지 않을 가능성이 크고, 더 느리게 실행될 수 있어요.
-frepack-arrays
어떤 상황에서 GNU Fortran은 비연속적인 메모리 영역을 기술하는 디스크립터를 통해 가정 형상 배열 구간을 전달할 수 있어요. 이 옵션은 함수 프롤로그에 데이터를 런타임에 연속 블록으로 다시 패킹하는 코드를 추가해요. 배열 접근은 빨라질 수 있지만, 특히 전달된 데이터가 비연속적일 때 함수 호출 오버헤드가 커질 수 있어요.
-fshort-enums
-fshort-enums로 컴파일한 C 코드와의 상호운용을 위해 제공돼요. GNU Fortran이 주어진 열거자 집합에 맞는 가장 작은 INTEGER 종류를 고르고 모든 열거자에 그 종류를 부여하게 해요.
-finline-arg-packing
프로시저의 가정 형상 인자를, 가정 크기나 명시적 크기 인자로, 또는 명시적 인터페이스가 없는 프로시저의 인자로 넘길 때 그 인자를 패킹(연속 메모리에 배치)해야 할 수 있어요. -finline-arg-packing이 켜져 있으면 이 패킹이 인라인 코드로 수행돼요. 최적화 기회는 늘지만 코드 크기도 늘어나요. 이 옵션은 -Os로 크기 최적화할 때를 제외하고는 어떤 -O 옵션에서든 암시돼요. 패킹해야 할 인자가 아주 많으면 코드 크기와 컴파일 시간이 과도해질 수 있는데, 그때는 끄는 게 나을 수 있어요. 패킹 사례는 -Warray-temporaries로 찾을 수 있어요.
-fexternal-blas
행렬이 주어진 한계보다 클 때(참고: -fblas-matmul-limit) MATMUL 같은 일부 행렬 연산에 대해 자체 알고리즘 대신 BLAS 함수 호출을 생성하게 해요. 최적화된 벤더 BLAS 라이브러리가 있으면 유리할 수 있어요. BLAS 라이브러리는 링크 시점에 지정해야 하고, 이 옵션은 기본 종류(32비트) 정수 인자를 가진 BLAS 라이브러리를 지정해요. -fexternal-blas64와 함께 쓸 수 없어요.
-fexternal-blas64
같은 방식으로 BLAS 호출을 생성하되, KIND=8(64비트) 정수 인자를 가진 BLAS 라이브러리를 지정해요. -fexternal-blas와 함께 쓸 수 없고 64비트 시스템이 필요해요. 또 -ffrontend-optimize가 필요해요.
-fblas-matmul-limit=n
-fexternal-blas나 -fexternal-blas64가 켜져 있을 때만 의미가 있어요. 크기가 n 이상인 행렬 곱셈은 BLAS 호출로, 나머지는 gfortran 내부 알고리즘으로 처리돼요. 정사각이 아니면 인자·결과 행렬 차원의 기하 평균으로 크기 비교를 해요. n의 기본값은 30이에요.
-finline-intrinsics
내장 함수를 구현하기 위해 libgfortran 함수 호출 대신 인라인 코드 생성을 선호해요. 대부분의 내장 함수는 구현 방식이 하나뿐이라 선택지가 없지만, 일부는 라이브러리 함수 또는 인라인 코드 중 선택할 수 있고 인라인이 최적화 기회가 더 많은 경우가 많아요. -finline-intrinsics=... 또는 -fno-inline-intrinsics=...는 쉼표 목록에 있는 내장 함수에만 적용돼요. 선택이 없으면 최적화에 따라 기본 동작이 정해지는데, 최적화하지 않거나 크기 최적화하면 라이브러리 호출, 그 외에는 인라인을 선호해요. 현재 인자로 허용되는 내장 함수는 MAXLOC와 MINLOC로 제한되고, DIM 인자가 없고 ARRAY가 비문자 타입인 호출에만 효과가 있어요. 순위 1 인자 + DIM 인자인 MAXLOC(A(:),DIM=1) 같은 경우는 숫자 순위 1 배열 인자에 대해 무조건 인라인돼요.
-finline-matmul-limit=n
프론트엔드 최적화가 활성화되면 일부 MATMUL 호출이 인라인돼요. 컴파일 타임에 행렬 크기를 정할 수 없으면 두 경우 모두 코드를 생성해서 코드 크기가 늘어날 수 있어요. -finline-matmul-limit=0은 모든 경우 인라인을 꺼요. n 값은 최대 n 크기 행렬까지 인라인 코드를 만들어요. 정사각이 아니면 기하 평균으로 비교해요. 기본값은 30이고, -fblas-matmul-limit으로 바꿀 수 있어요.
-frecursive
모든 지역 배열을 스택에 할당하게 해서 간접 재귀를 허용해요. -fmax-stack-var-size=나 -fno-automatic과 함께 쓸 수 없어요.
-finit-local-zero 계열
-finit-local-zero, -finit-derived, -finit-integer=n, -finit-real=<zero|inf|-inf|nan|snan>, -finit-logical=<true|false>, -finit-character=n
-finit-local-zero는 지역 INTEGER, REAL, COMPLEX 변수를 0으로, LOGICAL을 false로, CHARACTER를 널 바이트 문자열로 초기화하라고 지시해요. 더 세밀한 옵션으로는 -finit-integer=n, -finit-real=<zero|inf|-inf|nan|snan>(지역 COMPLEX의 실수·허수부도 초기화), -finit-logical=<true|false>, -finit-character=n(n은 ASCII 문자 값)이 있어요.
-finit-derived를 쓰면 파생 타입 변수의 컴포넌트도 이 플래그들에 따라 초기화돼요. 명시적 -finit-* 플래그가 적용되지 않는 타입의 컴포넌트는 -finit-local-zero처럼 취급돼요.
이 옵션들은 POINTER 속성이 있는 객체, allocatable 배열, EQUIVALENCE 문에 나타나는 변수를 초기화하지 않아요. (이 제한은 향후 릴리스에서 없어질 수 있어요.)
-finit-real=nan은 REAL·COMPLEX 변수를 quiet NaN으로 초기화해요. signalling NaN은 -finit-real=snan으로 쓰는데, 컴파일 타임 최적화가 quiet NaN으로 바꿀 수 있고 트래핑(-ffpe-trap 등)이 켜져 있어야 해요. -finit-integer는 호스트의 INTEGER(kind=C_LONG)으로 값을 해석해서 Fortran 정수 변수에 대입하는데, 종류에 비해 값이 크면 래핑(wraparound)될 수 있어요.
마지막으로, -finit-* 옵션을 켜면 해당 지역 변수에 대해 -Wuninitialized가 내던 경고가 잠잠해져요.
-falign-commons
기본적으로 gfortran은 COMMON 블록의 모든 변수가 제대로 정렬되도록 패딩해요. 어떤 플랫폼에서는 필수이고, 다른 플랫폼에서는 성능을 높여요. COMMON 블록이 모든 곳에서 일관된 데이터 타입으로 선언되지 않으면 이 패딩이 문제를 일으킬 수 있어요. -fno-align-commons로 자동 정렬을 끌 수 있고, COMMON 블록을 공유하는 모든 파일에서 같은 형태의 이 옵션을 써야 해요. 정렬 문제를 피하려면 객체를 큰 것부터 작은 것 순으로 배치하는 걸 권장해요.
-fno-protect-parens
기본적으로 모든 최적화 수준에서 식의 괄호를 존중해서 컴파일러가 재연관(reassociation)을 하지 않아요. -fno-protect-parens를 쓰면 REAL·COMPLEX 식을 재배치해서 더 빠른 코드를 만들 수 있어요. 재연관 최적화에는 -fno-signed-zeros와 -fno-trapping-math가 켜져 있어야 해요. 괄호 보호는 -Ofast를 주지 않으면 기본적으로 켜져 있어요.
-frealloc-lhs
내장 대입에서 allocatable 좌변(lhs)이 할당되지 않았거나 모양이 다르면 자동으로 (재)할당해요. -std=f95를 주지 않으면 기본적으로 켜져 있어요. -Wrealloc-lhs도 함께 보세요.
-faggressive-function-elimination
문 안에서 인자 목록이 동일한 함수를 제거해요. 함수가 PURE로 표시됐는지와는 무관해요. 예를 들어 a = f(b,c) + f(b,c)에서는 f에 대한 호출이 하나만 남아요. -ffrontend-optimize가 켜져 있을 때만 동작해요.
-ffrontend-optimize
Fortran 파스 트리의 일부를 조작해서 프론트엔드 최적화를 수행하는 옵션이에요. -O0와 -Og를 제외한 어떤 -O 옵션에서도 기본으로 켜져요. 이 옵션이 켜는 최적화는 다음을 포함해요.
MATMUL호출 인라인- 식 안에서 동일한 함수 호출 제거
- 비교·대입에서 불필요한
TRIM호출 제거 TRIM(a)를a(1:LEN_TRIM(a))로 대체- 논리 연산자(
.AND.,.OR.)의 단락(short-circuit) 평가
-fno-frontend-optimize로 끌 수 있어요.
-ffrontend-loop-interchange
이득이 있는 곳에서 Fortran 프론트엔드가 루프를 교환(interchange)하도록 시도해요. 어떤 -O 옵션에서든 기본으로 켜져요. 현재는 여러 forall 삼중항을 가진 FORALL과 DO CONCURRENT 문에만 영향을 줘요.
백엔드가 제공하는 더 많은 옵션은 GCC 사용 설명서의 코드 생성 관례 절을 참고하면 돼요(gfortran, gcc, 그리고 다른 GNU 컴파일러가 공유하는 백엔드).
더 알아보기
- 이름·인자 전달 관례가 궁금하면 'Naming and argument-passing conventions' 문서가 함께 도움이 돼요.
- C언어와 연동할 때는 밑줄 치환 옵션보다
bind(C)(ISO_C_BINDING)를 쓰는 편이 훨씬 견고해요. - 런타임 검사(
-fcheck)와 초기화(-finit-*)는 디버깅과 크래시 원인 파악에 실질적인 도움을 줘요. 다만 성능과 코드 크기에 영향을 주니 필요할 때 골라서 켜는 게 좋아요. - 오브젝트 파일 호환성(특히
-ff2c, 언더스코어링)은 오래된 도구와 섞을 때만 신경 쓰면 돼요. 새 프로젝트라면 기본 호출 관례를 유지하는 게 안전해요.