diff --git a/llvm/lib/Target/ARM/ARMSelectionDAGInfo.h b/llvm/lib/Target/ARM/ARMSelectionDAGInfo.h --- a/llvm/lib/Target/ARM/ARMSelectionDAGInfo.h +++ b/llvm/lib/Target/ARM/ARMSelectionDAGInfo.h @@ -61,6 +61,17 @@ SDValue Chain, SDValue Dst, SDValue Src, SDValue Size, unsigned Align, RTLIB::Libcall LC) const; + + SDValue emitTargetCodeForGetFPEnv(SelectionDAG &DAG, const SDLoc &DL, + SDValue Chain, SDValue Addr, + MachinePointerInfo PtrInfo) const override; + + SDValue emitTargetCodeForSetFPEnv(SelectionDAG &DAG, const SDLoc &DL, + SDValue Chain, SDValue Addr, + MachinePointerInfo PtrInfo) const override; + + SDValue emitTargetCodeForResetFPEnv(SelectionDAG &DAG, const SDLoc &DL, + SDValue Chain) const override; }; } diff --git a/llvm/lib/Target/ARM/ARMSelectionDAGInfo.cpp b/llvm/lib/Target/ARM/ARMSelectionDAGInfo.cpp --- a/llvm/lib/Target/ARM/ARMSelectionDAGInfo.cpp +++ b/llvm/lib/Target/ARM/ARMSelectionDAGInfo.cpp @@ -253,3 +253,35 @@ return EmitSpecializedLibcall(DAG, dl, Chain, Dst, Src, Size, Align, RTLIB::MEMSET); } + +SDValue ARMSelectionDAGInfo::emitTargetCodeForGetFPEnv( + SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Addr, + MachinePointerInfo PtrInfo) const { + SDValue Ops[] = {Chain, + DAG.getConstant(Intrinsic::arm_get_fpscr, DL, MVT::i32)}; + SDValue FPSCR = + DAG.getNode(ISD::INTRINSIC_W_CHAIN, DL, {MVT::i32, MVT::Other}, Ops); + Chain = FPSCR.getValue(1); + Chain = DAG.getStore(Chain, DL, FPSCR.getValue(0), Addr, PtrInfo, + DAG.InferPtrAlign(Addr), MachineMemOperand::MOStore); + return Chain; +} + +SDValue ARMSelectionDAGInfo::emitTargetCodeForSetFPEnv( + SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Addr, + MachinePointerInfo PtrInfo) const { + SDValue Load = DAG.getLoad(Addr.getValueType(), DL, Chain, Addr, PtrInfo, + DAG.InferPtrAlign(Addr)); + SDValue Ops[] = { + Chain, DAG.getConstant(Intrinsic::arm_set_fpscr, DL, MVT::i32), Load}; + return DAG.getNode(ISD::INTRINSIC_VOID, DL, MVT::Other, Ops); +} + +SDValue ARMSelectionDAGInfo::emitTargetCodeForResetFPEnv(SelectionDAG &DAG, + const SDLoc &DL, + SDValue Chain) const { + SDValue Ops[] = {Chain, + DAG.getConstant(Intrinsic::arm_set_fpscr, DL, MVT::i32), + DAG.getConstant(0, DL, MVT::i32)}; + return DAG.getNode(ISD::INTRINSIC_VOID, DL, MVT::Other, Ops); +} diff --git a/llvm/test/CodeGen/ARM/fpenv.ll b/llvm/test/CodeGen/ARM/fpenv.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/fpenv.ll @@ -0,0 +1,71 @@ +; RUN: llc -mtriple=arm-eabi -float-abi=soft -mattr=+vfp2 -o - %s | FileCheck %s + +define void @func_01(i8* %fpenv) { +entry: + call void @llvm.get.fpenv(i8* %fpenv) + ret void +} +; CHECK-LABEL: func_01: +; CHECK: vmrs [[REG:r[0-9]+]], fpscr +; CHECK-NEXT: str [[REG]], [r0] +; CHECK-NEXT: mov pc, lr + + +define void @func_02(i8* %fpenv) { +entry: + call void @llvm.set.fpenv(i8* %fpenv) + ret void +} +; CHECK-LABEL: func_02: +; CHECK: ldr [[REG:r[0-9]+]], [r0] +; CHECK-NEXT: vmsr fpscr, [[REG]] +; CHECK-NEXT: mov pc, lr + + +define void @func_03() { +entry: + call void @llvm.reset.fpenv() + ret void +} +; CHECK-LABEL: func_03: +; CHECK: mov [[REG:r[0-9]+]], #0 +; CHECK-NEXT: vmsr fpscr, [[REG]] +; CHECK-NEXT: mov pc, lr + + +; Check that in the sequence (get_fpenv, set_fpenv) memory is not used. +define void @func_04() { +entry: + %fpenv = alloca i32 + %fptr = bitcast i32* %fpenv to i8* + call void @llvm.get.fpenv(i8* %fptr) + call void @llvm.set.fpenv(i8* %fptr) + ret void +} +; CHECK-LABEL: func_04: +; CHECK: vmrs [[REG:r[0-9]+]], fpscr +; CHECK: vmsr fpscr, [[REG]] + + +; Check that in the sequence (get_fpenv, state modification, set_fpenv) memory +; is not used. +define void @func_05() { +entry: + %fpenv = alloca i32 + %fptr = bitcast i32* %fpenv to i8* + call void @llvm.get.fpenv(i8* %fptr) + %fpenv2 = load i32, i32* %fpenv + %fpenv3 = and i32 %fpenv2, 12582912 + store i32 %fpenv3, i32* %fpenv, align 4 + call void @llvm.set.fpenv(i8* %fptr) + ret void +} +; CHECK-LABEL: func_05: +; CHECK: vmrs [[REG1:r[0-9]+]], fpscr +; CHECK: and [[REG2:r[0-0]+]], [[REG1]], #12582912 +; CHECK: vmsr fpscr, [[REG2]] + + +declare void @llvm.get.fpenv(i8* %fpenv) +declare void @llvm.set.fpenv(i8* %fpenv) +declare void @llvm.reset.fpenv()