-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[InferAlignment] Propagate alignment between loads/stores of the same base pointer #145733
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
15600f9
b905f1c
cd9e9f6
9c99e0a
8db304a
d696eee
c53e595
39dc009
2f73c04
3e55c80
1ef4008
622cf48
4a05b2e
5d15526
8d0d3fc
c6eb67a
215d65f
0025877
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,296 @@ | ||||||||||||||||||||||||||||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 | ||||||||||||||||||||||||||||||
; RUN: opt -passes=load-store-vectorizer -S < %s | FileCheck %s | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
; The IR has the first float3 labeled with align 16, and that 16 should | ||||||||||||||||||||||||||||||
; be propagated such that the second set of 4 values | ||||||||||||||||||||||||||||||
; can also be vectorized together. | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this papering over a missed middle end optimization? The middle end should have inferred the pointer argument to align 16, and then each successive access should already have a refined alignment derived from that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is nice to have for a variety of edge cases, but the specific motivator for this change is based on InstCombine's handling of nested structs. You are correct that if loads/stores are unpacked from aligned loads/stores of aggregates, alignment will be propagated to the unpacked elements. However, with nested structs, InstCombine unpacks one layer at a time, losing alignment context in between passes over the worklist. Before IC:
After IC:
To visualize what's happening under the hood, InstCombine is unpacking the load in stages like this: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the instcombine is fixed to propagate the alignment properly, will this patch still be useful/needed? If not, then I would agree with Matt that we should try fixing the source of the problem. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This instcombine case is the only one I currently know of those benefits from this specific optimization, but I would suspect there could be a different way to arrive at a pattern like this. But let's ignore those hypotheticals, because I understand the desire to stick within the realm of known use cases. The question is, are we comfortable changing the llvm-project/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp Lines 788 to 801 in 8c32f95
I feel like implementing that would be a bit of a mess, and leads to questions like "how many layers deep should it recurse"? Unpacking one layer at a time and then adding the nested struct elements to the IC worklist to be independently operated on in a later iteration is a much cleaner solution and feels more in line with the design philosophy of InstCombine. I could be wrong though, open to feedback or challenges to my assumptions. And just to be clear, in case my previous explanation was confusing, the reason it would have to recurse through all layers at once is because we cannot store the knowledge that "the second element of Edit: We would also have to change unpackStoreToAggregate to do this too, further increasing the code complexity There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has the align 16 with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Isel benefits from better alignment information in general.
We should definitely not infer better alignments in InstCombine. We specifically split out the InferAlignments pass out of InstCombine to reduce compile-time from repeatedly inferring alignment. I think with the scheme I proposed above doing this in InferAlignment should be close to free, but of course we won't know until we try.
I'm not sure we'll be losing much in practice. I'd generally expect that after CSE, cases where alignment can be increased in this manner will have the form of constant offset GEPs from a common base. All of the tests you added in this PR are of this form. Did you see motivating cases where this does not hold? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I agree. There is technically a solution that involves expanding the aggregate unpacking logic to traverse multiple layers at once, which would theoretically solve this problem for "free", but I think there are other tradeoffs that come with that.
This is a reasonable argument. Let me investigate this possibility and get back to you all with my findings. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That is the command, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, I see what you mean now. That Attributor pass (which does not run by default) labels the argument pointer with the I don't know much about the history of the Attributor pass. Is that something we should consider enabling by default? Or maybe adding the alignment attribute to the argument at some other point in the pipeline, such as in InstCombine when the aggregate is split? Edit: or maybe an addition to InferAlignments that attempts to add param attributes before the computeKnownBits pass? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it's looking like the InferAlignments change is working. I'll put it up soon for review soon, and we can weigh it against a possible Attributor fix. |
||||||||||||||||||||||||||||||
%struct.float3 = type { float, float, float } | ||||||||||||||||||||||||||||||
%struct.S1 = type { %struct.float3, %struct.float3, i32, i32 } | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
define void @testStore(ptr nocapture writeonly %1) { | ||||||||||||||||||||||||||||||
; CHECK-LABEL: define void @testStore( | ||||||||||||||||||||||||||||||
; CHECK-SAME: ptr writeonly captures(none) [[TMP0:%.*]]) { | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <4 x float> zeroinitializer, ptr [[TMP0]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM10:%.*]] = getelementptr inbounds [[STRUCT_S1:%.*]], ptr [[TMP0]], i64 0, i32 1, i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <4 x i32> zeroinitializer, ptr [[GETELEM10]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: ret void | ||||||||||||||||||||||||||||||
; | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %1, align 16 | ||||||||||||||||||||||||||||||
%getElem = getelementptr inbounds %struct.float3, ptr %1, i64 0, i32 1 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem, align 4 | ||||||||||||||||||||||||||||||
%getElem8 = getelementptr inbounds %struct.float3, ptr %1, i64 0, i32 2 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem8, align 8 | ||||||||||||||||||||||||||||||
%getElem9 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 1 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem9, align 4 | ||||||||||||||||||||||||||||||
%getElem10 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 1, i32 1 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem10, align 4 | ||||||||||||||||||||||||||||||
%getElem11 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 1, i32 2 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem11, align 4 | ||||||||||||||||||||||||||||||
%getElem12 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 2 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem12, align 8 | ||||||||||||||||||||||||||||||
%getElem13 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 3 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem13, align 4 | ||||||||||||||||||||||||||||||
ret void | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
define void @testLoad(ptr nocapture writeonly %1) { | ||||||||||||||||||||||||||||||
; CHECK-LABEL: define void @testLoad( | ||||||||||||||||||||||||||||||
; CHECK-SAME: ptr writeonly captures(none) [[TMP0:%.*]]) { | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP2:%.*]] = load <4 x float>, ptr [[TMP0]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L11:%.*]] = extractelement <4 x float> [[TMP2]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L22:%.*]] = extractelement <4 x float> [[TMP2]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L33:%.*]] = extractelement <4 x float> [[TMP2]], i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L44:%.*]] = extractelement <4 x float> [[TMP2]], i32 3 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM10:%.*]] = getelementptr inbounds [[STRUCT_S1:%.*]], ptr [[TMP0]], i64 0, i32 1, i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP3:%.*]] = load <4 x i32>, ptr [[GETELEM10]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L55:%.*]] = extractelement <4 x i32> [[TMP3]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP4:%.*]] = bitcast i32 [[L55]] to float | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L66:%.*]] = extractelement <4 x i32> [[TMP3]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP5:%.*]] = bitcast i32 [[L66]] to float | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L77:%.*]] = extractelement <4 x i32> [[TMP3]], i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L88:%.*]] = extractelement <4 x i32> [[TMP3]], i32 3 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: ret void | ||||||||||||||||||||||||||||||
; | ||||||||||||||||||||||||||||||
%l1 = load float, ptr %1, align 16 | ||||||||||||||||||||||||||||||
%getElem = getelementptr inbounds %struct.float3, ptr %1, i64 0, i32 1 | ||||||||||||||||||||||||||||||
%l2 = load float, ptr %getElem, align 4 | ||||||||||||||||||||||||||||||
%getElem8 = getelementptr inbounds %struct.float3, ptr %1, i64 0, i32 2 | ||||||||||||||||||||||||||||||
%l3 = load float, ptr %getElem8, align 8 | ||||||||||||||||||||||||||||||
%getElem9 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 1 | ||||||||||||||||||||||||||||||
%l4 = load float, ptr %getElem9, align 4 | ||||||||||||||||||||||||||||||
%getElem10 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 1, i32 1 | ||||||||||||||||||||||||||||||
%l5 = load float, ptr %getElem10, align 4 | ||||||||||||||||||||||||||||||
%getElem11 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 1, i32 2 | ||||||||||||||||||||||||||||||
%l6 = load float, ptr %getElem11, align 4 | ||||||||||||||||||||||||||||||
%getElem12 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 2 | ||||||||||||||||||||||||||||||
%l7 = load i32, ptr %getElem12, align 8 | ||||||||||||||||||||||||||||||
%getElem13 = getelementptr inbounds %struct.S1, ptr %1, i64 0, i32 3 | ||||||||||||||||||||||||||||||
%l8 = load i32, ptr %getElem13, align 4 | ||||||||||||||||||||||||||||||
ret void | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
; Also, test without the struct geps, to see if it still works with i8 geps/ptradd | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
define void @testStorei8(ptr nocapture writeonly %1) { | ||||||||||||||||||||||||||||||
; CHECK-LABEL: define void @testStorei8( | ||||||||||||||||||||||||||||||
; CHECK-SAME: ptr writeonly captures(none) [[TMP0:%.*]]) { | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <4 x float> zeroinitializer, ptr [[TMP0]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM10:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <4 x i32> zeroinitializer, ptr [[GETELEM10]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: ret void | ||||||||||||||||||||||||||||||
; | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %1, align 16 | ||||||||||||||||||||||||||||||
%getElem = getelementptr inbounds i8, ptr %1, i64 4 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem, align 4 | ||||||||||||||||||||||||||||||
%getElem8 = getelementptr inbounds i8, ptr %1, i64 8 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem8, align 8 | ||||||||||||||||||||||||||||||
%getElem9 = getelementptr inbounds i8, ptr %1, i64 12 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem9, align 4 | ||||||||||||||||||||||||||||||
%getElem10 = getelementptr inbounds i8, ptr %1, i64 16 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem10, align 4 | ||||||||||||||||||||||||||||||
%getElem11 = getelementptr inbounds i8, ptr %1, i64 20 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem11, align 4 | ||||||||||||||||||||||||||||||
%getElem12 = getelementptr inbounds i8, ptr %1, i64 24 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem12, align 8 | ||||||||||||||||||||||||||||||
%getElem13 = getelementptr inbounds i8, ptr %1, i64 28 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem13, align 4 | ||||||||||||||||||||||||||||||
ret void | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
define void @testLoadi8(ptr nocapture writeonly %1) { | ||||||||||||||||||||||||||||||
; CHECK-LABEL: define void @testLoadi8( | ||||||||||||||||||||||||||||||
; CHECK-SAME: ptr writeonly captures(none) [[TMP0:%.*]]) { | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP2:%.*]] = load <4 x float>, ptr [[TMP0]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L11:%.*]] = extractelement <4 x float> [[TMP2]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L22:%.*]] = extractelement <4 x float> [[TMP2]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L33:%.*]] = extractelement <4 x float> [[TMP2]], i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L44:%.*]] = extractelement <4 x float> [[TMP2]], i32 3 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM10:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP3:%.*]] = load <4 x i32>, ptr [[GETELEM10]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L55:%.*]] = extractelement <4 x i32> [[TMP3]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP4:%.*]] = bitcast i32 [[L55]] to float | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L66:%.*]] = extractelement <4 x i32> [[TMP3]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP5:%.*]] = bitcast i32 [[L66]] to float | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L77:%.*]] = extractelement <4 x i32> [[TMP3]], i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L88:%.*]] = extractelement <4 x i32> [[TMP3]], i32 3 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: ret void | ||||||||||||||||||||||||||||||
; | ||||||||||||||||||||||||||||||
%l1 = load float, ptr %1, align 16 | ||||||||||||||||||||||||||||||
%getElem = getelementptr inbounds i8, ptr %1, i64 4 | ||||||||||||||||||||||||||||||
%l2 = load float, ptr %getElem, align 4 | ||||||||||||||||||||||||||||||
%getElem8 = getelementptr inbounds i8, ptr %1, i64 8 | ||||||||||||||||||||||||||||||
%l3 = load float, ptr %getElem8, align 8 | ||||||||||||||||||||||||||||||
%getElem9 = getelementptr inbounds i8, ptr %1, i64 12 | ||||||||||||||||||||||||||||||
%l4 = load float, ptr %getElem9, align 4 | ||||||||||||||||||||||||||||||
%getElem10 = getelementptr inbounds i8, ptr %1, i64 16 | ||||||||||||||||||||||||||||||
%l5 = load float, ptr %getElem10, align 4 | ||||||||||||||||||||||||||||||
%getElem11 = getelementptr inbounds i8, ptr %1, i64 20 | ||||||||||||||||||||||||||||||
%l6 = load float, ptr %getElem11, align 4 | ||||||||||||||||||||||||||||||
%getElem12 = getelementptr inbounds i8, ptr %1, i64 24 | ||||||||||||||||||||||||||||||
%l7 = load i32, ptr %getElem12, align 8 | ||||||||||||||||||||||||||||||
%getElem13 = getelementptr inbounds i8, ptr %1, i64 28 | ||||||||||||||||||||||||||||||
%l8 = load i32, ptr %getElem13, align 4 | ||||||||||||||||||||||||||||||
ret void | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you use more representative tests with some uses? As is these all optimize to memset or no-op There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slight pushback, my understanding is that lit tests are most useful when they are minimal reproducers of the problem the optimization is targeting. Adding uses would not really change the nature of this optimization. Tests like If you think it would be better, I could combine each pair of load and store tests into individual tests, storing the result of the loads. Other LSV tests use that pattern a lot. |
||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
; This version of the test adjusts the struct to hold two i32s at the beginning, | ||||||||||||||||||||||||||||||
; but still assumes that the first float3 is 16 aligned. If the alignment | ||||||||||||||||||||||||||||||
; propagation works correctly, it should be able to load this struct in three | ||||||||||||||||||||||||||||||
; loads: a 2x32, a 4x32, and a 4x32. Without the alignment propagation, the last | ||||||||||||||||||||||||||||||
; 4x32 will instead be a 2x32 and a 2x32 | ||||||||||||||||||||||||||||||
%struct.S2 = type { i32, i32, %struct.float3, %struct.float3, i32, i32 } | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
define void @testStore_2(ptr nocapture writeonly %1) { | ||||||||||||||||||||||||||||||
; CHECK-LABEL: define void @testStore_2( | ||||||||||||||||||||||||||||||
; CHECK-SAME: ptr writeonly captures(none) [[TMP0:%.*]]) { | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <2 x i32> zeroinitializer, ptr [[TMP0]], align 8 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM1:%.*]] = getelementptr inbounds [[STRUCT_S2:%.*]], ptr [[TMP0]], i64 0, i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <4 x float> zeroinitializer, ptr [[GETELEM1]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM10:%.*]] = getelementptr inbounds [[STRUCT_S2]], ptr [[TMP0]], i64 0, i32 3, i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <4 x i32> zeroinitializer, ptr [[GETELEM10]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: ret void | ||||||||||||||||||||||||||||||
; | ||||||||||||||||||||||||||||||
store i32 0, ptr %1, align 8 | ||||||||||||||||||||||||||||||
%getElem = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 1 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem, align 4 | ||||||||||||||||||||||||||||||
%getElem1 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 2 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem1, align 16 | ||||||||||||||||||||||||||||||
%getElem2 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 2, i32 1 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem2, align 4 | ||||||||||||||||||||||||||||||
%getElem8 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 2, i32 2 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem8, align 8 | ||||||||||||||||||||||||||||||
%getElem9 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 3 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem9, align 4 | ||||||||||||||||||||||||||||||
%getElem10 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 3, i32 1 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem10, align 4 | ||||||||||||||||||||||||||||||
%getElem11 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 3, i32 2 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem11, align 4 | ||||||||||||||||||||||||||||||
%getElem12 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 4 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem12, align 8 | ||||||||||||||||||||||||||||||
%getElem13 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 5 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem13, align 4 | ||||||||||||||||||||||||||||||
ret void | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
define void @testLoad_2(ptr nocapture writeonly %1) { | ||||||||||||||||||||||||||||||
; CHECK-LABEL: define void @testLoad_2( | ||||||||||||||||||||||||||||||
; CHECK-SAME: ptr writeonly captures(none) [[TMP0:%.*]]) { | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP2:%.*]] = load <2 x i32>, ptr [[TMP0]], align 8 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L1:%.*]] = extractelement <2 x i32> [[TMP2]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L22:%.*]] = extractelement <2 x i32> [[TMP2]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM1:%.*]] = getelementptr inbounds [[STRUCT_S2:%.*]], ptr [[TMP0]], i64 0, i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP3:%.*]] = load <4 x float>, ptr [[GETELEM1]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L33:%.*]] = extractelement <4 x float> [[TMP3]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L44:%.*]] = extractelement <4 x float> [[TMP3]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L55:%.*]] = extractelement <4 x float> [[TMP3]], i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L66:%.*]] = extractelement <4 x float> [[TMP3]], i32 3 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM10:%.*]] = getelementptr inbounds [[STRUCT_S2]], ptr [[TMP0]], i64 0, i32 3, i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP4:%.*]] = load <4 x i32>, ptr [[GETELEM10]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L77:%.*]] = extractelement <4 x i32> [[TMP4]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP5:%.*]] = bitcast i32 [[L77]] to float | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L88:%.*]] = extractelement <4 x i32> [[TMP4]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP6:%.*]] = bitcast i32 [[L88]] to float | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L99:%.*]] = extractelement <4 x i32> [[TMP4]], i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L010:%.*]] = extractelement <4 x i32> [[TMP4]], i32 3 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: ret void | ||||||||||||||||||||||||||||||
; | ||||||||||||||||||||||||||||||
%l = load i32, ptr %1, align 8 | ||||||||||||||||||||||||||||||
%getElem = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 1 | ||||||||||||||||||||||||||||||
%l2 = load i32, ptr %getElem, align 4 | ||||||||||||||||||||||||||||||
%getElem1 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 2 | ||||||||||||||||||||||||||||||
%l3 = load float, ptr %getElem1, align 16 | ||||||||||||||||||||||||||||||
%getElem2 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 2, i32 1 | ||||||||||||||||||||||||||||||
%l4 = load float, ptr %getElem2, align 4 | ||||||||||||||||||||||||||||||
%getElem8 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 2, i32 2 | ||||||||||||||||||||||||||||||
%l5 = load float, ptr %getElem8, align 8 | ||||||||||||||||||||||||||||||
%getElem9 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 3 | ||||||||||||||||||||||||||||||
%l6 = load float, ptr %getElem9, align 4 | ||||||||||||||||||||||||||||||
%getElem10 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 3, i32 1 | ||||||||||||||||||||||||||||||
%l7 = load float, ptr %getElem10, align 4 | ||||||||||||||||||||||||||||||
%getElem11 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 3, i32 2 | ||||||||||||||||||||||||||||||
%l8 = load float, ptr %getElem11, align 4 | ||||||||||||||||||||||||||||||
%getElem12 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 4 | ||||||||||||||||||||||||||||||
%l9 = load i32, ptr %getElem12, align 8 | ||||||||||||||||||||||||||||||
%getElem13 = getelementptr inbounds %struct.S2, ptr %1, i64 0, i32 5 | ||||||||||||||||||||||||||||||
%l0 = load i32, ptr %getElem13, align 4 | ||||||||||||||||||||||||||||||
ret void | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
; Also, test without the struct geps, to see if it still works with i8 geps/ptradd | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
define void @testStorei8_2(ptr nocapture writeonly %1) { | ||||||||||||||||||||||||||||||
; CHECK-LABEL: define void @testStorei8_2( | ||||||||||||||||||||||||||||||
; CHECK-SAME: ptr writeonly captures(none) [[TMP0:%.*]]) { | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <2 x i32> zeroinitializer, ptr [[TMP0]], align 8 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 8 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <4 x float> zeroinitializer, ptr [[GETELEM1]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM10:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 24 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: store <4 x i32> zeroinitializer, ptr [[GETELEM10]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: ret void | ||||||||||||||||||||||||||||||
; | ||||||||||||||||||||||||||||||
store i32 0, ptr %1, align 8 | ||||||||||||||||||||||||||||||
%getElem = getelementptr inbounds i8, ptr %1, i64 4 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem, align 4 | ||||||||||||||||||||||||||||||
%getElem1 = getelementptr inbounds i8, ptr %1, i64 8 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem1, align 16 | ||||||||||||||||||||||||||||||
%getElem2 = getelementptr inbounds i8, ptr %1, i64 12 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem2, align 4 | ||||||||||||||||||||||||||||||
%getElem8 = getelementptr inbounds i8, ptr %1, i64 16 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem8, align 8 | ||||||||||||||||||||||||||||||
%getElem9 = getelementptr inbounds i8, ptr %1, i64 20 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem9, align 4 | ||||||||||||||||||||||||||||||
%getElem10 = getelementptr inbounds i8, ptr %1, i64 24 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem10, align 4 | ||||||||||||||||||||||||||||||
%getElem11 = getelementptr inbounds i8, ptr %1, i64 28 | ||||||||||||||||||||||||||||||
store float 0.000000e+00, ptr %getElem11, align 4 | ||||||||||||||||||||||||||||||
%getElem12 = getelementptr inbounds i8, ptr %1, i64 32 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem12, align 8 | ||||||||||||||||||||||||||||||
%getElem13 = getelementptr inbounds i8, ptr %1, i64 36 | ||||||||||||||||||||||||||||||
store i32 0, ptr %getElem13, align 4 | ||||||||||||||||||||||||||||||
ret void | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
define void @testLoadi8_2(ptr nocapture writeonly %1) { | ||||||||||||||||||||||||||||||
; CHECK-LABEL: define void @testLoadi8_2( | ||||||||||||||||||||||||||||||
; CHECK-SAME: ptr writeonly captures(none) [[TMP0:%.*]]) { | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP2:%.*]] = load <2 x i32>, ptr [[TMP0]], align 8 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L1:%.*]] = extractelement <2 x i32> [[TMP2]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L22:%.*]] = extractelement <2 x i32> [[TMP2]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 8 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP3:%.*]] = load <4 x float>, ptr [[GETELEM1]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L33:%.*]] = extractelement <4 x float> [[TMP3]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L44:%.*]] = extractelement <4 x float> [[TMP3]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L55:%.*]] = extractelement <4 x float> [[TMP3]], i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L66:%.*]] = extractelement <4 x float> [[TMP3]], i32 3 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[GETELEM10:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 24 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP4:%.*]] = load <4 x i32>, ptr [[GETELEM10]], align 16 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L77:%.*]] = extractelement <4 x i32> [[TMP4]], i32 0 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP5:%.*]] = bitcast i32 [[L77]] to float | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L88:%.*]] = extractelement <4 x i32> [[TMP4]], i32 1 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[TMP6:%.*]] = bitcast i32 [[L88]] to float | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L99:%.*]] = extractelement <4 x i32> [[TMP4]], i32 2 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: [[L010:%.*]] = extractelement <4 x i32> [[TMP4]], i32 3 | ||||||||||||||||||||||||||||||
; CHECK-NEXT: ret void | ||||||||||||||||||||||||||||||
; | ||||||||||||||||||||||||||||||
%l = load i32, ptr %1, align 8 | ||||||||||||||||||||||||||||||
%getElem = getelementptr inbounds i8, ptr %1, i64 4 | ||||||||||||||||||||||||||||||
%l2 = load i32, ptr %getElem, align 4 | ||||||||||||||||||||||||||||||
%getElem1 = getelementptr inbounds i8, ptr %1, i64 8 | ||||||||||||||||||||||||||||||
%l3 = load float, ptr %getElem1, align 16 | ||||||||||||||||||||||||||||||
%getElem2 = getelementptr inbounds i8, ptr %1, i64 12 | ||||||||||||||||||||||||||||||
%l4 = load float, ptr %getElem2, align 4 | ||||||||||||||||||||||||||||||
%getElem8 = getelementptr inbounds i8, ptr %1, i64 16 | ||||||||||||||||||||||||||||||
%l5 = load float, ptr %getElem8, align 8 | ||||||||||||||||||||||||||||||
%getElem9 = getelementptr inbounds i8, ptr %1, i64 20 | ||||||||||||||||||||||||||||||
%l6 = load float, ptr %getElem9, align 4 | ||||||||||||||||||||||||||||||
%getElem10 = getelementptr inbounds i8, ptr %1, i64 24 | ||||||||||||||||||||||||||||||
%l7 = load float, ptr %getElem10, align 4 | ||||||||||||||||||||||||||||||
%getElem11 = getelementptr inbounds i8, ptr %1, i64 28 | ||||||||||||||||||||||||||||||
%l8 = load float, ptr %getElem11, align 4 | ||||||||||||||||||||||||||||||
%getElem12 = getelementptr inbounds i8, ptr %1, i64 32 | ||||||||||||||||||||||||||||||
%l9 = load i32, ptr %getElem12, align 8 | ||||||||||||||||||||||||||||||
%getElem13 = getelementptr inbounds i8, ptr %1, i64 36 | ||||||||||||||||||||||||||||||
%l0 = load i32, ptr %getElem13, align 4 | ||||||||||||||||||||||||||||||
ret void | ||||||||||||||||||||||||||||||
} |
Uh oh!
There was an error while loading. Please reload this page.