Files
clang-p2996/llvm/test/Transforms/InstCombine/extractvalue.ll
Nikita Popov 90ba33099c [InstCombine] Canonicalize constant GEPs to i8 source element type (#68882)
This patch canonicalizes getelementptr instructions with constant
indices to use the `i8` source element type. This makes it easier for
optimizations to recognize that two GEPs are identical, because they
don't need to see past many different ways to express the same offset.

This is a first step towards
https://discourse.llvm.org/t/rfc-replacing-getelementptr-with-ptradd/68699.
This is limited to constant GEPs only for now, as they have a clear
canonical form, while we're not yet sure how exactly to deal with
variable indices.

The test llvm/test/Transforms/PhaseOrdering/switch_with_geps.ll gives
two representative examples of the kind of optimization improvement we
expect from this change. In the first test SimplifyCFG can now realize
that all switch branches are actually the same. In the second test it
can convert it into simple arithmetic. These are representative of
common optimization failures we see in Rust.

Fixes https://github.com/llvm/llvm-project/issues/69841.
2024-01-24 15:25:29 +01:00

122 lines
4.3 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
declare void @bar({i32, i32} %a)
declare i32 @baz(i32 %a)
; Instcombine should fold various combinations of insertvalue and extractvalue
; together
define i32 @foo(i32 %a, i32 %b) {
; CHECK-LABEL: @foo(
; CHECK-NEXT: [[TMP1:%.*]] = insertvalue { i32, i32 } undef, i32 [[A:%.*]], 0
; CHECK-NEXT: [[S2:%.*]] = insertvalue { i32, i32 } [[TMP1]], i32 [[B:%.*]], 1
; CHECK-NEXT: call void @bar({ i32, i32 } [[S2]])
; CHECK-NEXT: [[TMP2:%.*]] = insertvalue { i32, i32 } undef, i32 [[A]], 0
; CHECK-NEXT: [[S3:%.*]] = insertvalue { i32, i32 } [[TMP2]], i32 [[B]], 1
; CHECK-NEXT: call void @bar({ i32, i32 } [[S3]])
; CHECK-NEXT: ret i32 [[B]]
;
%s1.1 = insertvalue {i32, i32} undef, i32 %a, 0
%s1 = insertvalue {i32, i32} %s1.1, i32 %b, 1
%v1 = extractvalue {i32, i32} %s1, 0
%v2 = extractvalue {i32, i32} %s1, 1
; Build a nested struct and pull a sub struct out of it
; This requires instcombine to insert a few insertvalue instructions
%ns1.1 = insertvalue {i32, {i32, i32}} undef, i32 %v1, 0
%ns1.2 = insertvalue {i32, {i32, i32}} %ns1.1, i32 %v1, 1, 0
%ns1 = insertvalue {i32, {i32, i32}} %ns1.2, i32 %v2, 1, 1
%s2 = extractvalue {i32, {i32, i32}} %ns1, 1
%v3 = extractvalue {i32, {i32, i32}} %ns1, 1, 1
call void @bar({i32, i32} %s2)
; Use nested extractvalues to get to a value
%s3 = extractvalue {i32, {i32, i32}} %ns1, 1
%v4 = extractvalue {i32, i32} %s3, 1
call void @bar({i32, i32} %s3)
; Use nested insertvalues to build a nested struct
%s4.1 = insertvalue {i32, i32} undef, i32 %v3, 0
%s4 = insertvalue {i32, i32} %s4.1, i32 %v4, 1
%ns2 = insertvalue {i32, {i32, i32}} undef, {i32, i32} %s4, 1
; And now extract a single value from there
%v5 = extractvalue {i32, {i32, i32}} %ns2, 1, 1
ret i32 %v5
}
; The load + extractvalue should be converted
; to an inbounds gep + smaller load.
; The new load should be in the same spot as the old load.
define i32 @extract2gep(ptr %pair, ptr %P) {
; CHECK-LABEL: @extract2gep(
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[PAIR:%.*]], i64 4
; CHECK-NEXT: [[E:%.*]] = load i32, ptr [[TMP1]], align 4
; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[C:%.*]] = call i32 @baz(i32 [[E]])
; CHECK-NEXT: store i32 [[C]], ptr [[P]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[C]], 0
; CHECK-NEXT: br i1 [[COND]], label [[END:%.*]], label [[LOOP]]
; CHECK: end:
; CHECK-NEXT: ret i32 [[E]]
;
%L = load {i16, i32}, ptr %pair
store i32 0, ptr %P
br label %loop
loop:
%E = extractvalue {i16, i32} %L, 1
%C = call i32 @baz(i32 %E)
store i32 %C, ptr %P
%cond = icmp eq i32 %C, 0
br i1 %cond, label %end, label %loop
end:
ret i32 %E
}
; The load + extractvalues should be converted
; to a 3-index inbounds gep + smaller load.
define i16 @doubleextract2gep(ptr %arg) {
; CHECK-LABEL: @doubleextract2gep(
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARG:%.*]], i64 8
; CHECK-NEXT: [[E2:%.*]] = load i16, ptr [[TMP1]], align 2
; CHECK-NEXT: ret i16 [[E2]]
;
%L = load {i16, {i32, i16}}, ptr %arg
%E1 = extractvalue {i16, {i32, i16}} %L, 1
%E2 = extractvalue {i32, i16} %E1, 1
ret i16 %E2
}
; The load should be left unchanged since both parts are needed.
define i32 @nogep-multiuse(ptr %pair) {
; CHECK-LABEL: @nogep-multiuse(
; CHECK-NEXT: [[L:%.*]] = load volatile { i32, i32 }, ptr [[PAIR:%.*]], align 4
; CHECK-NEXT: [[LHS:%.*]] = extractvalue { i32, i32 } [[L]], 0
; CHECK-NEXT: [[RHS:%.*]] = extractvalue { i32, i32 } [[L]], 1
; CHECK-NEXT: [[R:%.*]] = add i32 [[LHS]], [[RHS]]
; CHECK-NEXT: ret i32 [[R]]
;
%L = load volatile {i32, i32}, ptr %pair
%LHS = extractvalue {i32, i32} %L, 0
%RHS = extractvalue {i32, i32} %L, 1
%R = add i32 %LHS, %RHS
ret i32 %R
}
; The load volatile should be left unchanged.
define i32 @nogep-volatile(ptr %pair) {
; CHECK-LABEL: @nogep-volatile(
; CHECK-NEXT: [[L:%.*]] = load volatile { i32, i32 }, ptr [[PAIR:%.*]], align 4
; CHECK-NEXT: [[E:%.*]] = extractvalue { i32, i32 } [[L]], 1
; CHECK-NEXT: ret i32 [[E]]
;
%L = load volatile {i32, i32}, ptr %pair
%E = extractvalue {i32, i32} %L, 1
ret i32 %E
}