| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: llc -mtriple=loongarch64 -mattr=-ual -verify-machineinstrs < %s | FileCheck %s --check-prefix=NOUAL | |
| ; RUN: llc -mtriple=loongarch64 -mattr=+ual -verify-machineinstrs < %s | FileCheck %s --check-prefix=UAL | |
| ; An i32 result does not require sign extension to i64: it becomes | |
| ; extloadi32 instead of sextloadi32. Select LDPTR.W rather than materializing | |
| ; the offset with ORI/LU12I.W and loading through LDX.W. | |
| define i32 @anyext_2048(ptr %base) { | |
| ; NOUAL-LABEL: anyext_2048: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: ldptr.w $a0, $a0, 2048 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: anyext_2048: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: ldptr.w $a0, $a0, 2048 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 2048 | |
| %v = load volatile i32, ptr %p, align 4 | |
| ret i32 %v | |
| } | |
| define i32 @anyext_7960(ptr %base) { | |
| ; NOUAL-LABEL: anyext_7960: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: ldptr.w $a0, $a0, 7960 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: anyext_7960: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: ldptr.w $a0, $a0, 7960 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 7960 | |
| %v = load volatile i32, ptr %p, align 4 | |
| ret i32 %v | |
| } | |
| define i32 @anyext_negative(ptr %base) { | |
| ; NOUAL-LABEL: anyext_negative: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: ldptr.w $a0, $a0, -2052 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: anyext_negative: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: ldptr.w $a0, $a0, -2052 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 -2052 | |
| %v = load volatile i32, ptr %p, align 4 | |
| ret i32 %v | |
| } | |
| define i32 @anyext_min(ptr %base) { | |
| ; NOUAL-LABEL: anyext_min: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: ldptr.w $a0, $a0, -32768 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: anyext_min: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: ldptr.w $a0, $a0, -32768 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 -32768 | |
| %v = load volatile i32, ptr %p, align 4 | |
| ret i32 %v | |
| } | |
| define i32 @anyext_max(ptr %base) { | |
| ; NOUAL-LABEL: anyext_max: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: ldptr.w $a0, $a0, 32764 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: anyext_max: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: ldptr.w $a0, $a0, 32764 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 32764 | |
| %v = load volatile i32, ptr %p, align 4 | |
| ret i32 %v | |
| } | |
| ; Prefer LD.W for offsets that fit its signed 12-bit immediate. | |
| define i32 @anyext_simm12(ptr %base) { | |
| ; NOUAL-LABEL: anyext_simm12: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: ld.w $a0, $a0, 2044 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: anyext_simm12: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: ld.w $a0, $a0, 2044 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 2044 | |
| %v = load volatile i32, ptr %p, align 4 | |
| ret i32 %v | |
| } | |
| ; Out-of-range offsets must not be encoded as LDPTR.W. | |
| define i32 @anyext_overflow(ptr %base) { | |
| ; NOUAL-LABEL: anyext_overflow: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: lu12i.w $a1, 8 | |
| ; NOUAL-NEXT: ldx.w $a0, $a0, $a1 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: anyext_overflow: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: lu12i.w $a1, 8 | |
| ; UAL-NEXT: ldx.w $a0, $a0, $a1 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 32768 | |
| %v = load volatile i32, ptr %p, align 4 | |
| ret i32 %v | |
| } | |
| ; A zextloadi32 cannot be implemented by the sign-extending LDPTR.W. | |
| define i64 @zext_is_not_ldptr(ptr %base) { | |
| ; NOUAL-LABEL: zext_is_not_ldptr: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: lu12i.w $a1, 1 | |
| ; NOUAL-NEXT: ori $a1, $a1, 3864 | |
| ; NOUAL-NEXT: ldx.wu $a0, $a0, $a1 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: zext_is_not_ldptr: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: lu12i.w $a1, 1 | |
| ; UAL-NEXT: ori $a1, $a1, 3864 | |
| ; UAL-NEXT: ldx.wu $a0, $a0, $a1 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 7960 | |
| %v = load volatile i32, ptr %p, align 4 | |
| %x = zext i32 %v to i64 | |
| ret i64 %x | |
| } | |
| ; The following forms are already handled by existing upstream patterns. | |
| define i64 @sext_already_supported(ptr %base) { | |
| ; NOUAL-LABEL: sext_already_supported: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: ldptr.w $a0, $a0, 7960 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: sext_already_supported: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: ldptr.w $a0, $a0, 7960 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 7960 | |
| %v = load volatile i32, ptr %p, align 4 | |
| %x = sext i32 %v to i64 | |
| ret i64 %x | |
| } | |
| define i64 @load64_already_supported(ptr %base) { | |
| ; NOUAL-LABEL: load64_already_supported: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: ldptr.d $a0, $a0, 7960 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: load64_already_supported: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: ldptr.d $a0, $a0, 7960 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 7960 | |
| %v = load volatile i64, ptr %p, align 8 | |
| ret i64 %v | |
| } | |
| define void @store32_already_supported(ptr %base, i32 %value) { | |
| ; NOUAL-LABEL: store32_already_supported: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: stptr.w $a1, $a0, 7960 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: store32_already_supported: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: stptr.w $a1, $a0, 7960 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 7960 | |
| store volatile i32 %value, ptr %p, align 4 | |
| ret void | |
| } | |
| define void @store64_already_supported(ptr %base, i64 %value) { | |
| ; NOUAL-LABEL: store64_already_supported: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: stptr.d $a1, $a0, 7960 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: store64_already_supported: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: stptr.d $a1, $a0, 7960 | |
| ; UAL-NEXT: ret | |
| %p = getelementptr i8, ptr %base, i64 7960 | |
| store volatile i64 %value, ptr %p, align 8 | |
| ret void | |
| } | |
| ; Nearby loads may share address materialization rather than | |
| ; independently selecting LDPTR.W for every offset. | |
| ; Ensure the first eligible load still selects LDPTR.W. | |
| define i32 @four_nearby_loads(ptr %base) { | |
| ; NOUAL-LABEL: four_nearby_loads: | |
| ; NOUAL: # %bb.0: | |
| ; NOUAL-NEXT: lu12i.w $a1, 1 | |
| ; NOUAL-NEXT: ori $a1, $a1, 3864 | |
| ; NOUAL-NEXT: add.d $a1, $a0, $a1 | |
| ; NOUAL-NEXT: ldptr.w $a0, $a0, 7960 | |
| ; NOUAL-NEXT: ld.w $a2, $a1, 4 | |
| ; NOUAL-NEXT: ld.w $a3, $a1, 8 | |
| ; NOUAL-NEXT: ld.w $a1, $a1, 12 | |
| ; NOUAL-NEXT: add.d $a0, $a0, $a2 | |
| ; NOUAL-NEXT: add.d $a1, $a3, $a1 | |
| ; NOUAL-NEXT: add.w $a0, $a0, $a1 | |
| ; NOUAL-NEXT: ret | |
| ; | |
| ; UAL-LABEL: four_nearby_loads: | |
| ; UAL: # %bb.0: | |
| ; UAL-NEXT: ldptr.w $a1, $a0, 7960 | |
| ; UAL-NEXT: ldptr.w $a2, $a0, 7964 | |
| ; UAL-NEXT: ldptr.w $a3, $a0, 7968 | |
| ; UAL-NEXT: ldptr.w $a0, $a0, 7972 | |
| ; UAL-NEXT: add.d $a1, $a1, $a2 | |
| ; UAL-NEXT: add.d $a0, $a3, $a0 | |
| ; UAL-NEXT: add.w $a0, $a1, $a0 | |
| ; UAL-NEXT: ret | |
| %p0 = getelementptr i8, ptr %base, i64 7960 | |
| %p1 = getelementptr i8, ptr %base, i64 7964 | |
| %p2 = getelementptr i8, ptr %base, i64 7968 | |
| %p3 = getelementptr i8, ptr %base, i64 7972 | |
| %v0 = load volatile i32, ptr %p0, align 4 | |
| %v1 = load volatile i32, ptr %p1, align 4 | |
| %v2 = load volatile i32, ptr %p2, align 4 | |
| %v3 = load volatile i32, ptr %p3, align 4 | |
| %s0 = add i32 %v0, %v1 | |
| %s1 = add i32 %v2, %v3 | |
| %sum = add i32 %s0, %s1 | |
| ret i32 %sum | |
| } |