Compiler projects using llvm
; RUN: llc --mtriple=loongarch32 --mattr=+d --relocation-model=static < %s | FileCheck %s --check-prefixes=ALL,LA32NOPIC,LA32
; RUN: llc --mtriple=loongarch32 --mattr=+d --relocation-model=pic < %s | FileCheck %s --check-prefixes=ALL,LA32PIC,LA32
; RUN: llc --mtriple=loongarch64 --mattr=+d --relocation-model=static < %s | FileCheck %s --check-prefixes=ALL,LA64NOPIC,LA64
; RUN: llc --mtriple=loongarch64 --mattr=+d --relocation-model=pic < %s | FileCheck %s --check-prefixes=ALL,LA64PIC,LA64

;; Check load from and store to global variables.
@G = dso_local global i32 zeroinitializer, align 4
@arr = dso_local global [10 x i32] zeroinitializer, align 4

define i32 @load_store_global() nounwind {
; ALL-LABEL:      load_store_global:
; ALL:            # %bb.0:

; LA32NOPIC-NEXT:   pcalau12i $a0, G
; LA32NOPIC-NEXT:   addi.w $a1, $a0, G
; LA32PIC-NEXT:     pcalau12i $a0, .LG$local
; LA32PIC-NEXT:     addi.w $a1, $a0, .LG$local
; LA32-NEXT:        ld.w $a0, $a1, 0
; LA32-NEXT:        addi.w $a0, $a0, 1
; LA32-NEXT:        st.w $a0, $a1, 0

; LA64NOPIC-NEXT:   pcalau12i $a0, G
; LA64NOPIC-NEXT:   addi.d $a1, $a0, G
; LA64PIC-NEXT:     pcalau12i $a0, .LG$local
; LA64PIC-NEXT:     addi.d $a1, $a0, .LG$local
; LA64-NEXT:        ld.w $a0, $a1, 0
; LA64-NEXT:        addi.d $a0, $a0, 1
; LA64-NEXT:        st.w $a0, $a1, 0

; ALL-NEXT:         jirl $zero, $ra, 0

  %v = load i32, ptr @G
  %sum = add i32 %v, 1
  store i32 %sum, ptr @G
  ret i32 %sum
}

define i32 @load_store_global_array(i32 %a) nounwind {
; ALL-LABEL: load_store_global_array:
; ALL:       # %bb.0:

; LA32NOPIC-NEXT:   pcalau12i $a1, arr
; LA32NOPIC-NEXT:   addi.w $a2, $a1, arr
; LA32PIC-NEXT:     pcalau12i $a1, .Larr$local
; LA32PIC-NEXT:     addi.w $a2, $a1, .Larr$local
; LA32-NEXT:        ld.w $a1, $a2, 0
; LA32-NEXT:        st.w $a0, $a2, 0
; LA32NOPIC-NEXT:   ld.w $a3, $a2, 0
; LA32NOPIC-NEXT:   st.w $a0, $a2, 0
; LA32PIC-NEXT:     ld.w $a3, $a2, 36
; LA32PIC-NEXT:     st.w $a0, $a2, 36

; LA64NOPIC-NEXT:   pcalau12i $a1, arr
; LA64NOPIC-NEXT:   addi.d $a2, $a1, arr
; LA64PIC-NEXT:     pcalau12i $a1, .Larr$local
; LA64PIC-NEXT:     addi.d $a2, $a1, .Larr$local
; LA64-NEXT:        ld.w $a1, $a2, 0
; LA64-NEXT:        st.w $a0, $a2, 0
; LA64NOPIC-NEXT:   ld.w $a3, $a2, 0
; LA64NOPIC-NEXT:   st.w $a0, $a2, 0
; LA64PIC-NEXT:     ld.w $a3, $a2, 36
; LA64PIC-NEXT:     st.w $a0, $a2, 36

; ALL-NEXT:         move $a0, $a1
; ALL-NEXT:         jirl $zero, $ra, 0

  %1 = load volatile i32, ptr @arr, align 4
  store i32 %a, ptr @arr, align 4
  %2 = getelementptr [10 x i32], ptr @arr, i32 0, i32 9
  %3 = load volatile i32, ptr %2, align 4
  store i32 %a, ptr %2, align 4
  ret i32 %1
}

;; Check indexed and unindexed, sext, zext and anyext loads.

