inline fn
inline fn
함수를 정의할 때 inline 키워드를 붙이면, 그 함수는 호출 지점(callsite)에서 의미론적으로 인라인(inline) 되어요. 겉보기엔 컴파일러가 최적화할 때 참고할 만한 힌트처럼 보이지만, 사실 이 키워드는 함수 호출에 관여하는 타입과 값에 실제로 영향을 줍니다. 뒤에서 보겠지만 이건 "그냥 힌트"가 아니에요.
일반 함수 호출과 달리, inline 함수의 호출 지점에서 컴파일 타임에 알려진 인자는 Compile Time Parameters로 취급됩니다. 이 효과는 경우에 따라 함수의 반환값까지 전파될 수 있어요. 직접 확인해 볼게요.
여기서 주의할 점은 코드의 if 조건이 정수 리터럴(1200과 34)로 호출한다는 거예요. inline 덕분에 이 값들이 컴파일 타임 상수로 취급되면서, a + b도 컴파일 시점에 계산될 수 있습니다.
본문
inline 키워드를 함수 정의에 추가하면 그 함수는 호출 지점에서 의미론적으로 인라인 됩니다. 이건 최적화 과정에서 지켜볼 수 있는 힌트이기는 하지만(Inline Is Not A Hint), 그보다 함수 호출에 관여하는 타입과 값에 직접적인 영향을 준다는 점이 핵심이에요.
일반 함수 호출과 달리, inline 함수 호출 지점의 인자 중 컴파일 타임에 알려진 것은 Compile Time Parameters로 취급됩니다. 그리고 이건 잠재적으로 함수의 반환값까지 전파될 수 있죠.
// inline_call.zig
const std = @import("std");
pub fn main() void {
if (foo(1200, 34) != 1234) {
@compileError("bad");
}
}
inline fn foo(a: i32, b: i32) i32 {
std.debug.print("runtime a = {} b = {}", .{ a, b });
return a + b;
}
$ zig build-exe inline_call.zig
$ ./inline_call
runtime a = 1200 b = 34
만약 여기서 inline을 지우면, 이 테스트는 통과하지 못하고 @compileError("bad")로 컴파일 에러가 나게 됩니다. 즉 인라인이 아니라면 foo(1200, 34)가 반환하는 값을 컴파일러가 미리 알 수 없어서, 그 비교 결과가 상수가 되지 못하기 때문이에요.
Inline Is Not A Hint
inline을 쓴다는 건, 그만큼 컴파일러가 할 수 있는 일이 제한된다는 뜻이기도 합니다. 바이너리 크기, 컴파일 속도, 심지어 런타임 성능까지 나빠질 수 있어요. 그 대신, 이렇게 쓰고 싶은 경우들을 해결해 줍니다:
- 특정 스택 프레임이 호출 스택에 존재함을 보장할 수 있어요. 디버깅 목적이거나 Assembly와 연동할 때 유용합니다.
- 호출 지점에서 마침 comptime인 인자들을 강제로 함수의 반환값까지 전파시킵니다. 위의 예시가 바로 이 경우죠.
- 컴파일러 백엔드 구현의 한계를 우회할 때도 씁니다.
그냥 "이 함수를 인라인하면 좋겠다"는 힌트만 컴파일러에 전달하고 싶다면, inline 대신 @branchHint를 사용하면 돼요.
더 알아보기(Learn more)
- Compile Time Parameters — inline 함수 인자의 컴파일 타임 상수 취급과 관련돼요.
- comptime — 컴파일 타임에 평가되는 값이 어디까지 전파되는지.
- Assembly — 스택 프레임 보장이 필요한 어셈블리 연동 사례.
- @branchHint — 인라인 힌트만 필요할 때 쓰는 대안.