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.
122 lines
4.3 KiB
LLVM
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
|
|
}
|