define i64 @ld_b(ptr %a) nounwind {
; LA32-LABEL: ld_b:
; LA32:       # %bb.0:
; LA32-NEXT:    ld.b $a1, $a0, 0
; LA32-NEXT:    ld.b $a0, $a0, 1
; LA32-NEXT:    srai.w $a1, $a0, 31
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: ld_b:
; LA64:       # %bb.0:
; LA64-NEXT:    ld.b $a1, $a0, 0
; LA64-NEXT:    ld.b $a0, $a0, 1
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr i8, ptr %a, i64 1
  %2 = load i8, ptr %1
  %3 = sext i8 %2 to i64
  %4 = load volatile i8, ptr %a
  ret i64 %3
}

define i64 @ld_h(ptr %a) nounwind {
; LA32-LABEL: ld_h:
; LA32:       # %bb.0:
; LA32-NEXT:    ld.h $a1, $a0, 0
; LA32-NEXT:    ld.h $a0, $a0, 4
; LA32-NEXT:    srai.w $a1, $a0, 31
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: ld_h:
; LA64:       # %bb.0:
; LA64-NEXT:    ld.h $a1, $a0, 0
; LA64-NEXT:    ld.h $a0, $a0, 4
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr i16, ptr %a, i64 2
  %2 = load i16, ptr %1
  %3 = sext i16 %2 to i64
  %4 = load volatile i16, ptr %a
  ret i64 %3
}

define i64 @ld_w(ptr %a) nounwind {
; LA32-LABEL: ld_w:
; LA32:       # %bb.0:
; LA32-NEXT:    ld.w $a1, $a0, 0
; LA32-NEXT:    ld.w $a0, $a0, 12
; LA32-NEXT:    srai.w $a1, $a0, 31
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: ld_w:
; LA64:       # %bb.0:
; LA64-NEXT:    ld.w $a1, $a0, 0
; LA64-NEXT:    ld.w $a0, $a0, 12
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr i32, ptr %a, i64 3
  %2 = load i32, ptr %1
  %3 = sext i32 %2 to i64
  %4 = load volatile i32, ptr %a
  ret i64 %3
}

define i64 @ld_d(ptr %a) nounwind {
; LA32-LABEL: ld_d:
; LA32:       # %bb.0:
; LA32-NEXT:    ld.w $a1, $a0, 4
; LA32-NEXT:    ld.w $a1, $a0, 0
; LA32-NEXT:    ld.w $a1, $a0, 28
; LA32-NEXT:    ld.w $a0, $a0, 24
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: ld_d:
; LA64:       # %bb.0:
; LA64-NEXT:    ld.d $a1, $a0, 0
; LA64-NEXT:    ld.d $a0, $a0, 24
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr i64, ptr %a, i64 3
  %2 = load i64, ptr %1
  %3 = load volatile i64, ptr %a
  ret i64 %2
}

define i64 @ld_bu(ptr %a) nounwind {
; LA32-LABEL: ld_bu:
; LA32:       # %bb.0:
; LA32-NEXT:    ld.bu $a1, $a0, 0
; LA32-NEXT:    ld.bu $a2, $a0, 4
; LA32-NEXT:    add.w $a0, $a2, $a1
; LA32-NEXT:    sltu $a1, $a0, $a2
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: ld_bu:
; LA64:       # %bb.0:
; LA64-NEXT:    ld.bu $a1, $a0, 0
; LA64-NEXT:    ld.bu $a0, $a0, 4
; LA64-NEXT:    add.d $a0, $a0, $a1
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr i8, ptr %a, i64 4
  %2 = load i8, ptr %1
  %3 = zext i8 %2 to i64
  %4 = load volatile i8, ptr %a
  %5 = zext i8 %4 to i64
  %6 = add i64 %3, %5
  ret i64 %6
}

define i64 @ld_hu(ptr %a) nounwind {
; LA32-LABEL: ld_hu:
; LA32:       # %bb.0:
; LA32-NEXT:    ld.hu $a1, $a0, 0
; LA32-NEXT:    ld.hu $a2, $a0, 10
; LA32-NEXT:    add.w $a0, $a2, $a1
; LA32-NEXT:    sltu $a1, $a0, $a2
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: ld_hu:
; LA64:       # %bb.0:
; LA64-NEXT:    ld.hu $a1, $a0, 0
; LA64-NEXT:    ld.hu $a0, $a0, 10
; LA64-NEXT:    add.d $a0, $a0, $a1
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr i16, ptr %a, i64 5
  %2 = load i16, ptr %1
  %3 = zext i16 %2 to i64
  %4 = load volatile i16, ptr %a
  %5 = zext i16 %4 to i64
  %6 = add i64 %3, %5
  ret i64 %6
}

