Changeset View
Changeset View
Standalone View
Standalone View
test/Transforms/LoadElim/invariant.ll
; RUN: opt < %s -gvn -S | FileCheck %s | ; RUN: opt < %s -gvn -S | FileCheck %s --check-prefix=CHECKL --check-prefix=CHECK | ||||
; RUN: opt < %s -globalopt -gvn -S | FileCheck %s --check-prefix=CHECKG --check-prefix=CHECK | |||||
; On a given pointer to allocated memory, invariant_start/end intrinsic | ; On a given pointer to allocated memory, invariant_start/end intrinsic | ||||
; calls indicate that the memory can be considered constant for load | ; calls indicate that the memory can be considered constant for load | ||||
; elimination purposes. | ; elimination purposes. | ||||
define void @example() { | define void @example() { | ||||
entry: | entry: | ||||
%i = alloca i32 | %i = alloca i32 | ||||
call void @foo(i32* %i) ; #1 | call void @foo(i32* %i) ; #1 | ||||
%0 = bitcast i32* %i to i8* | %0 = bitcast i32* %i to i8* | ||||
%1 = call {}* (i64, i8*) @llvm.invariant.start(i64 4, i8* %0) | %1 = call {}* (i64, i8*) @llvm.invariant.start(i64 4, i8* %0) | ||||
; CHECK: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* | ; CHECK: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* | ||||
call void @foo(i32* %i) ; #2 | call void @foo(i32* %i) ; #2 | ||||
%2 = load i32, i32* %i ; Not clobbered by #2; Clobbered by #1; Unchanged. | %2 = load i32, i32* %i ; Not clobbered by #2; Clobbered by #1; Unchanged. | ||||
; CHECK: load i32, i32* | ; CHECK: load i32, i32* | ||||
call void @bar(i32 %2) | call void @bar(i32 %2) | ||||
call void @foo(i32* %i) ; #3 | call void @foo(i32* %i) ; #3 | ||||
%3 = load i32, i32* %i ; Not clobbered by #3; Merged into %2. | %3 = load i32, i32* %i ; Not clobbered by #3; Merged into %2. | ||||
; CHECK-NOT: load i32, i32* | ; CHECK-NOT: load i32, i32* | ||||
call void @bar(i32 %3) | call void @bar(i32 %3) | ||||
call void @llvm.invariant.end({}* %1, i64 4, i8* %0) | call void @llvm.invariant.end({}* %1, i64 4, i8* %0) | ||||
; CHECK: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* | ; CHECK: call {{.*}}@llvm.invariant.end({{.*}}, i64 {{[0-9]+}}, i8* | ||||
call void @foo(i32* %i) ; #4 | call void @foo(i32* %i) ; #4 | ||||
%4 = load i32, i32* %i ; Clobbered by #4; Unchanged. | %4 = load i32, i32* %i ; Clobbered by #4; Unchanged. | ||||
; CHECK: load i32, i32* | ; CHECK: load i32, i32* | ||||
call void @bar(i32 %4) | call void @bar(i32 %4) | ||||
ret void | ret void | ||||
} | } | ||||
; Example with a global variable instead of an alloca instruction, | |||||
; with an invariant_start call during global construction and | |||||
; with no invariant_end call. | |||||
@gi = internal global i32 0 | |||||
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @global_constructor, i8* null }] | |||||
define void @globalex() { | |||||
nlewycky: This function is never called, and thus should never be examined by GlobalOpt's Evaluate calls… | |||||
lvoufoAuthorUnsubmitted Not Done ReplyInline ActionsI'm confused. This *is* supposed to test the effect of changes to -globalopt for -gvn... lvoufo: I'm confused. This *is* supposed to test the effect of changes to -globalopt for -gvn... | |||||
entry: | |||||
call void @foo(i32* @gi) ; #2 | |||||
%0 = load i32, i32* @gi ; Not clobbered by #2; Unknown dependence; Unchanged. | |||||
; CHECK: load i32, i32* | |||||
call void @bar(i32 %0) | |||||
call void @foo(i32* @gi) ; #3 | |||||
%1 = load i32, i32* @gi ; Not clobbered by #3; Merged into %0. | |||||
; CHECKL: load i32, i32* | |||||
; CHECKG-NOT: load i32, i32* | |||||
call void @bar(i32 %1) | |||||
call void @foo(i32* @gi) ; #4 | |||||
%2 = load i32, i32* @gi ; Not clobbered by #4, nor #3; Merged into %0. | |||||
; CHECKL: load i32, i32* | |||||
; CHECKG-NOT: load i32, i32* | |||||
call void @bar(i32 %2) | |||||
ret void | |||||
} | |||||
define internal void @construct_gi() { | |||||
entry: | |||||
call void @foo(i32* @gi) ; #1 -- preserves load instructions from over-simplification. | |||||
%0 = bitcast i32* @gi to i8* | |||||
%1 = call {}* (i64, i8*) @llvm.invariant.start(i64 4, i8* %0) | |||||
; CHECK: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* | |||||
ret void | |||||
} | |||||
define internal void @global_constructor() { | |||||
entry: | |||||
call void @construct_gi() | |||||
ret void | |||||
} | |||||
; Helper function declarations. | |||||
declare void @bar(i32) | declare void @bar(i32) | ||||
declare void @foo(i32*) | declare void @foo(i32*) | ||||
declare {}* @llvm.invariant.start(i64, i8* nocapture) | declare {}* @llvm.invariant.start(i64, i8* nocapture) | ||||
declare void @llvm.invariant.end({}*, i64, i8* nocapture) | declare void @llvm.invariant.end({}*, i64, i8* nocapture) | ||||
This function is never called, and thus should never be examined by GlobalOpt's Evaluate calls at all, right?
I notice you're doing -globalopt -gvn. Please split GVN tests and GlobalOpt tests apart and add new changes testing globalopt only for changes to globalopt. If you need to verify that the combination of passes works well, you would add it to test/Transforms/PhaseOrdering, but I don't think that's necessary here.