#include <stdio.h>
void handle_positive(void) {
printf("Positive!\n");
}
void handle_negative(void) {
printf("Negative!\n");
}
void cleanup(void) {
printf("Cleanup!\n");
}
void process(int x) {
if (x > 0) {
handle_positive();
} else {
handle_negative();
}
cleanup();
}
int main() {
process(5);
process(-3);
return 0;
}
SanitizerCoverage로 컴파일하여 어셈블리 확인
clang -S -O1 -fsanitize-coverage=trace-pc,trace-cmp -o test_coverage.s test_coverage.c 2>&1
grep -A 50 "^process:" test_coverage.s | head -60
process: # @process
.cfi_startproc
# %bb.0:
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset %rbx, -16
movl %edi, %ebx
callq __sanitizer_cov_trace_pc@PLT
xorl %edi, %edi
movl %ebx, %esi
callq __sanitizer_cov_trace_const_cmp4@PLT
testl %ebx, %ebx
movl $.Lstr, %eax
movl $.Lstr.3, %edi
cmovgq %rax, %rdi
callq puts@PLT
movl $.Lstr.4, %edi
popq %rbx
.cfi_def_cfa_offset 8
jmp puts@PLT # TAILCALL
.Lfunc_end3:
.size process, .Lfunc_end3-process
.cfi_endproc
# -- End function
.globl main # -- Begin function main
.p2align 4
.type main,@function
main: # @main
.cfi_startproc
# %bb.0:
pushq %rax
.cfi_def_cfa_offset 16
callq __sanitizer_cov_trace_pc@PLT
movl $.Lstr, %edi
callq puts@PLT
movl $.Lstr.4, %edi
callq puts@PLT
movl $.Lstr.3, %edi
callq puts@PLT
movl $.Lstr.4, %edi
callq puts@PLT
xorl %eax, %eax
popq %rcx
.cfi_def_cfa_offset 8
retq
.Lfunc_end4:
.size main, .Lfunc_end4-main
.cfi_endproc
# -- End function
.type .Lstr,@object # @str
.section .rodata.str1.1,"aMS",@progbits,1
최적화 없이 컴파일하여 기본 블록 분리 확인
clang -S -O0 -fsanitize-coverage=trace-pc,trace-cmp -o test_no_opt.s test_coverage.c 2>&1
grep -A 80 "^process:" test_no_opt.s | head -85
process: # @process
.cfi_startproc
# %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl %edi, -12(%rbp) # 4-byte Spill
callq __sanitizer_cov_trace_pc@PLT
movl -12(%rbp), %edi # 4-byte Reload
movl %edi, -4(%rbp)
movl -4(%rbp), %esi
movl %esi, -8(%rbp) # 4-byte Spill
xorl %edi, %edi
callq __sanitizer_cov_trace_const_cmp4@PLT
movl -8(%rbp), %eax # 4-byte Reload
cmpl $0, %eax
jle .LBB3_2
# %bb.1:
callq __sanitizer_cov_trace_pc@PLT
callq handle_positive
jmp .LBB3_3
.LBB3_2:
callq __sanitizer_cov_trace_pc@PLT
callq handle_negative
.LBB3_3:
callq cleanup
addq $16, %rsp
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Lfunc_end3:
.size process, .Lfunc_end3-process
.cfi_endproc
# -- End function
.globl main # -- Begin function main
.p2align 4
.type main,@function
main: # @main
.cfi_startproc
# %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
callq __sanitizer_cov_trace_pc@PLT
movl $0, -4(%rbp)
movl $5, %edi
callq process
movl $4294967293, %edi # imm = 0xFFFFFFFD
callq process
xorl %eax, %eax
addq $16, %rsp
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Lfunc_end4:
.size main, .Lfunc_end4-main
.cfi_endproc
# -- End function
.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "Positive!\n"
.size .L.str, 11
.type .L.str.1,@object # @.str.1
.L.str.1:
.asciz "Negative!\n"
.size .L.str.1, 11
.type .L.str.2,@object # @.str.2
.L.str.2:
.asciz "Cleanup!\n"
.size .L.str.2, 10
.ident "clang version 21.1.6 (Fedora 21.1.6-1.fc43)"
트레이싱 없이 컴파일하여 비교
clang -S -O0 -o test_normal.s test_coverage.c
grep -A 35 "^process:" test_normal.s | head -40
process: # @process
.cfi_startproc
# %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
cmpl $0, -4(%rbp)
jle .LBB3_2
# %bb.1:
callq handle_positive
jmp .LBB3_3
.LBB3_2:
callq handle_negative
.LBB3_3:
callq cleanup
addq $16, %rsp
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Lfunc_end3:
.size process, .Lfunc_end3-process
.cfi_endproc
# -- End function
.globl main # -- Begin function main
.p2align 4
.type main,@function
main: # @main
.cfi_startproc
# %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
C 코드를 LLVM IR로 변환
clang -S -emit-llvm -O0 -o test.ll test_coverage.
grep -A 30 "define.*@process" test.ll
define dso_local void @process(i32 noundef %0) #0 {
%2 = alloca i32, align 4
store i32 %0, ptr %2, align 4
%3 = load i32, ptr %2, align 4
%4 = icmp sgt i32 %3, 0
br i1 %4, label %5, label %6
5: ; preds = %1
call void @handle_positive()
br label %7
6: ; preds = %1
call void @handle_negative()
br label %7
7: ; preds = %6, %5
call void @cleanup()
ret void
}
; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, ptr %1, align 4
call void @process(i32 noundef 5)
call void @process(i32 noundef -3)
ret i32 0
}
attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
opt 사용 (LLVM 15 이상)하여 CFG DOT 파일 생성
$ opt -passes=dot-cfg test.ll -disable-output
Writing '.handle_positive.dot'...
Writing '.handle_negative.dot'...
Writing '.cleanup.dot'...
Writing '.process.dot'...
Writing '.main.dot'...
PNG로 변환
dot -Tpng .process.dot -o process_cfg.png
References
'System Programming > Compiler' 카테고리의 다른 글
| LLVM tutorial #0 Architecture (0) | 2025.10.11 |
|---|