define i64 @ld_wu(ptr %a) nounwind {
; LA32-LABEL: ld_wu:
; LA32:       # %bb.0:
; LA32-NEXT:    ld.w $a1, $a0, 0
; LA32-NEXT:    ld.w $a2, $a0, 20
; LA32-NEXT:    add.w $a0, $a2, $a1
; LA32-NEXT:    sltu $a1, $a0, $a2
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: ld_wu:
; LA64:       # %bb.0:
; LA64-NEXT:    ld.wu $a1, $a0, 0
; LA64-NEXT:    ld.wu $a0, $a0, 20
; LA64-NEXT:    add.d $a0, $a0, $a1
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr i32, ptr %a, i64 5
  %2 = load i32, ptr %1
  %3 = zext i32 %2 to i64
  %4 = load volatile i32, ptr %a
  %5 = zext i32 %4 to i64
  %6 = add i64 %3, %5
  ret i64 %6
}

;; Check indexed and unindexed stores.

define void @st_b(ptr %a, i8 %b) nounwind {
; ALL-LABEL: st_b:
; ALL:       # %bb.0:
; ALL-NEXT:    st.b $a1, $a0, 6
; ALL-NEXT:    st.b $a1, $a0, 0
; ALL-NEXT:    jirl $zero, $ra, 0
  store i8 %b, ptr %a
  %1 = getelementptr i8, ptr %a, i64 6
  store i8 %b, ptr %1
  ret void
}

define void @st_h(ptr %a, i16 %b) nounwind {
; ALL-LABEL: st_h:
; ALL:       # %bb.0:
; ALL-NEXT:    st.h $a1, $a0, 14
; ALL-NEXT:    st.h $a1, $a0, 0
; ALL-NEXT:    jirl $zero, $ra, 0
  store i16 %b, ptr %a
  %1 = getelementptr i16, ptr %a, i64 7
  store i16 %b, ptr %1
  ret void
}

define void @st_w(ptr %a, i32 %b) nounwind {
; ALL-LABEL: st_w:
; ALL:       # %bb.0:
; ALL-NEXT:    st.w $a1, $a0, 28
; ALL-NEXT:    st.w $a1, $a0, 0
; ALL-NEXT:    jirl $zero, $ra, 0
  store i32 %b, ptr %a
  %1 = getelementptr i32, ptr %a, i64 7
  store i32 %b, ptr %1
  ret void
}

define void @st_d(ptr %a, i64 %b) nounwind {
; LA32-LABEL: st_d:
; LA32:       # %bb.0:
; LA32-NEXT:    st.w $a2, $a0, 68
; LA32-NEXT:    st.w $a2, $a0, 4
; LA32-NEXT:    st.w $a1, $a0, 64
; LA32-NEXT:    st.w $a1, $a0, 0
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: st_d:
; LA64:       # %bb.0:
; LA64-NEXT:    st.d $a1, $a0, 64
; LA64-NEXT:    st.d $a1, $a0, 0
; LA64-NEXT:    jirl $zero, $ra, 0
  store i64 %b, ptr %a
  %1 = getelementptr i64, ptr %a, i64 8
  store i64 %b, ptr %1
  ret void
}

;; Check load from and store to an i1 location.
define i64 @load_sext_zext_anyext_i1(ptr %a) nounwind {
  ;; sextload i1
; LA32-LABEL: load_sext_zext_anyext_i1:
; LA32:       # %bb.0:
; LA32-NEXT:    ld.b $a1, $a0, 0
; LA32-NEXT:    ld.bu $a1, $a0, 1
; LA32-NEXT:    ld.bu $a2, $a0, 2
; LA32-NEXT:    sub.w $a0, $a2, $a1
; LA32-NEXT:    sltu $a1, $a2, $a1
; LA32-NEXT:    sub.w $a1, $zero, $a1
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: load_sext_zext_anyext_i1:
; LA64:       # %bb.0:
; LA64-NEXT:    ld.b $a1, $a0, 0
; LA64-NEXT:    ld.bu $a1, $a0, 1
; LA64-NEXT:    ld.bu $a0, $a0, 2
; LA64-NEXT:    sub.d $a0, $a0, $a1
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr i1, ptr %a, i64 1
  %2 = load i1, ptr %1
  %3 = sext i1 %2 to i64
  ;; zextload i1
  %4 = getelementptr i1, ptr %a, i64 2
  %5 = load i1, ptr %4
  %6 = zext i1 %5 to i64
  %7 = add i64 %3, %6
  ;; extload i1 (anyext). Produced as the load is unused.
  %8 = load volatile i1, ptr %a
  ret i64 %7
}

