| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: llc -mtriple=loongarch64 -mattr=+ual -O2 -verify-machineinstrs < %s | FileCheck %s | |
| ; RUN: llc -mtriple=loongarch64 -mattr=+ual -O2 -verify-machineinstrs < %s | llvm-mc -triple=loongarch64 -filetype=obj -o %t.o | |
| ; RUN: llc -mtriple=loongarch64 -mattr=+ual -O0 -verify-machineinstrs < %s | llvm-mc -triple=loongarch64 -filetype=obj -o %t.o | |
| ; RUN: llc -mtriple=loongarch64 -mattr=+ual -O1 -verify-machineinstrs < %s | llvm-mc -triple=loongarch64 -filetype=obj -o %t.o | |
| ; RUN: llc -mtriple=loongarch64 -mattr=+ual -O3 -verify-machineinstrs < %s | llvm-mc -triple=loongarch64 -filetype=obj -o %t.o | |
| ; RUN: llc -mtriple=loongarch64 -mattr=-ual -O0 -verify-machineinstrs < %s | llvm-mc -triple=loongarch64 -filetype=obj -o %t.o | |
| ; RUN: llc -mtriple=loongarch64 -mattr=-ual -O1 -verify-machineinstrs < %s | llvm-mc -triple=loongarch64 -filetype=obj -o %t.o | |
| ; RUN: llc -mtriple=loongarch64 -mattr=-ual -O2 -verify-machineinstrs < %s | llvm-mc -triple=loongarch64 -filetype=obj -o %t.o | |
| ; RUN: llc -mtriple=loongarch64 -mattr=-ual -O3 -verify-machineinstrs < %s | llvm-mc -triple=loongarch64 -filetype=obj -o %t.o | |
| ; | |
| ; The displacement 2048 fits si14 << 2 during instruction selection. The | |
| ; byte-aligned stack object makes the final FP-relative displacement -977, | |
| ; which is not a multiple of four. Frame index elimination must fall back | |
| ; to the corresponding unscaled load/store without changing the address. | |
| ; | |
| ; Keep the assembly round-trip: MachineVerifier alone does not diagnose | |
| ; every target-specific immediate constraint, and an MC encoder must not | |
| ; silently discard the low two offset bits. | |
| ; Reviewer reproducer: the i32 load is promoted to an any-extending load. | |
| define i32 @anyext_frame_offset() "frame-pointer"="all" nounwind { | |
| ; CHECK-LABEL: anyext_frame_offset: | |
| ; CHECK: # %bb.0: # %entry | |
| ; CHECK-NEXT: addi.d $sp, $sp, -2032 | |
| ; CHECK-NEXT: st.d $ra, $sp, 2024 # 8-byte Folded Spill | |
| ; CHECK-NEXT: st.d $fp, $sp, 2016 # 8-byte Folded Spill | |
| ; CHECK-NEXT: addi.d $fp, $sp, 2032 | |
| ; CHECK-NEXT: addi.d $sp, $sp, -1008 | |
| ; CHECK-NEXT: lu12i.w $a0, -1 | |
| ; CHECK-NEXT: ori $a0, $a0, 1071 | |
| ; CHECK-NEXT: add.d $a0, $fp, $a0 | |
| ; CHECK-NEXT: pcaddu18i $ra, %call36(use) | |
| ; CHECK-NEXT: jirl $ra, $ra, 0 | |
| ; CHECK-NEXT: ld.w $a0, $fp, -977 | |
| ; CHECK-NEXT: addi.d $sp, $sp, 1008 | |
| ; CHECK-NEXT: ld.d $fp, $sp, 2016 # 8-byte Folded Reload | |
| ; CHECK-NEXT: ld.d $ra, $sp, 2024 # 8-byte Folded Reload | |
| ; CHECK-NEXT: addi.d $sp, $sp, 2032 | |
| ; CHECK-NEXT: ret | |
| entry: | |
| %a = alloca [3001 x i8], align 1 | |
| call void @use(ptr %a) | |
| %p = getelementptr i8, ptr %a, i64 2048 | |
| %v = load i32, ptr %p, align 1 | |
| ret i32 %v | |
| } | |
| ; The pre-existing sign-extending pattern needs the same final-offset check. | |
| define i64 @sext_frame_offset() "frame-pointer"="all" nounwind { | |
| ; CHECK-LABEL: sext_frame_offset: | |
| ; CHECK: # %bb.0: # %entry | |
| ; CHECK-NEXT: addi.d $sp, $sp, -2032 | |
| ; CHECK-NEXT: st.d $ra, $sp, 2024 # 8-byte Folded Spill | |
| ; CHECK-NEXT: st.d $fp, $sp, 2016 # 8-byte Folded Spill | |
| ; CHECK-NEXT: addi.d $fp, $sp, 2032 | |
| ; CHECK-NEXT: addi.d $sp, $sp, -1008 | |
| ; CHECK-NEXT: lu12i.w $a0, -1 | |
| ; CHECK-NEXT: ori $a0, $a0, 1071 | |
| ; CHECK-NEXT: add.d $a0, $fp, $a0 | |
| ; CHECK-NEXT: pcaddu18i $ra, %call36(use) | |
| ; CHECK-NEXT: jirl $ra, $ra, 0 | |
| ; CHECK-NEXT: ld.w $a0, $fp, -977 | |
| ; CHECK-NEXT: addi.d $sp, $sp, 1008 | |
| ; CHECK-NEXT: ld.d $fp, $sp, 2016 # 8-byte Folded Reload | |
| ; CHECK-NEXT: ld.d $ra, $sp, 2024 # 8-byte Folded Reload | |
| ; CHECK-NEXT: addi.d $sp, $sp, 2032 | |
| ; CHECK-NEXT: ret | |
| entry: | |
| %a = alloca [3001 x i8], align 1 | |
| call void @use(ptr %a) | |
| %p = getelementptr i8, ptr %a, i64 2048 | |
| %v = load i32, ptr %p, align 1 | |
| %ext = sext i32 %v to i64 | |
| ret i64 %ext | |
| } | |
| define i64 @load64_frame_offset() "frame-pointer"="all" nounwind { | |
| ; CHECK-LABEL: load64_frame_offset: | |
| ; CHECK: # %bb.0: # %entry | |
| ; CHECK-NEXT: addi.d $sp, $sp, -2032 | |
| ; CHECK-NEXT: st.d $ra, $sp, 2024 # 8-byte Folded Spill | |
| ; CHECK-NEXT: st.d $fp, $sp, 2016 # 8-byte Folded Spill | |
| ; CHECK-NEXT: addi.d $fp, $sp, 2032 | |
| ; CHECK-NEXT: addi.d $sp, $sp, -1008 | |
| ; CHECK-NEXT: lu12i.w $a0, -1 | |
| ; CHECK-NEXT: ori $a0, $a0, 1071 | |
| ; CHECK-NEXT: add.d $a0, $fp, $a0 | |
| ; CHECK-NEXT: pcaddu18i $ra, %call36(use) | |
| ; CHECK-NEXT: jirl $ra, $ra, 0 | |
| ; CHECK-NEXT: ld.d $a0, $fp, -977 | |
| ; CHECK-NEXT: addi.d $sp, $sp, 1008 | |
| ; CHECK-NEXT: ld.d $fp, $sp, 2016 # 8-byte Folded Reload | |
| ; CHECK-NEXT: ld.d $ra, $sp, 2024 # 8-byte Folded Reload | |
| ; CHECK-NEXT: addi.d $sp, $sp, 2032 | |
| ; CHECK-NEXT: ret | |
| entry: | |
| %a = alloca [3001 x i8], align 1 | |
| call void @use(ptr %a) | |
| %p = getelementptr i8, ptr %a, i64 2048 | |
| %v = load i64, ptr %p, align 1 | |
| ret i64 %v | |
| } | |
| ; Use a constant stored value so no extra callee-saved register changes | |
| ; the stack layout. Volatile keeps the store to the local object alive. | |
| define void @store32_frame_offset() "frame-pointer"="all" nounwind { | |
| ; CHECK-LABEL: store32_frame_offset: | |
| ; CHECK: # %bb.0: # %entry | |
| ; CHECK-NEXT: addi.d $sp, $sp, -2032 | |
| ; CHECK-NEXT: st.d $ra, $sp, 2024 # 8-byte Folded Spill | |
| ; CHECK-NEXT: st.d $fp, $sp, 2016 # 8-byte Folded Spill | |
| ; CHECK-NEXT: addi.d $fp, $sp, 2032 | |
| ; CHECK-NEXT: addi.d $sp, $sp, -1008 | |
| ; CHECK-NEXT: lu12i.w $a0, -1 | |
| ; CHECK-NEXT: ori $a0, $a0, 1071 | |
| ; CHECK-NEXT: add.d $a0, $fp, $a0 | |
| ; CHECK-NEXT: pcaddu18i $ra, %call36(use) | |
| ; CHECK-NEXT: jirl $ra, $ra, 0 | |
| ; CHECK-NEXT: st.w $zero, $fp, -977 | |
| ; CHECK-NEXT: addi.d $sp, $sp, 1008 | |
| ; CHECK-NEXT: ld.d $fp, $sp, 2016 # 8-byte Folded Reload | |
| ; CHECK-NEXT: ld.d $ra, $sp, 2024 # 8-byte Folded Reload | |
| ; CHECK-NEXT: addi.d $sp, $sp, 2032 | |
| ; CHECK-NEXT: ret | |
| entry: | |
| %a = alloca [3001 x i8], align 1 | |
| call void @use(ptr %a) | |
| %p = getelementptr i8, ptr %a, i64 2048 | |
| store volatile i32 0, ptr %p, align 1 | |
| ret void | |
| } | |
| define void @store64_frame_offset() "frame-pointer"="all" nounwind { | |
| ; CHECK-LABEL: store64_frame_offset: | |
| ; CHECK: # %bb.0: # %entry | |
| ; CHECK-NEXT: addi.d $sp, $sp, -2032 | |
| ; CHECK-NEXT: st.d $ra, $sp, 2024 # 8-byte Folded Spill | |
| ; CHECK-NEXT: st.d $fp, $sp, 2016 # 8-byte Folded Spill | |
| ; CHECK-NEXT: addi.d $fp, $sp, 2032 | |
| ; CHECK-NEXT: addi.d $sp, $sp, -1008 | |
| ; CHECK-NEXT: lu12i.w $a0, -1 | |
| ; CHECK-NEXT: ori $a0, $a0, 1071 | |
| ; CHECK-NEXT: add.d $a0, $fp, $a0 | |
| ; CHECK-NEXT: pcaddu18i $ra, %call36(use) | |
| ; CHECK-NEXT: jirl $ra, $ra, 0 | |
| ; CHECK-NEXT: st.d $zero, $fp, -977 | |
| ; CHECK-NEXT: addi.d $sp, $sp, 1008 | |
| ; CHECK-NEXT: ld.d $fp, $sp, 2016 # 8-byte Folded Reload | |
| ; CHECK-NEXT: ld.d $ra, $sp, 2024 # 8-byte Folded Reload | |
| ; CHECK-NEXT: addi.d $sp, $sp, 2032 | |
| ; CHECK-NEXT: ret | |
| entry: | |
| %a = alloca [3001 x i8], align 1 | |
| call void @use(ptr %a) | |
| %p = getelementptr i8, ptr %a, i64 2048 | |
| store volatile i64 0, ptr %p, align 1 | |
| ret void | |
| } | |
| declare void @use(ptr) |