blob: d02cf1324b983d061a65ea162890df060290c4d5 [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
; Codegen for load/store addresses of the form (base + large constant offset)
; under the Qualcomm Xqcilo/Xqcilia extensions.
;
; RUN: llc < %s -mtriple=riscv32 -mattr=+xqcilo,+xqcilia -riscv-no-aliases \
; RUN: | FileCheck %s --check-prefix=CHECK
;===----------------------------------------------------------------------===;
; Path A: offset in simm26 but not simm12 -> folded directly into qc.e.lw/sw.
;===----------------------------------------------------------------------===;
define i32 @load_simm26_pos(ptr %p) nounwind optsize {
; CHECK-LABEL: load_simm26_pos:
; CHECK: # %bb.0:
; CHECK-NEXT: qc.e.lw a0, 3000000(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 3000000
%v = load i32, ptr %g, align 4
ret i32 %v
}
; Positive simm26 boundary (maxIntN(26) = 33554431).
define i32 @load_simm26_pos_max(ptr %p) nounwind optsize {
; CHECK-LABEL: load_simm26_pos_max:
; CHECK: # %bb.0:
; CHECK-NEXT: qc.e.lw a0, 33554431(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 33554431
%v = load i32, ptr %g, align 4
ret i32 %v
}
define i32 @load_simm26_neg(ptr %p) nounwind optsize {
; CHECK-LABEL: load_simm26_neg:
; CHECK: # %bb.0:
; CHECK-NEXT: qc.e.lw a0, -3000000(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -3000000
%v = load i32, ptr %g, align 4
ret i32 %v
}
; Negative simm26 boundary (minIntN(26) = -33554432).
define i32 @load_simm26_neg_max(ptr %p) nounwind optsize {
; CHECK-LABEL: load_simm26_neg_max:
; CHECK: # %bb.0:
; CHECK-NEXT: qc.e.lw a0, -33554432(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -33554432
%v = load i32, ptr %g, align 4
ret i32 %v
}
define void @store_simm26_pos(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_simm26_pos:
; CHECK: # %bb.0:
; CHECK-NEXT: qc.e.sw a1, 3000000(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 3000000
store i32 %v, ptr %g, align 4
ret void
}
define void @store_simm26_neg(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_simm26_neg:
; CHECK: # %bb.0:
; CHECK-NEXT: qc.e.sw a1, -3000000(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -3000000
store i32 %v, ptr %g, align 4
ret void
}
;===----------------------------------------------------------------------===;
; Path B: offset just outside simm26 where the saturation adjustment fits simm6
; -> compressible ADDI (c.addi) + qc.e.lw/sw.
;===----------------------------------------------------------------------===;
; Positive, boundary (maxIntN(26) + 31): +31 adjustment fits simm6.
define i32 @load_band_pos_boundary(ptr %p) nounwind optsize {
; CHECK-LABEL: load_band_pos_boundary:
; CHECK: # %bb.0:
; CHECK-NEXT: c.addi a0, 31
; CHECK-NEXT: qc.e.lw a0, 33554431(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 33554462
%v = load i32, ptr %g, align 4
ret i32 %v
}
; Negative, boundary (minIntN(26) - 32): -32 adjustment fits simm6.
define i32 @load_band_neg_boundary(ptr %p) nounwind optsize {
; CHECK-LABEL: load_band_neg_boundary:
; CHECK: # %bb.0:
; CHECK-NEXT: c.addi a0, -32
; CHECK-NEXT: qc.e.lw a0, -33554432(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -33554464
%v = load i32, ptr %g, align 4
ret i32 %v
}
define void @store_band_pos_boundary(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_band_pos_boundary:
; CHECK: # %bb.0:
; CHECK-NEXT: c.addi a0, 31
; CHECK-NEXT: qc.e.sw a1, 33554431(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 33554462
store i32 %v, ptr %g, align 4
ret void
}
define void @store_band_neg_boundary(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_band_neg_boundary:
; CHECK: # %bb.0:
; CHECK-NEXT: c.addi a0, -32
; CHECK-NEXT: qc.e.sw a1, -33554432(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -33554464
store i32 %v, ptr %g, align 4
ret void
}
;===----------------------------------------------------------------------===;
; Path C: the saturation adjustment does not fit simm6, but the residual low-12
; offset is 4-aligned and in the c.lw range, therefore the standard lui + add +
; c.lw/c.sw sequence (with a compressible access) is used.
;===----------------------------------------------------------------------===;
; Positive, low-12 offset 32 (4-aligned, in c.lw range).
define i32 @load_pathC_pos(ptr %p) nounwind optsize {
; CHECK-LABEL: load_pathC_pos:
; CHECK: # %bb.0:
; CHECK-NEXT: lui a1, 8192
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: c.lw a0, 32(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 33554464
%v = load i32, ptr %g, align 4
ret i32 %v
}
; Negative, low-12 offset 0 (4-aligned, in c.lw range).
define i32 @load_pathC_neg(ptr %p) nounwind optsize {
; CHECK-LABEL: load_pathC_neg:
; CHECK: # %bb.0:
; CHECK-NEXT: lui a1, 1040383
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: c.lw a0, 0(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -33558528
%v = load i32, ptr %g, align 4
ret i32 %v
}
define void @store_pathC_pos(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_pathC_pos:
; CHECK: # %bb.0:
; CHECK-NEXT: lui a2, 8192
; CHECK-NEXT: c.add a0, a2
; CHECK-NEXT: c.sw a1, 32(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 33554464
store i32 %v, ptr %g, align 4
ret void
}
define void @store_pathC_neg(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_pathC_neg:
; CHECK: # %bb.0:
; CHECK-NEXT: lui a2, 1040383
; CHECK-NEXT: c.add a0, a2
; CHECK-NEXT: c.sw a1, 0(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -33558528
store i32 %v, ptr %g, align 4
ret void
}
;===----------------------------------------------------------------------===;
; Path D: the saturation adjustment does not fit simm6, the residual low-12
; offset is not c.lw-compressible, therefore ADDI + qc.e.lw/sw is emitted.
;===----------------------------------------------------------------------===;
define i32 @load_pathD_pos(ptr %p) nounwind optsize {
; CHECK-LABEL: load_pathD_pos:
; CHECK: # %bb.0:
; CHECK-NEXT: addi a0, a0, 129
; CHECK-NEXT: qc.e.lw a0, 33554431(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 33554560
%v = load i32, ptr %g, align 4
ret i32 %v
}
define i32 @load_pathD_neg(ptr %p) nounwind optsize {
; CHECK-LABEL: load_pathD_neg:
; CHECK: # %bb.0:
; CHECK-NEXT: addi a0, a0, -128
; CHECK-NEXT: qc.e.lw a0, -33554432(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -33554560
%v = load i32, ptr %g, align 4
ret i32 %v
}
define void @store_pathD_pos(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_pathD_pos:
; CHECK: # %bb.0:
; CHECK-NEXT: addi a0, a0, 129
; CHECK-NEXT: qc.e.sw a1, 33554431(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 33554560
store i32 %v, ptr %g, align 4
ret void
}
define void @store_pathD_neg(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_pathD_neg:
; CHECK: # %bb.0:
; CHECK-NEXT: addi a0, a0, -128
; CHECK-NEXT: qc.e.sw a1, -33554432(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -33554560
store i32 %v, ptr %g, align 4
ret void
}
;===----------------------------------------------------------------------===;
; Path E: the offset is so far outside simm26 that even the simm12 split does
; not land back in simm26 therefore lui + add + lw is used and no qc.e.addai is
; emitted for a single-use address.
;===----------------------------------------------------------------------===;
define i32 @load_too_large(ptr %p) nounwind optsize {
; CHECK-LABEL: load_too_large:
; CHECK: # %bb.0:
; CHECK-NEXT: lui a1, 17090
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: lw a0, -640(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 70000000
%v = load i32, ptr %g, align 4
ret i32 %v
}
define i32 @load_too_large_neg(ptr %p) nounwind optsize {
; CHECK-LABEL: load_too_large_neg:
; CHECK: # %bb.0:
; CHECK-NEXT: lui a1, 1031486
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: lw a0, 640(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -70000000
%v = load i32, ptr %g, align 4
ret i32 %v
}
define void @store_too_large(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_too_large:
; CHECK: # %bb.0:
; CHECK-NEXT: lui a2, 17090
; CHECK-NEXT: c.add a0, a2
; CHECK-NEXT: sw a1, -640(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 70000000
store i32 %v, ptr %g, align 4
ret void
}
define void @store_too_large_neg(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: store_too_large_neg:
; CHECK: # %bb.0:
; CHECK-NEXT: lui a2, 1031486
; CHECK-NEXT: c.add a0, a2
; CHECK-NEXT: sw a1, 640(a0)
; CHECK-NEXT: c.jr ra
%g = getelementptr inbounds nuw i8, ptr %p, i32 -70000000
store i32 %v, ptr %g, align 4
ret void
}
;===----------------------------------------------------------------------===;
; Path F: Shared-base / reuse cases: multiple accesses off the same pointer whose
; large offsets are close enough to share one materialized base.
;===----------------------------------------------------------------------===;
; Base %p reused (also loaded at a small offset), two accesses.
define i32 @load_band_reuse2(ptr %p) nounwind optsize {
; CHECK-LABEL: load_band_reuse2:
; CHECK: # %bb.0:
; CHECK-NEXT: addi a1, a0, 569
; CHECK-NEXT: c.lw a0, 100(a0)
; CHECK-NEXT: qc.e.lw a1, 33554431(a1)
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: c.jr ra
%g0 = getelementptr inbounds nuw i8, ptr %p, i32 33555000
%v0 = load i32, ptr %g0, align 4
%g1 = getelementptr inbounds nuw i8, ptr %p, i32 100
%v1 = load i32, ptr %g1, align 4
%r1 = add i32 %v1, %v0
ret i32 %r1
}
; Base %p reused with a third large-offset access sharing the common base.
define i32 @load_band_reuse(ptr %p) nounwind optsize {
; CHECK-LABEL: load_band_reuse:
; CHECK: # %bb.0:
; CHECK-NEXT: c.mv a1, a0
; CHECK-NEXT: qc.e.addai a1, 33554462
; CHECK-NEXT: c.lw a0, 100(a0)
; CHECK-NEXT: lw a2, 538(a1)
; CHECK-NEXT: c.lw a1, 0(a1)
; CHECK-NEXT: c.add a0, a2
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: c.jr ra
%g0 = getelementptr inbounds nuw i8, ptr %p, i32 33555000
%v0 = load i32, ptr %g0, align 4
%g1 = getelementptr inbounds nuw i8, ptr %p, i32 100
%v1 = load i32, ptr %g1, align 4
%g2 = getelementptr inbounds nuw i8, ptr %p, i32 33554462
%v2 = load i32, ptr %g2, align 4
%r1 = add i32 %v1, %v0
%r2 = add i32 %r1, %v2
ret i32 %r2
}
; Four large-offset accesses; the last one's delta from the shared base exceeds
; simm26 (by 1), so it needs a separate base materialization.
define i32 @load_band_reuse_4(ptr %p) nounwind optsize {
; CHECK-LABEL: load_band_reuse_4:
; CHECK: # %bb.0:
; CHECK-NEXT: lui a1, 16384
; CHECK-NEXT: c.mv a2, a0
; CHECK-NEXT: c.lw a3, 100(a0)
; CHECK-NEXT: qc.e.addai a2, 33554462
; CHECK-NEXT: lw a4, 538(a2)
; CHECK-NEXT: c.lw a5, 0(a2)
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: qc.e.lw a1, 33554431(a2)
; CHECK-NEXT: lw a0, 30(a0)
; CHECK-NEXT: c.add a3, a4
; CHECK-NEXT: c.add a1, a5
; CHECK-NEXT: c.add a1, a3
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: c.jr ra
%g0 = getelementptr inbounds nuw i8, ptr %p, i32 33555000
%v0 = load i32, ptr %g0, align 4
%g1 = getelementptr inbounds nuw i8, ptr %p, i32 100
%v1 = load i32, ptr %g1, align 4
%g2 = getelementptr inbounds nuw i8, ptr %p, i32 33554462
%v2 = load i32, ptr %g2, align 4
%g3 = getelementptr inbounds nuw i8, ptr %p, i32 67108893
%v3 = load i32, ptr %g3, align 4
%g4 = getelementptr inbounds nuw i8, ptr %p, i32 67108894
%v4 = load i32, ptr %g4, align 4
%r1 = add i32 %v1, %v0
%r2 = add i32 %r1, %v2
%r3 = add i32 %r2, %v3
%r4 = add i32 %r3, %v4
ret i32 %r4
}
; Two loads whose offsets differ by a foldable 26-bit amount: the DAG shares a
; common base %p + 70000000 (materialized once as qc.e.addai) and folds the
; second access's residual into a qc.e.lw.
define i32 @qcaddai_plus_qcelw(ptr %p) nounwind optsize {
; CHECK-LABEL: qcaddai_plus_qcelw:
; CHECK: # %bb.0:
; CHECK-NEXT: qc.e.addai a0, 70000000
; CHECK-NEXT: c.lw a1, 0(a0)
; CHECK-NEXT: qc.e.lw a0, 40000(a0)
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: c.jr ra
%g0 = getelementptr inbounds nuw i8, ptr %p, i32 70000000
%v0 = load i32, ptr %g0, align 4
%g1 = getelementptr inbounds nuw i8, ptr %p, i32 70040000
%v1 = load i32, ptr %g1, align 4
%r = add i32 %v0, %v1
ret i32 %r
}
; Store variant of the shared-base qc.e.addai case above.
define void @qcaddai_plus_qcesw(ptr %p, i32 %v) nounwind optsize {
; CHECK-LABEL: qcaddai_plus_qcesw:
; CHECK: # %bb.0:
; CHECK-NEXT: qc.e.addai a0, 70000000
; CHECK-NEXT: c.sw a1, 0(a0)
; CHECK-NEXT: qc.e.sw a1, 40000(a0)
; CHECK-NEXT: c.jr ra
%g0 = getelementptr inbounds nuw i8, ptr %p, i32 70000000
store i32 %v, ptr %g0, align 4
%g1 = getelementptr inbounds nuw i8, ptr %p, i32 70040000
store i32 %v, ptr %g1, align 4
ret void
}
;===----------------------------------------------------------------------===;
; Path G: Offset is within compressed immediate range for loads however due to
; register pressure, register allocator is not able to allocate any register
; from GPRC register set resulting in lw not being compressed.
;===----------------------------------------------------------------------===;
define i32 @load_no_clw_reg_pressure(ptr %p) nounwind {
; CHECK-LABEL: load_no_clw_reg_pressure:
; CHECK: # %bb.0:
; CHECK-NEXT: c.lw a1, 0(a0)
; CHECK-NEXT: c.lw a2, 4(a0)
; CHECK-NEXT: c.lw a3, 8(a0)
; CHECK-NEXT: c.lw a4, 12(a0)
; CHECK-NEXT: c.lw a5, 16(a0)
; CHECK-NEXT: lw a6, 20(a0)
; CHECK-NEXT: lw a7, 24(a0)
; CHECK-NEXT: lw t0, 28(a0)
; CHECK-NEXT: lw t1, 32(a0)
; CHECK-NEXT: lw t2, 36(a0)
; CHECK-NEXT: c.add a1, a2
; CHECK-NEXT: c.add a3, a4
; CHECK-NEXT: c.lw a2, 40(a0)
; CHECK-NEXT: c.lw a0, 44(a0)
; CHECK-NEXT: c.add a1, a3
; CHECK-NEXT: c.add a5, a6
; CHECK-NEXT: c.add t0, t1
; CHECK-NEXT: c.add a5, a7
; CHECK-NEXT: c.add t0, t2
; CHECK-NEXT: c.add a1, a5
; CHECK-NEXT: c.add a2, t0
; CHECK-NEXT: c.add a1, a2
; CHECK-NEXT: c.add a0, a1
; CHECK-NEXT: c.jr ra
%g0 = getelementptr inbounds nuw i8, ptr %p, i32 0
%g1 = getelementptr inbounds nuw i8, ptr %p, i32 4
%g2 = getelementptr inbounds nuw i8, ptr %p, i32 8
%g3 = getelementptr inbounds nuw i8, ptr %p, i32 12
%g4 = getelementptr inbounds nuw i8, ptr %p, i32 16
%g5 = getelementptr inbounds nuw i8, ptr %p, i32 20
%g6 = getelementptr inbounds nuw i8, ptr %p, i32 24
%g7 = getelementptr inbounds nuw i8, ptr %p, i32 28
%g8 = getelementptr inbounds nuw i8, ptr %p, i32 32
%g9 = getelementptr inbounds nuw i8, ptr %p, i32 36
%g10 = getelementptr inbounds nuw i8, ptr %p, i32 40
%g11 = getelementptr inbounds nuw i8, ptr %p, i32 44
%v0 = load i32, ptr %g0
%v1 = load i32, ptr %g1
%v2 = load i32, ptr %g2
%v3 = load i32, ptr %g3
%v4 = load i32, ptr %g4
%v5 = load i32, ptr %g5
%v6 = load i32, ptr %g6
%v7 = load i32, ptr %g7
%v8 = load i32, ptr %g8
%v9 = load i32, ptr %g9
%v10 = load i32, ptr %g10
%v11 = load i32, ptr %g11
%s0 = add i32 %v0, %v1
%s1 = add i32 %s0, %v2
%s2 = add i32 %s1, %v3
%s3 = add i32 %s2, %v4
%s4 = add i32 %s3, %v5
%s5 = add i32 %s4, %v6
%s6 = add i32 %s5, %v7
%s7 = add i32 %s6, %v8
%s8 = add i32 %s7, %v9
%s9 = add i32 %s8, %v10
%s10 = add i32 %s9, %v11
ret i32 %s10
}