define i16 @load_sext_zext_anyext_i1_i16(ptr %a) nounwind {
  ;; sextload i1
; LA32-LABEL: load_sext_zext_anyext_i1_i16:
; LA32:       # %bb.0:
; LA32-NEXT:    ld.b $a1, $a0, 0
; LA32-NEXT:    ld.bu $a1, $a0, 1
; LA32-NEXT:    ld.bu $a0, $a0, 2
; LA32-NEXT:    sub.w $a0, $a0, $a1
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: load_sext_zext_anyext_i1_i16:
; LA64:       # %bb.0:
; LA64-NEXT:    ld.b $a1, $a0, 0
; LA64-NEXT:    ld.bu $a1, $a0, 1
; LA64-NEXT:    ld.bu $a0, $a0, 2
; LA64-NEXT:    sub.d $a0, $a0, $a1
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr i1, ptr %a, i64 1
  %2 = load i1, ptr %1
  %3 = sext i1 %2 to i16
  ;; zextload i1
  %4 = getelementptr i1, ptr %a, i64 2
  %5 = load i1, ptr %4
  %6 = zext i1 %5 to i16
  %7 = add i16 %3, %6
  ;; extload i1 (anyext). Produced as the load is unused.
  %8 = load volatile i1, ptr %a
  ret i16 %7
}

define i64 @ld_sd_constant(i64 %a) nounwind {
; LA32-LABEL: ld_sd_constant:
; LA32:       # %bb.0:
; LA32-NEXT:    lu12i.w $a3, -136485
; LA32-NEXT:    ori $a4, $a3, 3823
; LA32-NEXT:    ld.w $a2, $a4, 0
; LA32-NEXT:    st.w $a0, $a4, 0
; LA32-NEXT:    ori $a0, $a3, 3827
; LA32-NEXT:    ld.w $a3, $a0, 0
; LA32-NEXT:    st.w $a1, $a0, 0
; LA32-NEXT:    move $a0, $a2
; LA32-NEXT:    move $a1, $a3
; LA32-NEXT:    jirl $zero, $ra, 0
;
; LA64-LABEL: ld_sd_constant:
; LA64:       # %bb.0:
; LA64-NEXT:    lu12i.w $a1, -136485
; LA64-NEXT:    ori $a1, $a1, 3823
; LA64-NEXT:    lu32i.d $a1, -147729
; LA64-NEXT:    lu52i.d $a2, $a1, -534
; LA64-NEXT:    ld.d $a1, $a2, 0
; LA64-NEXT:    st.d $a0, $a2, 0
; LA64-NEXT:    move $a0, $a1
; LA64-NEXT:    jirl $zero, $ra, 0
  %1 = inttoptr i64 16045690984833335023 to ptr
  %2 = load volatile i64, ptr %1
  store i64 %a, ptr %1
  ret i64 %2
}

;; Check load from and store to a float location.
define float @load_store_float(ptr %a, float %b) nounwind {
; ALL-LABEL: load_store_float:
; ALL:       # %bb.0:
; ALL-NEXT:    fld.s $fa1, $a0, 4
; ALL-NEXT:    fst.s $fa0, $a0, 4
; ALL-NEXT:    fmov.s $fa0, $fa1
; ALL-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr float, ptr %a, i64 1
  %2 = load float, ptr %1
  store float %b, ptr %1
  ret float %2
}

;; Check load from and store to a double location.
define double @load_store_double(ptr %a, double %b) nounwind {
; ALL-LABEL: load_store_double:
; ALL:       # %bb.0:
; ALL-NEXT:    fld.d $fa1, $a0, 8
; ALL-NEXT:    fst.d $fa0, $a0, 8
; ALL-NEXT:    fmov.d $fa0, $fa1
; ALL-NEXT:    jirl $zero, $ra, 0
  %1 = getelementptr double, ptr %a, i64 1
  %2 = load double, ptr %1
  store double %b, ptr %1
  ret double %2
}