| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 | |
| ; RUN: opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s | |
| ; The reduced values are the zero-extended bytes of the same wider loaded | |
| ; scalar: the gather is emitted as a bitcast to the byte vector plus an | |
| ; extension. | |
| define i16 @sum8_i64(ptr %x) { | |
| ; CHECK-LABEL: define i16 @sum8_i64( | |
| ; CHECK-SAME: ptr [[X:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i64, ptr [[X]], align 8 | |
| ; CHECK-NEXT: [[TMP0:%.*]] = bitcast i64 [[L]] to <8 x i8> | |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext <8 x i8> [[TMP0]] to <8 x i16> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.vector.reduce.add.v8i16(<8 x i16> [[TMP1]]) | |
| ; CHECK-NEXT: ret i16 [[TMP2]] | |
| ; | |
| start: | |
| %l = load i64, ptr %x, align 8 | |
| %t0 = trunc i64 %l to i16 | |
| %b0 = and i16 %t0, 255 | |
| %t1 = trunc i64 %l to i16 | |
| %b1 = lshr i16 %t1, 8 | |
| %a1 = add nuw nsw i16 %b0, %b1 | |
| %s2 = lshr i64 %l, 16 | |
| %t2 = trunc i64 %s2 to i16 | |
| %b2 = and i16 %t2, 255 | |
| %a2 = add nuw nsw i16 %a1, %b2 | |
| %s3 = lshr i64 %l, 24 | |
| %t3 = trunc i64 %s3 to i16 | |
| %b3 = and i16 %t3, 255 | |
| %a3 = add nuw nsw i16 %a2, %b3 | |
| %s4 = lshr i64 %l, 32 | |
| %t4 = trunc i64 %s4 to i16 | |
| %b4 = and i16 %t4, 255 | |
| %a4 = add nuw nsw i16 %a3, %b4 | |
| %s5 = lshr i64 %l, 40 | |
| %t5 = trunc i64 %s5 to i16 | |
| %b5 = and i16 %t5, 255 | |
| %a5 = add nuw nsw i16 %a4, %b5 | |
| %s6 = lshr i64 %l, 48 | |
| %t6 = trunc i64 %s6 to i16 | |
| %b6 = and i16 %t6, 255 | |
| %a6 = add nuw nsw i16 %a5, %b6 | |
| %s7 = lshr i64 %l, 56 | |
| %b7 = trunc i64 %s7 to i16 | |
| %a7 = add nuw nsw i16 %a6, %b7 | |
| ret i16 %a7 | |
| } | |
| ; Same with ashr: the and-mask kills the sign bits, only the low byte is | |
| ; observed. | |
| define i16 @sum8_i64_ashr(ptr %x) { | |
| ; CHECK-LABEL: define i16 @sum8_i64_ashr( | |
| ; CHECK-SAME: ptr [[X:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i64, ptr [[X]], align 8 | |
| ; CHECK-NEXT: [[TMP0:%.*]] = bitcast i64 [[L]] to <8 x i8> | |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext <8 x i8> [[TMP0]] to <8 x i16> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.vector.reduce.add.v8i16(<8 x i16> [[TMP1]]) | |
| ; CHECK-NEXT: ret i16 [[TMP2]] | |
| ; | |
| start: | |
| %l = load i64, ptr %x, align 8 | |
| %t0 = trunc i64 %l to i16 | |
| %b0 = and i16 %t0, 255 | |
| %s1 = ashr i64 %l, 8 | |
| %t1 = trunc i64 %s1 to i16 | |
| %b1 = and i16 %t1, 255 | |
| %a1 = add nuw nsw i16 %b0, %b1 | |
| %s2 = ashr i64 %l, 16 | |
| %t2 = trunc i64 %s2 to i16 | |
| %b2 = and i16 %t2, 255 | |
| %a2 = add nuw nsw i16 %a1, %b2 | |
| %s3 = ashr i64 %l, 24 | |
| %t3 = trunc i64 %s3 to i16 | |
| %b3 = and i16 %t3, 255 | |
| %a3 = add nuw nsw i16 %a2, %b3 | |
| %s4 = ashr i64 %l, 32 | |
| %t4 = trunc i64 %s4 to i16 | |
| %b4 = and i16 %t4, 255 | |
| %a4 = add nuw nsw i16 %a3, %b4 | |
| %s5 = ashr i64 %l, 40 | |
| %t5 = trunc i64 %s5 to i16 | |
| %b5 = and i16 %t5, 255 | |
| %a5 = add nuw nsw i16 %a4, %b5 | |
| %s6 = ashr i64 %l, 48 | |
| %t6 = trunc i64 %s6 to i16 | |
| %b6 = and i16 %t6, 255 | |
| %a6 = add nuw nsw i16 %a5, %b6 | |
| %s7 = ashr i64 %l, 56 | |
| %t7 = trunc i64 %s7 to i16 | |
| %b7 = and i16 %t7, 255 | |
| %a7 = add nuw nsw i16 %a6, %b7 | |
| ret i16 %a7 | |
| } | |
| ; 16-bit sub-fields: matched and reduced as the field vector with the | |
| ; extension to the i32 lanes. | |
| define i32 @sum4_halves_i64(ptr %p) { | |
| ; CHECK-LABEL: define i32 @sum4_halves_i64( | |
| ; CHECK-SAME: ptr [[P:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i64, ptr [[P]], align 8 | |
| ; CHECK-NEXT: [[TMP0:%.*]] = bitcast i64 [[L]] to <4 x i16> | |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext <4 x i16> [[TMP0]] to <4 x i32> | |
| ; CHECK-NEXT: [[A3:%.*]] = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> [[TMP1]]) | |
| ; CHECK-NEXT: ret i32 [[A3]] | |
| ; | |
| start: | |
| %l = load i64, ptr %p, align 8 | |
| %t0 = trunc i64 %l to i32 | |
| %b0 = and i32 %t0, 65535 | |
| %s1 = lshr i64 %l, 16 | |
| %t1 = trunc i64 %s1 to i32 | |
| %b1 = and i32 %t1, 65535 | |
| %s2 = lshr i64 %l, 32 | |
| %t2 = trunc i64 %s2 to i32 | |
| %b2 = and i32 %t2, 65535 | |
| %s3 = lshr i64 %l, 48 | |
| %b3 = trunc i64 %s3 to i32 | |
| %a1 = add nuw nsw i32 %b0, %b1 | |
| %a2 = add nuw nsw i32 %a1, %b2 | |
| %a3 = add nuw nsw i32 %a2, %b3 | |
| ret i32 %a3 | |
| } | |
| ; Only 4 bytes of an i32: reduced as the field vector with the extension to | |
| ; the i16 lanes. | |
| define i16 @sum4_i32(ptr %p) { | |
| ; CHECK-LABEL: define i16 @sum4_i32( | |
| ; CHECK-SAME: ptr [[P:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[P]], align 4 | |
| ; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32 [[L]] to <4 x i8> | |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext <4 x i8> [[TMP0]] to <4 x i16> | |
| ; CHECK-NEXT: [[A3:%.*]] = call i16 @llvm.vector.reduce.add.v4i16(<4 x i16> [[TMP1]]) | |
| ; CHECK-NEXT: ret i16 [[A3]] | |
| ; | |
| start: | |
| %l = load i32, ptr %p, align 4 | |
| %t0 = trunc i32 %l to i16 | |
| %b0 = and i16 %t0, 255 | |
| %s1 = lshr i32 %l, 8 | |
| %t1 = trunc i32 %s1 to i16 | |
| %b1 = and i16 %t1, 255 | |
| %s2 = lshr i32 %l, 16 | |
| %t2 = trunc i32 %s2 to i16 | |
| %b2 = and i16 %t2, 255 | |
| %s3 = lshr i32 %l, 24 | |
| %b3 = trunc i32 %s3 to i16 | |
| %a1 = add nuw nsw i16 %b0, %b1 | |
| %a2 = add nuw nsw i16 %a1, %b2 | |
| %a3 = add nuw nsw i16 %a2, %b3 | |
| ret i16 %a3 | |
| } | |
| ; The same byte twice: the permutation is not a full one, the natural byte | |
| ; order cannot be used. | |
| define i16 @sum4_i32_dup(ptr %p) { | |
| ; CHECK-LABEL: define i16 @sum4_i32_dup( | |
| ; CHECK-SAME: ptr [[P:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[P]], align 4 | |
| ; CHECK-NEXT: [[T0A:%.*]] = trunc i32 [[L]] to i16 | |
| ; CHECK-NEXT: [[B0A:%.*]] = and i16 [[T0A]], 255 | |
| ; CHECK-NEXT: [[T0B:%.*]] = trunc i32 [[L]] to i8 | |
| ; CHECK-NEXT: [[B0B:%.*]] = zext i8 [[T0B]] to i16 | |
| ; CHECK-NEXT: [[S2:%.*]] = lshr i32 [[L]], 16 | |
| ; CHECK-NEXT: [[T2:%.*]] = trunc i32 [[S2]] to i16 | |
| ; CHECK-NEXT: [[B2:%.*]] = and i16 [[T2]], 255 | |
| ; CHECK-NEXT: [[S3:%.*]] = lshr i32 [[L]], 24 | |
| ; CHECK-NEXT: [[B3:%.*]] = trunc i32 [[S3]] to i16 | |
| ; CHECK-NEXT: [[A1:%.*]] = add nuw nsw i16 [[B0A]], [[B0B]] | |
| ; CHECK-NEXT: [[A2:%.*]] = add nuw nsw i16 [[A1]], [[B2]] | |
| ; CHECK-NEXT: [[A3:%.*]] = add nuw nsw i16 [[A2]], [[B3]] | |
| ; CHECK-NEXT: ret i16 [[A3]] | |
| ; | |
| start: | |
| %l = load i32, ptr %p, align 4 | |
| %t0a = trunc i32 %l to i16 | |
| %b0a = and i16 %t0a, 255 | |
| %t0b = trunc i32 %l to i8 | |
| %b0b = zext i8 %t0b to i16 | |
| %s2 = lshr i32 %l, 16 | |
| %t2 = trunc i32 %s2 to i16 | |
| %b2 = and i16 %t2, 255 | |
| %s3 = lshr i32 %l, 24 | |
| %b3 = trunc i32 %s3 to i16 | |
| %a1 = add nuw nsw i16 %b0a, %b0b | |
| %a2 = add nuw nsw i16 %a1, %b2 | |
| %a3 = add nuw nsw i16 %a2, %b3 | |
| ret i16 %a3 | |
| } | |
| ; Bytes stored to memory: the lane order is observable, the byte vector is | |
| ; permuted. | |
| define void @store_bytes_i64(ptr %p, ptr %out) { | |
| ; CHECK-LABEL: define void @store_bytes_i64( | |
| ; CHECK-SAME: ptr [[P:%.*]], ptr [[OUT:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i64, ptr [[P]], align 8 | |
| ; CHECK-NEXT: [[TMP0:%.*]] = bitcast i64 [[L]] to <8 x i8> | |
| ; CHECK-NEXT: [[TMP1:%.*]] = shufflevector <8 x i8> [[TMP0]], <8 x i8> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = zext <4 x i8> [[TMP1]] to <4 x i16> | |
| ; CHECK-NEXT: store <4 x i16> [[TMP2]], ptr [[OUT]], align 2 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| start: | |
| %l = load i64, ptr %p, align 8 | |
| %t0 = trunc i64 %l to i16 | |
| %b0 = and i16 %t0, 255 | |
| %t1 = trunc i64 %l to i16 | |
| %b1 = lshr i16 %t1, 8 | |
| %s2 = lshr i64 %l, 16 | |
| %t2 = trunc i64 %s2 to i16 | |
| %b2 = and i16 %t2, 255 | |
| %s3 = lshr i64 %l, 24 | |
| %t3 = trunc i64 %s3 to i16 | |
| %b3 = and i16 %t3, 255 | |
| store i16 %b0, ptr %out, align 2 | |
| %o1 = getelementptr i16, ptr %out, i64 1 | |
| store i16 %b1, ptr %o1, align 2 | |
| %o2 = getelementptr i16, ptr %out, i64 2 | |
| store i16 %b2, ptr %o2, align 2 | |
| %o3 = getelementptr i16, ptr %out, i64 3 | |
| store i16 %b3, ptr %o3, align 2 | |
| ret void | |
| } | |
| ; 16-bit sub-fields stored to memory. | |
| define void @store_halves_i64(ptr %p, ptr %out) { | |
| ; CHECK-LABEL: define void @store_halves_i64( | |
| ; CHECK-SAME: ptr [[P:%.*]], ptr [[OUT:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i64, ptr [[P]], align 8 | |
| ; CHECK-NEXT: [[TMP0:%.*]] = bitcast i64 [[L]] to <4 x i16> | |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext <4 x i16> [[TMP0]] to <4 x i32> | |
| ; CHECK-NEXT: store <4 x i32> [[TMP1]], ptr [[OUT]], align 4 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| start: | |
| %l = load i64, ptr %p, align 8 | |
| %t0 = trunc i64 %l to i32 | |
| %b0 = and i32 %t0, 65535 | |
| %s1 = lshr i64 %l, 16 | |
| %t1 = trunc i64 %s1 to i32 | |
| %b1 = and i32 %t1, 65535 | |
| %s2 = lshr i64 %l, 32 | |
| %t2 = trunc i64 %s2 to i32 | |
| %b2 = and i32 %t2, 65535 | |
| %s3 = lshr i64 %l, 48 | |
| %b3 = trunc i64 %s3 to i32 | |
| store i32 %b0, ptr %out, align 4 | |
| %o1 = getelementptr i32, ptr %out, i64 1 | |
| store i32 %b1, ptr %o1, align 4 | |
| %o2 = getelementptr i32, ptr %out, i64 2 | |
| store i32 %b2, ptr %o2, align 4 | |
| %o3 = getelementptr i32, ptr %out, i64 3 | |
| store i32 %b3, ptr %o3, align 4 | |
| ret void | |
| } | |
| ; Not sub-fields: the 16-bit values sit at misaligned offsets. | |
| define i16 @misaligned_fields_i64(ptr %p) { | |
| ; CHECK-LABEL: define i16 @misaligned_fields_i64( | |
| ; CHECK-SAME: ptr [[P:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i64, ptr [[P]], align 8 | |
| ; CHECK-NEXT: [[W0:%.*]] = trunc i64 [[L]] to i16 | |
| ; CHECK-NEXT: [[S1:%.*]] = lshr i64 [[L]], 8 | |
| ; CHECK-NEXT: [[W1:%.*]] = trunc i64 [[S1]] to i16 | |
| ; CHECK-NEXT: [[A:%.*]] = add i16 [[W0]], [[W1]] | |
| ; CHECK-NEXT: ret i16 [[A]] | |
| ; | |
| start: | |
| %l = load i64, ptr %p, align 8 | |
| %w0 = trunc i64 %l to i16 | |
| %s1 = lshr i64 %l, 8 | |
| %w1 = trunc i64 %s1 to i16 | |
| %a = add i16 %w0, %w1 | |
| ret i16 %a | |
| } | |
| ; Same fields as in sum8_i64, but reduced in a scrambled order: the lane | |
| ; order of the reduction root is unobservable, the fields are emitted in the | |
| ; natural order and no permutation is needed. | |
| define i16 @sum8_i64_scrambled(ptr %x) { | |
| ; CHECK-LABEL: define i16 @sum8_i64_scrambled( | |
| ; CHECK-SAME: ptr [[X:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i64, ptr [[X]], align 8 | |
| ; CHECK-NEXT: [[TMP0:%.*]] = bitcast i64 [[L]] to <8 x i8> | |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext <8 x i8> [[TMP0]] to <8 x i16> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.vector.reduce.add.v8i16(<8 x i16> [[TMP1]]) | |
| ; CHECK-NEXT: ret i16 [[TMP2]] | |
| ; | |
| start: | |
| %l = load i64, ptr %x, align 8 | |
| %t0 = trunc i64 %l to i16 | |
| %b0 = and i16 %t0, 255 | |
| %s1 = lshr i64 %l, 8 | |
| %t1 = trunc i64 %s1 to i16 | |
| %b1 = and i16 %t1, 255 | |
| %s2 = lshr i64 %l, 16 | |
| %t2 = trunc i64 %s2 to i16 | |
| %b2 = and i16 %t2, 255 | |
| %s3 = lshr i64 %l, 24 | |
| %t3 = trunc i64 %s3 to i16 | |
| %b3 = and i16 %t3, 255 | |
| %s4 = lshr i64 %l, 32 | |
| %t4 = trunc i64 %s4 to i16 | |
| %b4 = and i16 %t4, 255 | |
| %s5 = lshr i64 %l, 40 | |
| %t5 = trunc i64 %s5 to i16 | |
| %b5 = and i16 %t5, 255 | |
| %s6 = lshr i64 %l, 48 | |
| %t6 = trunc i64 %s6 to i16 | |
| %b6 = and i16 %t6, 255 | |
| %s7 = lshr i64 %l, 56 | |
| %b7 = trunc i64 %s7 to i16 | |
| %a1 = add nuw nsw i16 %b3, %b0 | |
| %a2 = add nuw nsw i16 %a1, %b5 | |
| %a3 = add nuw nsw i16 %a2, %b1 | |
| %a4 = add nuw nsw i16 %a3, %b7 | |
| %a5 = add nuw nsw i16 %a4, %b2 | |
| %a6 = add nuw nsw i16 %a5, %b6 | |
| %a7 = add nuw nsw i16 %a6, %b4 | |
| ret i16 %a7 | |
| } | |
| ; A gathered field with an external use stays as a scalar; the lane order of | |
| ; the reduction root gather is still unobservable, so the permutation is | |
| ; elided. | |
| define i16 @sum8_i64_ext_use(ptr %p, ptr %out) { | |
| ; CHECK-LABEL: define i16 @sum8_i64_ext_use( | |
| ; CHECK-SAME: ptr [[P:%.*]], ptr [[OUT:%.*]]) { | |
| ; CHECK-NEXT: [[START:.*:]] | |
| ; CHECK-NEXT: [[L:%.*]] = load i64, ptr [[P]], align 8 | |
| ; CHECK-NEXT: [[S3:%.*]] = lshr i64 [[L]], 24 | |
| ; CHECK-NEXT: [[T6:%.*]] = trunc i64 [[S3]] to i16 | |
| ; CHECK-NEXT: [[B6:%.*]] = and i16 [[T6]], 255 | |
| ; CHECK-NEXT: [[TMP0:%.*]] = bitcast i64 [[L]] to <8 x i8> | |
| ; CHECK-NEXT: [[TMP1:%.*]] = zext <8 x i8> [[TMP0]] to <8 x i16> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.vector.reduce.add.v8i16(<8 x i16> [[TMP1]]) | |
| ; CHECK-NEXT: store i16 [[B6]], ptr [[OUT]], align 2 | |
| ; CHECK-NEXT: ret i16 [[TMP2]] | |
| ; | |
| start: | |
| %l = load i64, ptr %p, align 8 | |
| %t0 = trunc i64 %l to i16 | |
| %b0 = and i16 %t0, 255 | |
| %t1 = trunc i64 %l to i16 | |
| %b1 = lshr i16 %t1, 8 | |
| %s2 = lshr i64 %l, 16 | |
| %t2 = trunc i64 %s2 to i16 | |
| %b2 = and i16 %t2, 255 | |
| %s3 = lshr i64 %l, 24 | |
| %t3 = trunc i64 %s3 to i16 | |
| %b3 = and i16 %t3, 255 | |
| %s4 = lshr i64 %l, 32 | |
| %t4 = trunc i64 %s4 to i16 | |
| %b4 = and i16 %t4, 255 | |
| %s5 = lshr i64 %l, 40 | |
| %t5 = trunc i64 %s5 to i16 | |
| %b5 = and i16 %t5, 255 | |
| %s6 = lshr i64 %l, 48 | |
| %t6 = trunc i64 %s6 to i16 | |
| %b6 = and i16 %t6, 255 | |
| %s7 = lshr i64 %l, 56 | |
| %b7 = trunc i64 %s7 to i16 | |
| %a1 = add nuw nsw i16 %b0, %b1 | |
| %a2 = add nuw nsw i16 %a1, %b2 | |
| %a3 = add nuw nsw i16 %a2, %b3 | |
| %a4 = add nuw nsw i16 %a3, %b4 | |
| %a5 = add nuw nsw i16 %a4, %b5 | |
| %a6 = add nuw nsw i16 %a5, %b6 | |
| %a7 = add nuw nsw i16 %a6, %b7 | |
| store i16 %b3, ptr %out, align 2 | |
| ret i16 %a7 | |
| } | |
| define void @vectorized_src(ptr %p, ptr %a, i64 %x) { | |
| ; CHECK-LABEL: define void @vectorized_src( | |
| ; CHECK-SAME: ptr [[P:%.*]], ptr [[A:%.*]], i64 [[X:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 | |
| ; CHECK-NEXT: [[TMP0:%.*]] = load <2 x i64>, ptr [[P]], align 8 | |
| ; CHECK-NEXT: [[LD1:%.*]] = load i64, ptr [[GEP]], align 8 | |
| ; CHECK-NEXT: [[TMP1:%.*]] = bitcast i64 [[LD1]] to <2 x i32> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = sitofp <2 x i32> [[TMP1]] to <2 x double> | |
| ; CHECK-NEXT: [[TMP3:%.*]] = trunc <2 x i64> [[TMP0]] to <2 x i32> | |
| ; CHECK-NEXT: [[TMP4:%.*]] = trunc i64 [[X]] to i32 | |
| ; CHECK-NEXT: [[TMP5:%.*]] = insertelement <2 x i32> [[TMP3]], i32 [[TMP4]], i64 1 | |
| ; CHECK-NEXT: [[TMP6:%.*]] = sitofp <2 x i32> [[TMP5]] to <2 x double> | |
| ; CHECK-NEXT: [[TMP7:%.*]] = fsub <2 x double> [[TMP2]], [[TMP6]] | |
| ; CHECK-NEXT: store <2 x double> [[TMP7]], ptr [[A]], align 8 | |
| ; CHECK-NEXT: ret void | |
| ; | |
| entry: | |
| %ld0 = load i64, ptr %p, align 8 | |
| %gep = getelementptr inbounds nuw i8, ptr %p, i64 8 | |
| %ld1 = load i64, ptr %gep, align 8 | |
| %t0 = trunc i64 %ld1 to i32 | |
| %c0 = sitofp i32 %t0 to double | |
| %t1 = trunc i64 %ld0 to i32 | |
| %c1 = sitofp i32 %t1 to double | |
| %sub0 = fsub double %c0, %c1 | |
| store double %sub0, ptr %a, align 8 | |
| %s = lshr i64 %ld1, 32 | |
| %t2 = trunc nuw i64 %s to i32 | |
| %c2 = sitofp i32 %t2 to double | |
| %t3 = trunc i64 %x to i32 | |
| %c3 = sitofp i32 %t3 to double | |
| %sub1 = fsub double %c2, %c3 | |
| %gep.a = getelementptr inbounds nuw i8, ptr %a, i64 8 | |
| store double %sub1, ptr %gep.a, align 8 | |
| ret void | |
| } | |
| ; A byte reused in the add reduction: the fields of the reduction root are | |
| ; emitted in the natural order, the repeat count scales the lane of that byte. | |
| define i32 @sum4_i32_reused(i32 %x) { | |
| ; CHECK-LABEL: define i32 @sum4_i32_reused( | |
| ; CHECK-SAME: i32 [[X:%.*]]) { | |
| ; CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[X]] to <4 x i8> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = zext <4 x i8> [[TMP1]] to <4 x i16> | |
| ; CHECK-NEXT: [[TMP3:%.*]] = zext <4 x i16> [[TMP2]] to <4 x i32> | |
| ; CHECK-NEXT: [[TMP4:%.*]] = mul <4 x i32> [[TMP3]], <i32 1, i32 1, i32 1, i32 3> | |
| ; CHECK-NEXT: [[TMP5:%.*]] = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> [[TMP4]]) | |
| ; CHECK-NEXT: ret i32 [[TMP5]] | |
| ; | |
| %t0 = trunc i32 %x to i8 | |
| %s1 = lshr i32 %x, 8 | |
| %t1 = trunc i32 %s1 to i8 | |
| %s2 = lshr i32 %x, 16 | |
| %t2 = trunc i32 %s2 to i8 | |
| %s3 = lshr i32 %x, 24 | |
| %t3 = trunc i32 %s3 to i8 | |
| %f0 = zext i8 %t0 to i32 | |
| %f1 = zext i8 %t1 to i32 | |
| %f2 = zext i8 %t2 to i32 | |
| %f3 = zext i8 %t3 to i32 | |
| %a = add i32 %f2, %f0 | |
| %b = add i32 %a, %f3 | |
| %c = add i32 %b, %f1 | |
| %d = add i32 %c, %f3 | |
| %e = add i32 %d, %f3 | |
| ret i32 %e | |
| } | |
| ; A byte reused twice in the xor reduction cancels out: the lane of that byte | |
| ; is zeroed in the natural order of the fields of the reduction root. | |
| define i16 @xor8_i64_reused(i64 %x) { | |
| ; CHECK-LABEL: define i16 @xor8_i64_reused( | |
| ; CHECK-SAME: i64 [[X:%.*]]) { | |
| ; CHECK-NEXT: [[TMP1:%.*]] = bitcast i64 [[X]] to <8 x i8> | |
| ; CHECK-NEXT: [[TMP2:%.*]] = zext <8 x i8> [[TMP1]] to <8 x i16> | |
| ; CHECK-NEXT: [[TMP3:%.*]] = shufflevector <8 x i16> [[TMP2]], <8 x i16> zeroinitializer, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 8> | |
| ; CHECK-NEXT: [[TMP4:%.*]] = call i16 @llvm.vector.reduce.xor.v8i16(<8 x i16> [[TMP3]]) | |
| ; CHECK-NEXT: ret i16 [[TMP4]] | |
| ; | |
| %t0 = trunc i64 %x to i8 | |
| %f0 = zext i8 %t0 to i16 | |
| %s1 = lshr i64 %x, 8 | |
| %t1 = trunc i64 %s1 to i8 | |
| %f1 = zext i8 %t1 to i16 | |
| %s2 = lshr i64 %x, 16 | |
| %t2 = trunc i64 %s2 to i8 | |
| %f2 = zext i8 %t2 to i16 | |
| %s3 = lshr i64 %x, 24 | |
| %t3 = trunc i64 %s3 to i8 | |
| %f3 = zext i8 %t3 to i16 | |
| %s4 = lshr i64 %x, 32 | |
| %t4 = trunc i64 %s4 to i8 | |
| %f4 = zext i8 %t4 to i16 | |
| %s5 = lshr i64 %x, 40 | |
| %t5 = trunc i64 %s5 to i8 | |
| %f5 = zext i8 %t5 to i16 | |
| %s6 = lshr i64 %x, 48 | |
| %t6 = trunc i64 %s6 to i8 | |
| %f6 = zext i8 %t6 to i16 | |
| %s7 = lshr i64 %x, 56 | |
| %t7 = trunc i64 %s7 to i8 | |
| %f7 = zext i8 %t7 to i16 | |
| %r0 = xor i16 %f5, %f2 | |
| %r1 = xor i16 %r0, %f7 | |
| %r2 = xor i16 %r1, %f0 | |
| %r3 = xor i16 %r2, %f3 | |
| %r4 = xor i16 %r3, %f6 | |
| %r5 = xor i16 %r4, %f1 | |
| %r6 = xor i16 %r5, %f4 | |
| %r7 = xor i16 %r6, %f7 | |
| ret i16 %r7 | |
| } |