diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h @@ -162,6 +162,8 @@ SDValue lowerUINT_TO_FP(SDValue Op, SelectionDAG &DAG) const; SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const; SDValue lowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; bool isFPImmLegal(const APFloat &Imm, EVT VT, bool ForCodeSize) const override; diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp @@ -222,10 +222,60 @@ return lowerUINT_TO_FP(Op, DAG); case ISD::VASTART: return lowerVASTART(Op, DAG); + case ISD::FRAMEADDR: + return lowerFRAMEADDR(Op, DAG); + case ISD::RETURNADDR: + return lowerRETURNADDR(Op, DAG); } return SDValue(); } +SDValue LoongArchTargetLowering::lowerFRAMEADDR(SDValue Op, + SelectionDAG &DAG) const { + if (!isa(Op.getOperand(0))) { + DAG.getContext()->emitError("argument to '__builtin_frame_address' must " + "be a constant integer"); + return SDValue(); + } + + // Currently only support lowering frame address for current frame. + unsigned Depth = cast(Op.getOperand(0))->getZExtValue(); + assert((Depth == 0) && + "Frame address can only be determined for current frame."); + if (Depth != 0) + return SDValue(); + + MachineFunction &MF = DAG.getMachineFunction(); + MF.getFrameInfo().setFrameAddressIsTaken(true); + + return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), + Subtarget.getRegisterInfo()->getFrameRegister(MF), + Op.getValueType()); +} + +SDValue LoongArchTargetLowering::lowerRETURNADDR(SDValue Op, + SelectionDAG &DAG) const { + if (verifyReturnAddressArgumentIsConstant(Op, DAG)) + return SDValue(); + + // Currently only support lowering return address for current frame. + unsigned Depth = cast(Op.getOperand(0))->getZExtValue(); + assert((Depth == 0) && + "Return address can only be determined for current frame."); + if (Depth != 0) + return SDValue(); + + MachineFunction &MF = DAG.getMachineFunction(); + MF.getFrameInfo().setReturnAddressIsTaken(true); + MVT GRLenVT = Subtarget.getGRLenVT(); + + // Return the value of the return address register, marking it an implicit + // live-in. + Register Reg = MF.addLiveIn(Subtarget.getRegisterInfo()->getRARegister(), + getRegClassFor(GRLenVT)); + return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), Reg, GRLenVT); +} + SDValue LoongArchTargetLowering::lowerEH_DWARF_CFA(SDValue Op, SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); diff --git a/llvm/test/CodeGen/LoongArch/frameaddr-returnaddr-error.ll b/llvm/test/CodeGen/LoongArch/frameaddr-returnaddr-error.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/frameaddr-returnaddr-error.ll @@ -0,0 +1,18 @@ +; RUN: not llc --mtriple=loongarch64 --disable-verify < %s 2>&1 | FileCheck %s + +declare i8* @llvm.frameaddress(i32) +declare i8* @llvm.returnaddress(i32) + +define i8* @test_frameaddress_0(i32 %x) nounwind { +; CHECK: argument to '__builtin_frame_address' must be a constant integer + %1 = call i8* @llvm.frameaddress(i32 %x) + ret i8* %1 +} + + +define i8* @test_returnaddress_0(i32 %x) nounwind { +; CHECK: argument to '__builtin_return_address' must be a constant integer + %1 = call i8* @llvm.returnaddress(i32 %x) + ret i8* %1 +} + diff --git a/llvm/test/CodeGen/LoongArch/frameaddr-returnaddr.ll b/llvm/test/CodeGen/LoongArch/frameaddr-returnaddr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/frameaddr-returnaddr.ll @@ -0,0 +1,84 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc --mtriple=loongarch32 < %s | FileCheck %s --check-prefix=LA32 +; RUN: llc --mtriple=loongarch64 < %s | FileCheck %s --check-prefix=LA64 + +declare i8* @llvm.frameaddress(i32) +declare i8* @llvm.returnaddress(i32) + +define i8* @test_frameaddress_0() nounwind { +; LA32-LABEL: test_frameaddress_0: +; LA32: # %bb.0: +; LA32-NEXT: addi.w $sp, $sp, -16 +; LA32-NEXT: st.w $ra, $sp, 12 # 4-byte Folded Spill +; LA32-NEXT: st.w $fp, $sp, 8 # 4-byte Folded Spill +; LA32-NEXT: addi.w $fp, $sp, 16 +; LA32-NEXT: move $a0, $fp +; LA32-NEXT: ld.w $fp, $sp, 8 # 4-byte Folded Reload +; LA32-NEXT: ld.w $ra, $sp, 12 # 4-byte Folded Reload +; LA32-NEXT: addi.w $sp, $sp, 16 +; LA32-NEXT: ret +; +; LA64-LABEL: test_frameaddress_0: +; LA64: # %bb.0: +; LA64-NEXT: addi.d $sp, $sp, -16 +; LA64-NEXT: st.d $ra, $sp, 8 # 8-byte Folded Spill +; LA64-NEXT: st.d $fp, $sp, 0 # 8-byte Folded Spill +; LA64-NEXT: addi.d $fp, $sp, 16 +; LA64-NEXT: move $a0, $fp +; LA64-NEXT: ld.d $fp, $sp, 0 # 8-byte Folded Reload +; LA64-NEXT: ld.d $ra, $sp, 8 # 8-byte Folded Reload +; LA64-NEXT: addi.d $sp, $sp, 16 +; LA64-NEXT: ret + %1 = call i8* @llvm.frameaddress(i32 0) + ret i8* %1 +} + +;; The ‘llvm.frameaddress’ intrinsic either returns a pointer indicating +;; the frame address of the specified call frame, +;; or zero if it cannot be identified. +;; Reference: https://llvm.org/docs/LangRef.html#llvm-frameaddress-intrinsic +define i8* @test_frameaddress_2() nounwind { +; LA32-LABEL: test_frameaddress_2: +; LA32: # %bb.0: +; LA32-NEXT: move $a0, $zero +; LA32-NEXT: ret +; +; LA64-LABEL: test_frameaddress_2: +; LA64: # %bb.0: +; LA64-NEXT: move $a0, $zero +; LA64-NEXT: ret + %1 = call i8* @llvm.frameaddress(i32 2) + ret i8* %1 +} + +define i8* @test_returnaddress_0() nounwind { +; LA32-LABEL: test_returnaddress_0: +; LA32: # %bb.0: +; LA32-NEXT: move $a0, $ra +; LA32-NEXT: ret +; +; LA64-LABEL: test_returnaddress_0: +; LA64: # %bb.0: +; LA64-NEXT: move $a0, $ra +; LA64-NEXT: ret + %1 = call i8* @llvm.returnaddress(i32 0) + ret i8* %1 +} + +;; The ‘llvm.returnaddress’ intrinsic either returns a pointer indicating +;; the return address of the specified call frame, +;; or zero if it cannot be identified. +;; Reference: https://llvm.org/docs/LangRef.html#llvm-returnaddress-intrinsic +define i8* @test_returnaddress_2() nounwind { +; LA32-LABEL: test_returnaddress_2: +; LA32: # %bb.0: +; LA32-NEXT: move $a0, $zero +; LA32-NEXT: ret +; +; LA64-LABEL: test_returnaddress_2: +; LA64: # %bb.0: +; LA64-NEXT: move $a0, $zero +; LA64-NEXT: ret + %1 = call i8* @llvm.returnaddress(i32 2) + ret i8* %1 +}