; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32LE ; RUN: llc -mtriple=riscv32be -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32BE ; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64LE ; RUN: llc -mtriple=riscv64be -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64BE ; Test basic load/store operations on both little-endian and big-endian RISC-V define i32 @load_i32(ptr %p) { ; RV32LE-LABEL: load_i32: ; RV32LE: # %bb.0: ; RV32LE-NEXT: lw a0, 0(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: load_i32: ; RV32BE: # %bb.0: ; RV32BE-NEXT: lw a0, 0(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: load_i32: ; RV64LE: # %bb.0: ; RV64LE-NEXT: lw a0, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: load_i32: ; RV64BE: # %bb.0: ; RV64BE-NEXT: lw a0, 0(a0) ; RV64BE-NEXT: ret %v = load i32, ptr %p ret i32 %v } define void @store_i32(ptr %p, i32 %v) { ; RV32LE-LABEL: store_i32: ; RV32LE: # %bb.0: ; RV32LE-NEXT: sw a1, 0(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: store_i32: ; RV32BE: # %bb.0: ; RV32BE-NEXT: sw a1, 0(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: store_i32: ; RV64LE: # %bb.0: ; RV64LE-NEXT: sw a1, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: store_i32: ; RV64BE: # %bb.0: ; RV64BE-NEXT: sw a1, 0(a0) ; RV64BE-NEXT: ret store i32 %v, ptr %p ret void } define i16 @load_i16(ptr %p) { ; RV32LE-LABEL: load_i16: ; RV32LE: # %bb.0: ; RV32LE-NEXT: lh a0, 0(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: load_i16: ; RV32BE: # %bb.0: ; RV32BE-NEXT: lh a0, 0(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: load_i16: ; RV64LE: # %bb.0: ; RV64LE-NEXT: lh a0, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: load_i16: ; RV64BE: # %bb.0: ; RV64BE-NEXT: lh a0, 0(a0) ; RV64BE-NEXT: ret %v = load i16, ptr %p ret i16 %v } define void @store_i16(ptr %p, i16 %v) { ; RV32LE-LABEL: store_i16: ; RV32LE: # %bb.0: ; RV32LE-NEXT: sh a1, 0(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: store_i16: ; RV32BE: # %bb.0: ; RV32BE-NEXT: sh a1, 0(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: store_i16: ; RV64LE: # %bb.0: ; RV64LE-NEXT: sh a1, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: store_i16: ; RV64BE: # %bb.0: ; RV64BE-NEXT: sh a1, 0(a0) ; RV64BE-NEXT: ret store i16 %v, ptr %p ret void } define i8 @load_i8(ptr %p) { ; RV32LE-LABEL: load_i8: ; RV32LE: # %bb.0: ; RV32LE-NEXT: lbu a0, 0(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: load_i8: ; RV32BE: # %bb.0: ; RV32BE-NEXT: lbu a0, 0(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: load_i8: ; RV64LE: # %bb.0: ; RV64LE-NEXT: lbu a0, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: load_i8: ; RV64BE: # %bb.0: ; RV64BE-NEXT: lbu a0, 0(a0) ; RV64BE-NEXT: ret %v = load i8, ptr %p ret i8 %v } define void @store_i8(ptr %p, i8 %v) { ; RV32LE-LABEL: store_i8: ; RV32LE: # %bb.0: ; RV32LE-NEXT: sb a1, 0(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: store_i8: ; RV32BE: # %bb.0: ; RV32BE-NEXT: sb a1, 0(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: store_i8: ; RV64LE: # %bb.0: ; RV64LE-NEXT: sb a1, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: store_i8: ; RV64BE: # %bb.0: ; RV64BE-NEXT: sb a1, 0(a0) ; RV64BE-NEXT: ret store i8 %v, ptr %p ret void } define i64 @load_i64(ptr %p) { ; RV32LE-LABEL: load_i64: ; RV32LE: # %bb.0: ; RV32LE-NEXT: lw a2, 0(a0) ; RV32LE-NEXT: lw a1, 4(a0) ; RV32LE-NEXT: mv a0, a2 ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: load_i64: ; RV32BE: # %bb.0: ; RV32BE-NEXT: lw a2, 0(a0) ; RV32BE-NEXT: lw a1, 4(a0) ; RV32BE-NEXT: mv a0, a2 ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: load_i64: ; RV64LE: # %bb.0: ; RV64LE-NEXT: ld a0, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: load_i64: ; RV64BE: # %bb.0: ; RV64BE-NEXT: ld a0, 0(a0) ; RV64BE-NEXT: ret %v = load i64, ptr %p ret i64 %v } define void @store_i64(ptr %p, i64 %v) { ; RV32LE-LABEL: store_i64: ; RV32LE: # %bb.0: ; RV32LE-NEXT: sw a1, 0(a0) ; RV32LE-NEXT: sw a2, 4(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: store_i64: ; RV32BE: # %bb.0: ; RV32BE-NEXT: sw a1, 0(a0) ; RV32BE-NEXT: sw a2, 4(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: store_i64: ; RV64LE: # %bb.0: ; RV64LE-NEXT: sd a1, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: store_i64: ; RV64BE: # %bb.0: ; RV64BE-NEXT: sd a1, 0(a0) ; RV64BE-NEXT: ret store i64 %v, ptr %p ret void } ; Test float/double loads and stores define float @load_float(ptr %p) { ; RV32LE-LABEL: load_float: ; RV32LE: # %bb.0: ; RV32LE-NEXT: lw a0, 0(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: load_float: ; RV32BE: # %bb.0: ; RV32BE-NEXT: lw a0, 0(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: load_float: ; RV64LE: # %bb.0: ; RV64LE-NEXT: lw a0, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: load_float: ; RV64BE: # %bb.0: ; RV64BE-NEXT: lw a0, 0(a0) ; RV64BE-NEXT: ret %v = load float, ptr %p ret float %v } define void @store_float(ptr %p, float %v) { ; RV32LE-LABEL: store_float: ; RV32LE: # %bb.0: ; RV32LE-NEXT: sw a1, 0(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: store_float: ; RV32BE: # %bb.0: ; RV32BE-NEXT: sw a1, 0(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: store_float: ; RV64LE: # %bb.0: ; RV64LE-NEXT: sw a1, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: store_float: ; RV64BE: # %bb.0: ; RV64BE-NEXT: sw a1, 0(a0) ; RV64BE-NEXT: ret store float %v, ptr %p ret void } define double @load_double(ptr %p) { ; RV32LE-LABEL: load_double: ; RV32LE: # %bb.0: ; RV32LE-NEXT: lw a2, 0(a0) ; RV32LE-NEXT: lw a1, 4(a0) ; RV32LE-NEXT: mv a0, a2 ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: load_double: ; RV32BE: # %bb.0: ; RV32BE-NEXT: lw a2, 0(a0) ; RV32BE-NEXT: lw a1, 4(a0) ; RV32BE-NEXT: mv a0, a2 ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: load_double: ; RV64LE: # %bb.0: ; RV64LE-NEXT: ld a0, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: load_double: ; RV64BE: # %bb.0: ; RV64BE-NEXT: ld a0, 0(a0) ; RV64BE-NEXT: ret %v = load double, ptr %p ret double %v } define void @store_double(ptr %p, double %v) { ; RV32LE-LABEL: store_double: ; RV32LE: # %bb.0: ; RV32LE-NEXT: sw a1, 0(a0) ; RV32LE-NEXT: sw a2, 4(a0) ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: store_double: ; RV32BE: # %bb.0: ; RV32BE-NEXT: sw a1, 0(a0) ; RV32BE-NEXT: sw a2, 4(a0) ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: store_double: ; RV64LE: # %bb.0: ; RV64LE-NEXT: sd a1, 0(a0) ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: store_double: ; RV64BE: # %bb.0: ; RV64BE-NEXT: sd a1, 0(a0) ; RV64BE-NEXT: ret store double %v, ptr %p ret void } ; Test f64 argument passing and returns declare double @external_f64_func(double, double) define double @test_f64_arg_return(double %a, double %b) { ; RV32LE-LABEL: test_f64_arg_return: ; RV32LE: # %bb.0: ; RV32LE-NEXT: addi sp, sp, -16 ; RV32LE-NEXT: .cfi_def_cfa_offset 16 ; RV32LE-NEXT: sw ra, 12(sp) # 4-byte Folded Spill ; RV32LE-NEXT: .cfi_offset ra, -4 ; RV32LE-NEXT: call external_f64_func ; RV32LE-NEXT: lw ra, 12(sp) # 4-byte Folded Reload ; RV32LE-NEXT: .cfi_restore ra ; RV32LE-NEXT: addi sp, sp, 16 ; RV32LE-NEXT: .cfi_def_cfa_offset 0 ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: test_f64_arg_return: ; RV32BE: # %bb.0: ; RV32BE-NEXT: addi sp, sp, -16 ; RV32BE-NEXT: .cfi_def_cfa_offset 16 ; RV32BE-NEXT: sw ra, 12(sp) # 4-byte Folded Spill ; RV32BE-NEXT: .cfi_offset ra, -4 ; RV32BE-NEXT: call external_f64_func ; RV32BE-NEXT: lw ra, 12(sp) # 4-byte Folded Reload ; RV32BE-NEXT: .cfi_restore ra ; RV32BE-NEXT: addi sp, sp, 16 ; RV32BE-NEXT: .cfi_def_cfa_offset 0 ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: test_f64_arg_return: ; RV64LE: # %bb.0: ; RV64LE-NEXT: addi sp, sp, -16 ; RV64LE-NEXT: .cfi_def_cfa_offset 16 ; RV64LE-NEXT: sd ra, 8(sp) # 8-byte Folded Spill ; RV64LE-NEXT: .cfi_offset ra, -8 ; RV64LE-NEXT: call external_f64_func ; RV64LE-NEXT: ld ra, 8(sp) # 8-byte Folded Reload ; RV64LE-NEXT: .cfi_restore ra ; RV64LE-NEXT: addi sp, sp, 16 ; RV64LE-NEXT: .cfi_def_cfa_offset 0 ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: test_f64_arg_return: ; RV64BE: # %bb.0: ; RV64BE-NEXT: addi sp, sp, -16 ; RV64BE-NEXT: .cfi_def_cfa_offset 16 ; RV64BE-NEXT: sd ra, 8(sp) # 8-byte Folded Spill ; RV64BE-NEXT: .cfi_offset ra, -8 ; RV64BE-NEXT: call external_f64_func ; RV64BE-NEXT: ld ra, 8(sp) # 8-byte Folded Reload ; RV64BE-NEXT: .cfi_restore ra ; RV64BE-NEXT: addi sp, sp, 16 ; RV64BE-NEXT: .cfi_def_cfa_offset 0 ; RV64BE-NEXT: ret %result = call double @external_f64_func(double %a, double %b) ret double %result } ; Test bitcast from f64 to i64 define i64 @bitcast_f64_to_i64(double %x) { ; RV32LE-LABEL: bitcast_f64_to_i64: ; RV32LE: # %bb.0: ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: bitcast_f64_to_i64: ; RV32BE: # %bb.0: ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: bitcast_f64_to_i64: ; RV64LE: # %bb.0: ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: bitcast_f64_to_i64: ; RV64BE: # %bb.0: ; RV64BE-NEXT: ret %y = bitcast double %x to i64 ret i64 %y } ; Test bitcast from i64 to f64 define double @bitcast_i64_to_f64(i64 %x) { ; RV32LE-LABEL: bitcast_i64_to_f64: ; RV32LE: # %bb.0: ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: bitcast_i64_to_f64: ; RV32BE: # %bb.0: ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: bitcast_i64_to_f64: ; RV64LE: # %bb.0: ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: bitcast_i64_to_f64: ; RV64BE: # %bb.0: ; RV64BE-NEXT: ret %y = bitcast i64 %x to double ret double %y } ; Test i64 return value register order (a0=low/a1=high for LE, a0=high/a1=low for BE) define i64 @return_i64_const() { ; RV32LE-LABEL: return_i64_const: ; RV32LE: # %bb.0: ; RV32LE-NEXT: li a0, 1 ; RV32LE-NEXT: li a1, 0 ; RV32LE-NEXT: ret ; ; RV32BE-LABEL: return_i64_const: ; RV32BE: # %bb.0: ; RV32BE-NEXT: li a1, 1 ; RV32BE-NEXT: li a0, 0 ; RV32BE-NEXT: ret ; ; RV64LE-LABEL: return_i64_const: ; RV64LE: # %bb.0: ; RV64LE-NEXT: li a0, 1 ; RV64LE-NEXT: ret ; ; RV64BE-LABEL: return_i64_const: ; RV64BE: # %bb.0: ; RV64BE-NEXT: li a0, 1 ; RV64BE-NEXT: ret ret i64 1 }