| ; Tests basic CopyProf instrumentation of special member functions. | |
| ; | |
| ; RUN: opt < %s -passes='function(copyprof),module(copyprof-module)' -S | FileCheck %s | |
| target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" | |
| target triple = "x86_64-unknown-linux-gnu" | |
| ;; Verifies that the module constructor and global ctors are set up. | |
| ; CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] | |
| ; CHECK-SAME: i32 0, ptr @copyprof.module_ctor | |
| ;; Tests constructor instrumentation (1-arg: this). | |
| define void @ctor(ptr %this) "copyprof-ctor"="24" { | |
| entry: | |
| %field = getelementptr i8, ptr %this, i64 8 | |
| store i32 0, ptr %field | |
| ret void | |
| } | |
| ; CHECK-LABEL: define void @ctor(ptr %this) | |
| ; CHECK-NEXT: entry: | |
| ; CHECK-NEXT: call void @__copyprof_ctor_enter_callback(ptr %this, i64 24) | |
| ; CHECK-NOT: call void @__copyprof_ctor_enter_callback | |
| ; CHECK: call void @__copyprof_ctor_exit_callback(ptr %this, i64 24) | |
| ; CHECK-NEXT: ret void | |
| ;; Tests copy constructor instrumentation (2-arg: this, other). | |
| define void @copy_ctor(ptr %this, ptr %other) "copyprof-copy-ctor"="16" { | |
| entry: | |
| %val = load i32, ptr %other | |
| store i32 %val, ptr %this | |
| ret void | |
| } | |
| ; CHECK-LABEL: define void @copy_ctor(ptr %this, ptr %other) | |
| ; CHECK-NEXT: entry: | |
| ; CHECK-NEXT: call void @__copyprof_copy_ctor_enter_callback(ptr %this, ptr %other, i64 16) | |
| ; CHECK-NOT: call void @__copyprof_copy_ctor_enter_callback | |
| ; CHECK: call void @__copyprof_copy_ctor_exit_callback(ptr %this, ptr %other, i64 16) | |
| ; CHECK-NEXT: ret void | |
| ;; Tests copy assignment operator instrumentation (2-arg: this, other). | |
| define void @copy_assign(ptr %this, ptr %other) "copyprof-copy-assign-op"="32" { | |
| entry: | |
| %val = load i64, ptr %other | |
| store i64 %val, ptr %this | |
| ret void | |
| } | |
| ; CHECK-LABEL: define void @copy_assign(ptr %this, ptr %other) | |
| ; CHECK-NEXT: entry: | |
| ; CHECK-NEXT: call void @__copyprof_copy_assign_op_enter_callback(ptr %this, ptr %other, i64 32) | |
| ; CHECK-NOT: call void @__copyprof_copy_assign_op_enter_callback | |
| ; CHECK: call void @__copyprof_copy_assign_op_exit_callback(ptr %this, ptr %other, i64 32) | |
| ; CHECK-NEXT: ret void | |
| ;; Tests destructor instrumentation (1-arg: this). | |
| define void @dtor(ptr %this) "copyprof-dtor"="24" { | |
| entry: | |
| ret void | |
| } | |
| ; CHECK-LABEL: define void @dtor(ptr %this) | |
| ; CHECK-NEXT: entry: | |
| ; CHECK-NEXT: call void @__copyprof_dtor_enter_callback(ptr %this, i64 24) | |
| ; CHECK-NOT: call void @__copyprof_dtor_enter_callback | |
| ; CHECK: call void @__copyprof_dtor_exit_callback(ptr %this, i64 24) | |
| ; CHECK-NEXT: ret void | |
| ;; Tests that exit callbacks are inserted before multiple return instructions. | |
| define void @ctor_multi_ret(ptr %this, i1 %cond) "copyprof-ctor"="8" { | |
| entry: | |
| br i1 %cond, label %then, label %else | |
| then: | |
| ret void | |
| else: | |
| ret void | |
| } | |
| ; CHECK-LABEL: define void @ctor_multi_ret(ptr %this, i1 %cond) | |
| ; CHECK: call void @__copyprof_ctor_enter_callback(ptr %this, i64 8) | |
| ; CHECK-NOT: call void @__copyprof_ctor_enter_callback | |
| ; CHECK: then: | |
| ; CHECK: call void @__copyprof_ctor_exit_callback(ptr %this, i64 8) | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: else: | |
| ; CHECK: call void @__copyprof_ctor_exit_callback(ptr %this, i64 8) | |
| ; CHECK-NEXT: ret void | |
| ;; Tests that exit callbacks are inserted before resume instructions | |
| ;; (exception unwinding). | |
| define void @ctor_with_resume(ptr %this) "copyprof-ctor"="8" personality ptr @__gxx_personality_v0 { | |
| entry: | |
| invoke void @may_throw() to label %cont unwind label %lpad | |
| cont: | |
| ret void | |
| lpad: | |
| %lp = landingpad { ptr, i32 } cleanup | |
| resume { ptr, i32 } %lp | |
| } | |
| declare void @may_throw() | |
| declare i32 @__gxx_personality_v0(...) | |
| ; CHECK-LABEL: define void @ctor_with_resume(ptr %this) | |
| ; CHECK: call void @__copyprof_ctor_enter_callback(ptr %this, i64 8) | |
| ; CHECK: cont: | |
| ; CHECK: call void @__copyprof_ctor_exit_callback(ptr %this, i64 8) | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: lpad: | |
| ; CHECK: call void @__copyprof_ctor_exit_callback(ptr %this, i64 8) | |
| ; CHECK-NEXT: resume | |
| ;; Tests that a function containing a musttail call is skipped entirely. | |
| declare void @tail_callee(ptr) | |
| define void @ctor_with_musttail(ptr %this) "copyprof-ctor"="8" { | |
| entry: | |
| musttail call void @tail_callee(ptr %this) | |
| ret void | |
| } | |
| ; CHECK-LABEL: define void @ctor_with_musttail(ptr %this) | |
| ; CHECK-NEXT: entry: | |
| ; CHECK-NEXT: musttail call void @tail_callee(ptr %this) | |
| ; CHECK-NEXT: ret void | |
| ; CHECK-NOT: call void @__copyprof_ | |
| ;; Tests that a function containing an ordinary tail call is instrumented normally, | |
| ;; with the exit callback inserted between the tail call and return instructions. | |
| define void @ctor_with_tail(ptr %this) "copyprof-ctor"="8" { | |
| entry: | |
| tail call void @tail_callee(ptr %this) | |
| ret void | |
| } | |
| ; CHECK-LABEL: define void @ctor_with_tail(ptr %this) | |
| ; CHECK-NEXT: entry: | |
| ; CHECK-NEXT: call void @__copyprof_ctor_enter_callback(ptr %this, i64 8) | |
| ; CHECK-NEXT: tail call void @tail_callee(ptr %this) | |
| ; CHECK-NEXT: call void @__copyprof_ctor_exit_callback(ptr %this, i64 8) | |
| ; CHECK-NEXT: ret void | |
| ;; Tests that entry callbacks are inserted after alloca instructions. | |
| define void @ctor_with_alloca(ptr %this) "copyprof-ctor"="8" { | |
| entry: | |
| %tmp = alloca i32 | |
| store i32 0, ptr %tmp | |
| ret void | |
| } | |
| ; CHECK-LABEL: define void @ctor_with_alloca(ptr %this) | |
| ; CHECK: %tmp = alloca i32 | |
| ; CHECK-NEXT: call void @__copyprof_ctor_enter_callback(ptr %this, i64 8) | |
| ; CHECK: call void @__copyprof_ctor_exit_callback(ptr %this, i64 8) | |
| ; CHECK-NEXT: ret void | |
| ;; Tests that no exit callback is inserted before unreachable terminators. | |
| define void @ctor_with_unreachable(ptr %this) "copyprof-ctor"="8" { | |
| entry: | |
| call void @may_throw() | |
| unreachable | |
| } | |
| ; CHECK-LABEL: define void @ctor_with_unreachable(ptr %this) | |
| ; CHECK: call void @__copyprof_ctor_enter_callback(ptr %this, i64 8) | |
| ; CHECK-NOT: call void @__copyprof_ctor_exit_callback | |
| ; CHECK: unreachable | |
| ;; Verifies that the module constructor calls the init function and is marked so | |
| ;; that it's never instrumented itself. | |
| ; CHECK: define internal void @copyprof.module_ctor() | |
| ; CHECK: call void @__copyprof_init_once() | |
| ; CHECK: disable_sanitizer_instrumentation |