From d1bad2fb8c7c938d836a792f58952aa694b36440 Mon Sep 17 00:00:00 2001 From: Owen Anderson Date: Thu, 17 Jul 2025 20:51:47 +0800 Subject: [PATCH] [CHERIOT] Use capability registers to store f64 values. This enables each f64 to be passed by value in a single cap register, rather than in pairs of integer registers. This required adding explicit type annotations to various places in the XCheri tblgen files, as the GPCR class can now hold values type c64 or f64, breaking type inference. --- llvm/lib/Target/RISCV/RISCVCallingConv.cpp | 8 + llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 154 ++++++++- llvm/lib/Target/RISCV/RISCVISelLowering.h | 3 +- llvm/lib/Target/RISCV/RISCVInstrInfoXCheri.td | 115 ++++--- llvm/lib/Target/RISCV/RISCVRegisterInfo.td | 71 ++--- .../CodeGen/RISCV/cheri/cheriot-f64-abi.ll | 295 ++++++++++++++++++ 6 files changed, 553 insertions(+), 93 deletions(-) create mode 100644 llvm/test/CodeGen/RISCV/cheri/cheriot-f64-abi.ll diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp index 9c919907a6387..e8821122a377a 100644 --- a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp +++ b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp @@ -486,6 +486,14 @@ bool llvm::CC_RISCV(unsigned ValNo, MVT ValVT, MVT LocVT, } } + // Cheriot uses GPCR without a bitcast when possible. + if (LocVT == MVT::f64 && Subtarget.hasVendorXCheriot() && !IsPureCapVarArgs) { + if (MCRegister Reg = State.AllocateReg(ArgGPCRs)) { + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + return false; + } + } + // FP smaller than XLen, uses custom GPR. if (LocVT == MVT::f16 || LocVT == MVT::bf16 || (LocVT == MVT::f32 && XLen == 64)) { diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 9b06ef6befd61..011400e1f4c36 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -170,6 +170,11 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, addRegisterClass(CapType, &RISCV::GPCRRegClass); } + if (Subtarget.hasVendorXCheriot()) { + // Cheriot holds f64's in capability registers. + addRegisterClass(MVT::f64, &RISCV::GPCRRegClass); + } + static const MVT::SimpleValueType BoolVecVTs[] = { MVT::nxv1i1, MVT::nxv2i1, MVT::nxv4i1, MVT::nxv8i1, MVT::nxv16i1, MVT::nxv32i1, MVT::nxv64i1}; @@ -680,6 +685,29 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, setLibcallName(RTLIB::MEMSET, "memset"); } + if (Subtarget.hasVendorXCheriot()) { + // FP64 is "legal" on Cheriot in that we store it in c64 registers, but + // essentially no operations on it are legal other than load/store/copy. + setOperationAction({ISD::ConstantFP, ISD::SELECT_CC, ISD::SETCC}, MVT::f64, + Custom); + + // These require custom lowering because their inputs might be f64. + setOperationAction({ISD::SELECT_CC, ISD::SETCC}, MVT::i32, Custom); + setOperationAction({ISD::FP_TO_UINT, ISD::FP_TO_SINT}, MVT::i32, LibCall); + + static const unsigned CheriotF64ExpandOps[] = { + ISD::FMINNUM, ISD::FMAXNUM, ISD::FADD, ISD::FSUB, + ISD::FMUL, ISD::FMA, ISD::FDIV, ISD::FSQRT, + ISD::FCEIL, ISD::FTRUNC, ISD::FFLOOR, ISD::FROUND, + ISD::FROUNDEVEN, ISD::FRINT, ISD::FNEARBYINT, ISD::IS_FPCLASS, + ISD::SETCC, ISD::FMAXIMUM, ISD::FMINIMUM, ISD::STRICT_FADD, + ISD::STRICT_FSUB, ISD::STRICT_FMUL, ISD::STRICT_FDIV, ISD::STRICT_FSQRT, + ISD::STRICT_FMA, ISD::FNEG, ISD::FABS, ISD::FCOPYSIGN, + ISD::UINT_TO_FP, ISD::SINT_TO_FP, ISD::BR_CC}; + setOperationAction(CheriotF64ExpandOps, MVT::f64, Expand); + setCondCodeAction(FPCCToExpand, MVT::f64, Expand); + } + // TODO: On M-mode only targets, the cycle[h]/time[h] CSR may not be present. // Unfortunately this can't be determined just from the ISA naming string. setOperationAction(ISD::READCYCLECOUNTER, MVT::i64, @@ -6145,11 +6173,44 @@ static SDValue lowerConstant(SDValue Op, SelectionDAG &DAG, return SDValue(); } -SDValue RISCVTargetLowering::lowerConstantFP(SDValue Op, - SelectionDAG &DAG) const { +SDValue +RISCVTargetLowering::lowerConstantFP(SDValue Op, SelectionDAG &DAG, + const RISCVSubtarget &Subtarget) const { MVT VT = Op.getSimpleValueType(); const APFloat &Imm = cast(Op)->getValueAPF(); + if (Subtarget.hasVendorXCheriot()) { + // Cheriot needs to custom lower f64 immediates using csethigh + if (VT != MVT::f64) + return Op; + + SDLoc DL(Op); + uint64_t Val = Imm.bitcastToAPInt().getLimitedValue(); + + // Materialize 0.0 as cnull + if (Val == 0) + return DAG.getRegister(getNullCapabilityRegister(), MVT::f64); + + // Otherwise, materialize the low part into a 32-bit register. + auto Lo = DAG.getConstant(Val & 0xFFFFFFFF, DL, MVT::i32); + auto LoAsCap = DAG.getTargetInsertSubreg(RISCV::sub_cap_addr, DL, MVT::c64, + DAG.getUNDEF(MVT::f64), Lo); + + // The high half of a capability register is zeroed by integer ops, + // so if we wanted a zero high half then we are done. + if (Val >> 32 == 0) + return DAG.getBitcast(MVT::f64, LoAsCap); + + // Otherwise, materialize the high half and use csethigh to combine the two + // halve. + auto Hi = DAG.getConstant(Val >> 32, DL, MVT::i32); + auto Cap = DAG.getNode( + ISD::INTRINSIC_WO_CHAIN, DL, MVT::c64, + DAG.getTargetConstant(Intrinsic::cheri_cap_high_set, DL, MVT::i32), + LoAsCap, Hi); + return DAG.getBitcast(MVT::f64, Cap); + } + // Can this constant be selected by a Zfa FLI instruction? bool Negate = false; int Index = getLegalZfaFPImm(Imm, VT); @@ -6799,7 +6860,7 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op, case ISD::Constant: return lowerConstant(Op, DAG, Subtarget); case ISD::ConstantFP: - return lowerConstantFP(Op, DAG); + return lowerConstantFP(Op, DAG, Subtarget); case ISD::SELECT: return lowerSELECT(Op, DAG); case ISD::BRCOND: @@ -7589,6 +7650,50 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op, case ISD::VECTOR_COMPRESS: return lowerVectorCompress(Op, DAG); case ISD::SELECT_CC: { + if (Subtarget.hasVendorXCheriot() && + (Op.getValueType() == MVT::f64 || + Op.getOperand(0).getValueType() == MVT::f64)) { + SDLoc DL(Op); + EVT CCVT = getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), + Op.getOperand(0).getValueType()); + SDValue SetCC; + if (Op.getOperand(0).getValueType() == MVT::f64) { + SDValue NewLHS = Op.getOperand(0), NewRHS = Op.getOperand(1); + ISD::CondCode CCCode = cast(Op.getOperand(4))->get(); + + NewLHS = DAG.getBitcast(MVT::c64, NewLHS); + NewRHS = DAG.getBitcast(MVT::c64, NewRHS); + softenSetCCOperands(DAG, MVT::f64, NewLHS, NewRHS, CCCode, DL, + Op.getOperand(0), Op.getOperand(1)); + + // If softenSetCCOperands returned a scalar, we need to compare the + // result against zero to select between true and false values. + if (!NewRHS.getNode()) { + NewRHS = DAG.getConstant(0, DL, NewLHS.getValueType()); + CCCode = ISD::SETNE; + } + + SetCC = DAG.getNode(ISD::SETCC, DL, CCVT, NewLHS, NewRHS, + DAG.getCondCode(CCCode)); + } else { + SetCC = DAG.getNode(ISD::SETCC, DL, CCVT, Op.getOperand(0), + Op.getOperand(1), Op.getOperand(4), Op->getFlags()); + } + + SDValue LSel = Op.getOperand(2); + if (LSel.getValueType() == MVT::f64) + LSel = DAG.getBitcast(MVT::c64, LSel); + SDValue RSel = Op.getOperand(3); + if (RSel.getValueType() == MVT::f64) + RSel = DAG.getBitcast(MVT::c64, RSel); + + SDValue Select = + DAG.getSelect(DL, LSel.getValueType(), SetCC, LSel, RSel); + if (Op.getValueType() == MVT::f64) + Select = DAG.getBitcast(MVT::f64, Select); + return Select; + } + // This occurs because we custom legalize SETGT and SETUGT for setcc. That // causes LegalizeDAG to think we need to custom legalize select_cc. Expand // into separate SETCC+SELECT just like LegalizeDAG. @@ -7608,13 +7713,43 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op, } case ISD::SETCC: { MVT OpVT = Op.getOperand(0).getSimpleValueType(); + + if (Subtarget.hasVendorXCheriot() && + (OpVT == MVT::f64 || Op.getValueType() == MVT::f64)) { + + SDNode *N = Op.getNode(); + bool IsStrict = N->isStrictFPOpcode(); + SDValue Op0 = N->getOperand(IsStrict ? 1 : 0); + SDValue Op1 = N->getOperand(IsStrict ? 2 : 1); + SDValue Chain = IsStrict ? N->getOperand(0) : SDValue(); + ISD::CondCode CCCode = + cast(N->getOperand(IsStrict ? 3 : 2))->get(); + + EVT VT = Op0.getValueType(); + SDValue NewLHS = DAG.getBitcast(MVT::c64, Op0); + SDValue NewRHS = DAG.getBitcast(MVT::c64, Op1); + softenSetCCOperands(DAG, VT, NewLHS, NewRHS, CCCode, SDLoc(N), Op0, Op1, + Chain, N->getOpcode() == ISD::STRICT_FSETCCS); + + // Update N to have the operands specified. + if (NewRHS.getNode()) { + if (IsStrict) + NewLHS = DAG.getNode(ISD::SETCC, SDLoc(N), N->getValueType(0), NewLHS, + NewRHS, DAG.getCondCode(CCCode)); + else + return SDValue(DAG.UpdateNodeOperands(N, NewLHS, NewRHS, + DAG.getCondCode(CCCode)), + 0); + } + } + if (OpVT.isScalarInteger()) { MVT VT = Op.getSimpleValueType(); SDValue LHS = Op.getOperand(0); SDValue RHS = Op.getOperand(1); ISD::CondCode CCVal = cast(Op.getOperand(2))->get(); - assert((CCVal == ISD::SETGT || CCVal == ISD::SETUGT) && - "Unexpected CondCode"); + if (CCVal != ISD::SETGT && CCVal != ISD::SETUGT) + return Op; SDLoc DL(Op); @@ -8633,6 +8768,15 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { MVT VT = Op.getSimpleValueType(); MVT XLenVT = Subtarget.getXLenVT(); + if (Subtarget.hasVendorXCheriot() && VT == MVT::f64) { + // Perform SELECT_CC on f64 by bitcasting through c64. + SDValue LHSCap = DAG.getBitcast(MVT::c64, TrueV); + SDValue RHSCap = DAG.getBitcast(MVT::c64, FalseV); + SDValue Select = + DAG.getNode(ISD::SELECT, DL, MVT::c64, CondV, LHSCap, RHSCap); + return DAG.getBitcast(MVT::f64, Select); + } + // Lower vector SELECTs to VSELECTs by splatting the condition. if (VT.isVector()) { MVT SplatCondVT = VT.changeVectorElementType(MVT::i1); diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h index d209b0808ce3c..72d83d71bde15 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.h +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h @@ -981,7 +981,8 @@ class RISCVTargetLowering : public TargetLowering { SelectionDAG &DAG) const; SDValue getTLSDescAddr(GlobalAddressSDNode *N, SelectionDAG &DAG) const; - SDValue lowerConstantFP(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerConstantFP(SDValue Op, SelectionDAG &DAG, + const RISCVSubtarget &Subtarget) const; SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerConstantPool(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXCheri.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXCheri.td index 17ef8e5526a8e..d77fef172e383 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoXCheri.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXCheri.td @@ -1455,7 +1455,7 @@ def : PatGpcrUimm12; def : PatGpcrGpr; def : PatGpcrUimm12; -def : Pat<(CapFrameAddrRegImm GPCR:$rs1, simm12:$imm12), +def : Pat<(CapFrameAddrRegImm(cPTR GPCR:$rs1), simm12:$imm12), (CIncOffsetImm GPCR:$rs1, simm12:$imm12)>; /// Pointer-Arithmetic Instructions @@ -1467,14 +1467,15 @@ def : Pat<(XLenVT (int_cheri_cap_diff GPCR:$cs1, GPCR:$cs2)), (XLenVT (EXTRACT_SUBREG GPCR:$cs2, sub_cap_addr)))>; let Predicates = [IsPureCapABI] in { -def : Pat<(inttoptr (XLenVT GPR:$rs2)), (CIncOffset C0, GPR:$rs2)>; -def : Pat<(inttoptr simm12:$imm12), (CIncOffsetImm C0, simm12:$imm12)>; -def : Pat<(XLenVT (ptrtoint GPCR:$rs1)), (PseudoCGetAddr GPCR:$rs1)>; + def : Pat<(inttoptr(XLenVT GPR:$rs2)), (cPTR(CIncOffset(cPTR C0), GPR:$rs2))>; + def : Pat<(inttoptr simm12:$imm12), (cPTR(CIncOffsetImm(cPTR C0), + simm12:$imm12))>; + def : Pat<(XLenVT(ptrtoint(cPTR GPCR:$rs1))), (PseudoCGetAddr GPCR:$rs1)>; } /// Null Capability Patterns -def : Pat<(inttoptr (XLenVT 0)), (CLenVT (COPY C0))>; +def : Pat<(inttoptr(XLenVT 0)), (CLenVT(COPY(cPTR C0)))>; def : Pat<(ptrtoint (CLenVT (inttoptr (XLenVT 0)))), (XLenVT (COPY (XLenVT X0)))>; @@ -1485,26 +1486,31 @@ def : Pat<(ptrtoint (CLenVT (inttoptr (XLenVT 0)))), // * Break untagged < tagged semantics // * Can't implement exact equality class CheriSetCCPatGpcrGpcr - : Pat<(XLenVT (CondOp GPCR:$cs1, GPCR:$cs2)), - (OutPatFrag<(ops node:$rs1, node:$rs2), GprGprDag> - (XLenVT (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), - (XLenVT (EXTRACT_SUBREG GPCR:$cs2, sub_cap_addr)))>; + : Pat<(XLenVT(CondOp(cPTR GPCR:$cs1), (cPTR GPCR:$cs2))), + (OutPatFrag<(ops node:$rs1, node:$rs2), GprGprDag>(XLenVT + (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), + (XLenVT(EXTRACT_SUBREG GPCR:$cs2, sub_cap_addr)))>; multiclass CheriSetCCPatGpcrImm { - def : Pat<(XLenVT (CondOp GPCR:$cs1, (inttoptr ImmTy:$imm12))), - (OutPatFrag<(ops node:$rs1, node:$imm12), GprImmDag> - (XLenVT (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), ImmTy:$imm12)>; - def : Pat<(XLenVT (CondOp GPCR:$cs1, (cptradd (inttoptr (XLenVT 0)), ImmTy:$imm12))), - (OutPatFrag<(ops node:$rs1, node:$imm12), GprImmDag> - (XLenVT (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), ImmTy:$imm12)>; - def : Pat<(XLenVT (CondOp GPCR:$cs1, - (int_cheri_cap_offset_set (inttoptr (XLenVT 0)), ImmTy:$imm12))), - (OutPatFrag<(ops node:$rs1, node:$imm12), GprImmDag> - (XLenVT (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), ImmTy:$imm12)>; - def : Pat<(XLenVT (CondOp GPCR:$cs1, - (int_cheri_cap_address_set (inttoptr (XLenVT 0)), ImmTy:$imm12))), - (OutPatFrag<(ops node:$rs1, node:$imm12), GprImmDag> - (XLenVT (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), ImmTy:$imm12)>; + def : Pat<(XLenVT(CondOp(cPTR GPCR:$cs1), (inttoptr ImmTy:$imm12))), + (OutPatFrag<(ops node:$rs1, node:$imm12), GprImmDag>(XLenVT + (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), + ImmTy:$imm12)>; + def : Pat<(XLenVT(CondOp(cPTR GPCR:$cs1), (cptradd(inttoptr(XLenVT 0)), + ImmTy:$imm12))), + (OutPatFrag<(ops node:$rs1, node:$imm12), GprImmDag>(XLenVT + (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), + ImmTy:$imm12)>; + def : Pat<(XLenVT(CondOp(cPTR GPCR:$cs1), + (int_cheri_cap_offset_set(inttoptr(XLenVT 0)), ImmTy:$imm12))), + (OutPatFrag<(ops node:$rs1, node:$imm12), GprImmDag>(XLenVT + (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), + ImmTy:$imm12)>; + def : Pat<(XLenVT(CondOp(cPTR GPCR:$cs1), + (int_cheri_cap_address_set(inttoptr(XLenVT 0)), ImmTy:$imm12))), + (OutPatFrag<(ops node:$rs1, node:$imm12), GprImmDag>(XLenVT + (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)), + ImmTy:$imm12)>; } multiclass CheriSetCCPatGpcrSimm12 @@ -1514,9 +1520,9 @@ multiclass CheriSetCCPatGpcrSimm12Plus1 : CheriSetCCPatGpcrImm; class CheriSetCCPatGpcrNull - : Pat<(XLenVT (CondOp GPCR:$cs1, (inttoptr (XLenVT 0)))), - (OutPatFrag<(ops node:$rs1), GprDag> - (XLenVT (EXTRACT_SUBREG GPCR:$cs1, sub_cap_addr)))>; + : Pat<(XLenVT(CondOp(cPTR GPCR:$cs1), (inttoptr(XLenVT 0)))), + (OutPatFrag<(ops node:$rs1), GprDag>(XLenVT(EXTRACT_SUBREG GPCR:$cs1, + sub_cap_addr)))>; class Swap : PatFrag<(ops node:$a, node:$b), (BinFrag $b, $a)>; @@ -1559,11 +1565,10 @@ defm Select_GPCR : SelectCC_GPR_rrirr; // No dedicated instructions; see above class CheriBccPat - : Pat<(brcond (XLenVT (CondOp GPCR:$rs1, GPCR:$rs2)), bb:$imm12), - (Inst - (XLenVT (EXTRACT_SUBREG GPCR:$rs1, sub_cap_addr)), - (XLenVT (EXTRACT_SUBREG GPCR:$rs2, sub_cap_addr)), - simm13_lsb0:$imm12)>; + : Pat<(brcond(XLenVT(CondOp(cPTR GPCR:$rs1), (cPTR GPCR:$rs2))), bb:$imm12), + (Inst(XLenVT(EXTRACT_SUBREG GPCR:$rs1, sub_cap_addr)), + (XLenVT(EXTRACT_SUBREG GPCR:$rs2, sub_cap_addr)), + simm13_lsb0:$imm12)>; def : CheriBccPat; def : CheriBccPat; @@ -1573,11 +1578,10 @@ def : CheriBccPat; def : CheriBccPat; class CheriBccSwapPat - : Pat<(brcond (XLenVT (CondOp GPCR:$rs1, GPCR:$rs2)), bb:$imm12), - (InstBcc - (XLenVT (EXTRACT_SUBREG GPCR:$rs2, sub_cap_addr)), - (XLenVT (EXTRACT_SUBREG GPCR:$rs1, sub_cap_addr)), - simm13_lsb0:$imm12)>; + : Pat<(brcond(XLenVT(CondOp(cPTR GPCR:$rs1), (cPTR GPCR:$rs2))), bb:$imm12), + (InstBcc(XLenVT(EXTRACT_SUBREG GPCR:$rs2, sub_cap_addr)), + (XLenVT(EXTRACT_SUBREG GPCR:$rs1, sub_cap_addr)), + simm13_lsb0:$imm12)>; def : CheriBccSwapPat; def : CheriBccSwapPat; @@ -1591,15 +1595,18 @@ def : PatGpcrGpcr; /// Special Capability Register Access Instructions -def : Pat<(int_cheri_ddc_get), (CSpecialRW SCR_DDC.Encoding, C0)>; -let Predicates = [HasCheri, IsPureCapABI] in -def : Pat<(int_cheri_stack_cap_get), (CLenVT (COPY C2))>; +def : Pat<(int_cheri_ddc_get), (CSpecialRW SCR_DDC.Encoding, (cPTR C0))>; +let Predicates = [HasCheri, + IsPureCapABI] in def : Pat<(int_cheri_stack_cap_get), + (CLenVT(COPY(cPTR C2)))>; let Predicates = [HasCheri, IsCapMode] in def : Pat<(int_cheri_pcc_get), (AUIPCC 0)>; -let Predicates = [HasCheri, NotCapMode] in -def : Pat<(int_cheri_pcc_get), (CSpecialRW SCR_PCC.Encoding, C0)>; +let Predicates = [HasCheri, + NotCapMode] in def : Pat<(int_cheri_pcc_get), + (CSpecialRW SCR_PCC.Encoding, + (cPTR C0))>; /// Fast Register-Clearing Instructions @@ -1828,13 +1835,14 @@ defm : PseudoCmpXchgPat<"atomic_cmp_swap_cap", PseudoCmpXchgCap, CLenVT, GPCR>; /// Capability Mode Instructions multiclass CheriLdPat { - def : Pat<(ReturnVt (LoadOp (CapRegImm GPCR:$rs1, simm12:$imm12))), + def : Pat<(ReturnVt(LoadOp(CapRegImm(cPTR GPCR:$rs1), simm12:$imm12))), (Inst GPCR:$rs1, simm12:$imm12)>; } multiclass CheriStPat { - def : Pat<(StoreOp (StoreVt StTy:$rs2), (CapRegImm GPCR:$rs1, simm12:$imm12)), - (Inst (StoreVt StTy:$rs2), GPCR:$rs1, simm12:$imm12)>; + def : Pat<(StoreOp(StoreVt StTy:$rs2), (CapRegImm(cPTR GPCR:$rs1), + simm12:$imm12)), + (Inst(StoreVt StTy:$rs2), GPCR:$rs1, simm12:$imm12)>; } multiclass CheriAtomicStPat @@ -2291,6 +2299,11 @@ defm : CheriLdPat; defm : CheriStPat; } // Predicates = [HasCheri, IsRV64, IsCapMode] +let Predicates = [HasCheri, HasCheriot, IsRV32, IsCapMode] in { + defm : CheriLdPat; + defm : CheriStPat; +} // Predicates = [HasCheri, HasCheriot, IsRV32, IsCapMode] + //===----------------------------------------------------------------------===// // Compress Instruction tablegen backend. //===----------------------------------------------------------------------===// @@ -2433,7 +2446,17 @@ let Predicates = [HasCheri, IsRV32, IsCapMode, IsRVE] in { let mayLoad = true, mayStore = false, hasSideEffects = false in def PseudoCLLW : Pseudo<(outs GPCR:$dst), (ins bare_symbol:$src), [], "cllc", "$dst, $src">; -def : Pat<(load (cPTR (load (iPTR globaladdr:$src)))), +def : Pat<(c64(load(cPTR(load(iPTR globaladdr:$src))))), + (PseudoCLLW bare_symbol:$src)>; +def : Pat<(f64(load(cPTR(load(iPTR globaladdr:$src))))), (PseudoCLLW bare_symbol:$src)>; } // Predicates = [HasCheri, IsRV32, IsCapMode, IsRVE] - \ No newline at end of file + +// Cheriot stores f64 in cap registers, so bitcasting between f64 and c64 +// is a no-op. +multiclass NopCapRegCast { + def : Pat<(Ty1(bitconvert(Ty2 GPCR:$Val))), (Ty1 GPCR:$Val)>; + def : Pat<(Ty2(bitconvert(Ty1 GPCR:$Val))), (Ty2 GPCR:$Val)>; +} + +let Predicates = [HasCheri, HasCheriot] in { defm : NopCapRegCast; } diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td index 27145170ea304..50a9165f8c42e 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td @@ -282,6 +282,8 @@ def XLenRI : RegInfoByHwMode< [RegInfo<32,32,32>, RegInfo<64,64,64>]>; def CLenVT : ValueTypeByHwMode<[RV32, RV64], [c64, c128]>; +def CLenFloatVT : ValueTypeByHwMode<[RV32, RV64], [f64, f128]>; + def CLenRI : RegInfoByHwMode< [RV32, RV64], [RegInfo<64,64,64>, RegInfo<128,128,128>]>; @@ -313,44 +315,32 @@ def GPR : GPRRegisterClass<(add (sequence "X%u", 10, 17), (sequence "X%u", 18, 27), (sequence "X%u", 0, 4))>; -def GPCR : RegisterClass<"RISCV", [CLenVT], 64, (add - (sequence "C%u", 10, 17), - (sequence "C%u", 5, 7), - (sequence "C%u", 28, 31), - (sequence "C%u", 8, 9), - (sequence "C%u", 18, 27), - (sequence "C%u", 0, 4) - )> { +def GPCR + : RegisterClass<"RISCV", [CLenVT, CLenFloatVT], 64, + (add(sequence "C%u", 10, 17), (sequence "C%u", 5, 7), + (sequence "C%u", 28, 31), (sequence "C%u", 8, 9), + (sequence "C%u", 18, 27), (sequence "C%u", 0, 4))> { let RegInfos = CLenRI; } -def GPRE : RegisterClass<"RISCV", [XLenVT], 32, (add - (sequence "X%u", 10, 15), - (sequence "X%u", 5, 7), - (sequence "X%u", 8, 9), - (sequence "X%u", 0, 4) - )> { +def GPRE : RegisterClass<"RISCV", [XLenVT, i32], 32, + (add(sequence "X%u", 10, 15), (sequence "X%u", 5, 7), + (sequence "X%u", 8, 9), (sequence "X%u", 0, 4))> { let RegInfos = XLenRI; } -def GPCRE : RegisterClass<"RISCV", [CLenVT], 64, (add - (sequence "C%u", 10, 15), - (sequence "C%u", 5, 7), - (sequence "C%u", 8, 9), - (sequence "C%u", 0, 4) - )> { +def GPCRE : RegisterClass<"RISCV", [CLenVT, CLenFloatVT], 64, + (add(sequence "C%u", 10, 15), (sequence "C%u", 5, 7), + (sequence "C%u", 8, 9), (sequence "C%u", 0, 4))> { let RegInfos = CLenRI; } -def GPCRC0IsDDC : RegisterClass<"RISCV", [CLenVT], 64, (add - (sequence "C%u", 10, 17), - (sequence "C%u", 5, 7), - (sequence "C%u", 28, 31), - (sequence "C%u", 8, 9), - (sequence "C%u", 18, 27), - (sequence "C%u", 1, 4), - DDC - )> { +def GPCRC0IsDDC + : RegisterClass<"RISCV", [CLenVT, CLenFloatVT], 64, + (add(sequence "C%u", 10, 17), (sequence "C%u", 5, 7), + (sequence "C%u", 28, 31), (sequence "C%u", 8, 9), + (sequence "C%u", 18, 27), (sequence "C%u", 1, 4), + DDC)> { let RegInfos = CLenRI; } @@ -360,11 +350,13 @@ def GPRX5 : GPRRegisterClass<(add X5)>; def GPRNoX0 : GPRRegisterClass<(sub GPR, X0)>; -def GPCRNoC0 : RegisterClass<"RISCV", [CLenVT], 64, (sub GPCR, C0)> { +def GPCRNoC0 + : RegisterClass<"RISCV", [CLenVT, CLenFloatVT], 64, (sub GPCR, C0)> { let RegInfos = CLenRI; } -def GPCRNoC0C1 : RegisterClass<"RISCV", [CLenVT], 64, (sub GPCRNoC0, C1)> { +def GPCRNoC0C1 + : RegisterClass<"RISCV", [CLenVT, CLenFloatVT], 64, (sub GPCRNoC0, C1)> { let RegInfos = CLenRI; } @@ -383,10 +375,9 @@ def GPRJALRNonX7 : GPRRegisterClass<(sub GPRJALR, X7)>; def GPRC : GPRRegisterClass<(add (sequence "X%u", 10, 15), (sequence "X%u", 8, 9))>; -def GPCRC : RegisterClass<"RISCV", [CLenVT], 64, (add - (sequence "C%u", 10, 15), - (sequence "C%u", 8, 9) - )> { +def GPCRC + : RegisterClass<"RISCV", [CLenVT, CLenFloatVT], 64, + (add(sequence "C%u", 10, 15), (sequence "C%u", 8, 9))> { let RegInfos = CLenRI; } @@ -399,17 +390,15 @@ def GPRTC : GPRRegisterClass<(add (sequence "X%u", 6, 7), (sequence "X%u", 28, 31))>; def GPRTCNonX7 : GPRRegisterClass<(sub GPRTC, X7)>; -def GPCRTC : RegisterClass<"RISCV", [CLenVT], 64, (add - (sequence "C%u", 5, 7), - (sequence "C%u", 10, 17), - (sequence "C%u", 28, 31) - )> { +def GPCRTC : RegisterClass<"RISCV", [CLenVT, CLenFloatVT], 64, + (add(sequence "C%u", 5, 7), (sequence "C%u", 10, 17), + (sequence "C%u", 28, 31))> { let RegInfos = CLenRI; } def SP : GPRRegisterClass<(add X2)>; -def CSP : RegisterClass<"RISCV", [CLenVT], 64, (add C2)> { +def CSP : RegisterClass<"RISCV", [CLenVT, CLenFloatVT], 64, (add C2)> { let RegInfos = CLenRI; } diff --git a/llvm/test/CodeGen/RISCV/cheri/cheriot-f64-abi.ll b/llvm/test/CodeGen/RISCV/cheri/cheriot-f64-abi.ll new file mode 100644 index 0000000000000..9f5e2ffeb4b44 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/cheri/cheriot-f64-abi.ll @@ -0,0 +1,295 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -o - %s --mcpu=cheriot --mtriple=riscv32cheriot-unknown-cheriotrtos -target-abi cheriot -mattr=+xcheri,+xcheriot,+cap-mode | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32-S128-pf200:64:64:64:32-A200-P200-G200" +target triple = "riscv32cheriot-unknown-cheriotrtos" + +%struct.TwoDoubles = type { double, double } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local noundef double @_Z6test01d(double noundef returned %a) local_unnamed_addr addrspace(200) #0 { +; CHECK-LABEL: _Z6test01d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cret +entry: + ret double %a +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local noundef double @_Z6test02dd(double noundef %a, double noundef returned %b) local_unnamed_addr addrspace(200) #0 { +; CHECK-LABEL: _Z6test02dd: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cmove ca0, ca1 +; CHECK-NEXT: ct.cret +entry: + ret double %b +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +define dso_local noundef double @_Z6test03Pd(ptr addrspace(200) nocapture noundef readonly %a) local_unnamed_addr addrspace(200) #1 { +; CHECK-LABEL: _Z6test03Pd: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.clc ca0, 0(ca0) +; CHECK-NEXT: ct.cret +entry: + %0 = load double, ptr addrspace(200) %a, align 8, !tbaa !6 + ret double %0 +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local noundef double @_Z6test04v() local_unnamed_addr addrspace(200) #0 { +; CHECK-LABEL: _Z6test04v: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: lui a0, 261888 +; CHECK-NEXT: ct.csethigh ca0, cnull, a0 +; CHECK-NEXT: ct.cret +entry: + ret double 1.000000e+00 +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local noundef double @_Z6test05d(double noundef %a) local_unnamed_addr addrspace(200) #0 { +; CHECK-LABEL: _Z6test05d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cincoffset csp, csp, -16 +; CHECK-NEXT: ct.csc cra, 8(csp) # 8-byte Folded Spill +; CHECK-NEXT: lui a1, 261888 +; CHECK-NEXT: ct.csethigh ca1, cnull, a1 +; CHECK-NEXT: .LBB4_1: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: ct.auipcc ct2, %cheriot_compartment_hi(__library_import_libcalls___adddf3) +; CHECK-NEXT: ct.clc ct2, %cheriot_compartment_lo_i(.LBB4_1)(ct2) +; CHECK-NEXT: ct.cjalr ct2 +; CHECK-NEXT: ct.clc cra, 8(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.cincoffset csp, csp, 16 +; CHECK-NEXT: ct.cret +entry: + %add = fadd double %a, 1.000000e+00 + ret double %add +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local noundef double @_Z6test06d(double noundef %a) local_unnamed_addr addrspace(200) #0 { +; CHECK-LABEL: _Z6test06d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cincoffset csp, csp, -16 +; CHECK-NEXT: ct.csc cra, 8(csp) # 8-byte Folded Spill +; CHECK-NEXT: ct.cmove ca1, cnull +; CHECK-NEXT: .LBB5_1: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: ct.auipcc ct2, %cheriot_compartment_hi(__library_import_libcalls___adddf3) +; CHECK-NEXT: ct.clc ct2, %cheriot_compartment_lo_i(.LBB5_1)(ct2) +; CHECK-NEXT: ct.cjalr ct2 +; CHECK-NEXT: ct.clc cra, 8(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.cincoffset csp, csp, 16 +; CHECK-NEXT: ct.cret +entry: + %add = fadd double %a, 0.000000e+00 + ret double %add +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local noundef double @_Z6test07dd(double noundef %a, double noundef %b) local_unnamed_addr addrspace(200) #0 { +; CHECK-LABEL: _Z6test07dd: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cincoffset csp, csp, -16 +; CHECK-NEXT: ct.csc cra, 8(csp) # 8-byte Folded Spill +; CHECK-NEXT: ct.cmove ca1, cnull +; CHECK-NEXT: .LBB6_1: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: ct.auipcc ct2, %cheriot_compartment_hi(__library_import_libcalls___adddf3) +; CHECK-NEXT: ct.clc ct2, %cheriot_compartment_lo_i(.LBB6_1)(ct2) +; CHECK-NEXT: ct.cjalr ct2 +; CHECK-NEXT: ct.clc cra, 8(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.cincoffset csp, csp, 16 +; CHECK-NEXT: ct.cret +entry: + %add = fadd double %a, 0.000000e+00 + ret double %add +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +define dso_local void @_Z6test08dPd(double noundef %a, ptr addrspace(200) nocapture noundef writeonly initializes((0, 8)) %b) local_unnamed_addr addrspace(200) #2 { +; CHECK-LABEL: _Z6test08dPd: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cincoffset csp, csp, -16 +; CHECK-NEXT: ct.csc cra, 8(csp) # 8-byte Folded Spill +; CHECK-NEXT: ct.csc cs0, 0(csp) # 8-byte Folded Spill +; CHECK-NEXT: ct.cmove cs0, ca1 +; CHECK-NEXT: lui a1, 261888 +; CHECK-NEXT: ct.csethigh ca1, cnull, a1 +; CHECK-NEXT: .LBB7_1: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: ct.auipcc ct2, %cheriot_compartment_hi(__library_import_libcalls___adddf3) +; CHECK-NEXT: ct.clc ct2, %cheriot_compartment_lo_i(.LBB7_1)(ct2) +; CHECK-NEXT: ct.cjalr ct2 +; CHECK-NEXT: ct.csc ca0, 0(cs0) +; CHECK-NEXT: ct.clc cra, 8(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.clc cs0, 0(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.cincoffset csp, csp, 16 +; CHECK-NEXT: ct.cret +entry: + %add = fadd double %a, 1.000000e+00 + store double %add, ptr addrspace(200) %b, align 8, !tbaa !6 + ret void +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +define dso_local void @_Z6test09dPd(double noundef %a, ptr addrspace(200) nocapture noundef writeonly initializes((8, 16)) %b) local_unnamed_addr addrspace(200) #2 { +; CHECK-LABEL: _Z6test09dPd: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.csc ca0, 8(ca1) +; CHECK-NEXT: ct.cret +entry: + %arrayidx = getelementptr inbounds nuw i8, ptr addrspace(200) %b, i32 8 + store double %a, ptr addrspace(200) %arrayidx, align 8, !tbaa !6 + ret void +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local noundef double @_Z6test10dd(double noundef %a, double noundef %b) local_unnamed_addr addrspace(200) #0 { +; CHECK-LABEL: _Z6test10dd: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cincoffset csp, csp, -16 +; CHECK-NEXT: ct.csc cra, 8(csp) # 8-byte Folded Spill +; CHECK-NEXT: .LBB9_1: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: ct.auipcc ct2, %cheriot_compartment_hi(__library_import_libcalls___subdf3) +; CHECK-NEXT: ct.clc ct2, %cheriot_compartment_lo_i(.LBB9_1)(ct2) +; CHECK-NEXT: ct.cjalr ct2 +; CHECK-NEXT: ct.clc cra, 8(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.cincoffset csp, csp, 16 +; CHECK-NEXT: ct.cret +entry: + %sub = fsub double %a, %b + ret double %sub +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local noundef double @_Z6test11dd(double noundef %a, double noundef %b) local_unnamed_addr addrspace(200) #0 { +; CHECK-LABEL: _Z6test11dd: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cincoffset csp, csp, -16 +; CHECK-NEXT: ct.csc cra, 8(csp) # 8-byte Folded Spill +; CHECK-NEXT: .LBB10_1: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: ct.auipcc ct2, %cheriot_compartment_hi(__library_import_libcalls___muldf3) +; CHECK-NEXT: ct.clc ct2, %cheriot_compartment_lo_i(.LBB10_1)(ct2) +; CHECK-NEXT: ct.cjalr ct2 +; CHECK-NEXT: ct.clc cra, 8(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.cincoffset csp, csp, 16 +; CHECK-NEXT: ct.cret +entry: + %mul = fmul double %a, %b + ret double %mul +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local noundef double @_Z6test12dd(double noundef %a, double noundef %b) local_unnamed_addr addrspace(200) #0 { +; CHECK-LABEL: _Z6test12dd: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cincoffset csp, csp, -16 +; CHECK-NEXT: ct.csc cra, 8(csp) # 8-byte Folded Spill +; CHECK-NEXT: .LBB11_1: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: ct.auipcc ct2, %cheriot_compartment_hi(__library_import_libcalls___divdf3) +; CHECK-NEXT: ct.clc ct2, %cheriot_compartment_lo_i(.LBB11_1)(ct2) +; CHECK-NEXT: ct.cjalr ct2 +; CHECK-NEXT: ct.clc cra, 8(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.cincoffset csp, csp, 16 +; CHECK-NEXT: ct.cret +entry: + %div = fdiv double %a, %b + ret double %div +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +define dso_local noundef double @_Z6test1310TwoDoubles(ptr addrspace(200) nocapture noundef readonly %in) local_unnamed_addr addrspace(200) #1 { +; CHECK-LABEL: _Z6test1310TwoDoubles: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.cincoffset csp, csp, -16 +; CHECK-NEXT: ct.csc cra, 8(csp) # 8-byte Folded Spill +; CHECK-NEXT: ct.csc cs0, 0(csp) # 8-byte Folded Spill +; CHECK-NEXT: ct.clc cs0, 0(ca0) +; CHECK-NEXT: ct.clc ca0, 8(ca0) +; CHECK-NEXT: ct.cmove ca1, ca0 +; CHECK-NEXT: .LBB12_1: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: ct.auipcc ct2, %cheriot_compartment_hi(__library_import_libcalls___adddf3) +; CHECK-NEXT: ct.clc ct2, %cheriot_compartment_lo_i(.LBB12_1)(ct2) +; CHECK-NEXT: ct.cjalr ct2 +; CHECK-NEXT: ct.cmove ca1, cs0 +; CHECK-NEXT: .LBB12_2: # %entry +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: ct.auipcc ct2, %cheriot_compartment_hi(__library_import_libcalls___adddf3) +; CHECK-NEXT: ct.clc ct2, %cheriot_compartment_lo_i(.LBB12_2)(ct2) +; CHECK-NEXT: ct.cjalr ct2 +; CHECK-NEXT: ct.clc cra, 8(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.clc cs0, 0(csp) # 8-byte Folded Reload +; CHECK-NEXT: ct.cincoffset csp, csp, 16 +; CHECK-NEXT: ct.cret +entry: + %0 = load double, ptr addrspace(200) %in, align 8, !tbaa !10 + %b = getelementptr inbounds nuw i8, ptr addrspace(200) %in, i32 8 + %1 = load double, ptr addrspace(200) %b, align 8, !tbaa !12 + %2 = tail call addrspace(200) double @llvm.fmuladd.f64(double %1, double 2.000000e+00, double %0) + ret double %2 +} + +; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare double @llvm.fmuladd.f64(double, double, double) addrspace(200) #3 + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +define dso_local void @_Z6test13d(ptr addrspace(200) dead_on_unwind noalias nocapture writable writeonly sret(%struct.TwoDoubles) align 8 initializes((0, 16)) %agg.result, double noundef %a) local_unnamed_addr addrspace(200) #2 { +; CHECK-LABEL: _Z6test13d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ct.csc ca1, 0(ca0) +; CHECK-NEXT: lui a1, 262272 +; CHECK-NEXT: ct.csw a1, 12(ca0) +; CHECK-NEXT: ct.csw zero, 8(ca0) +; CHECK-NEXT: ct.cret +entry: + store double %a, ptr addrspace(200) %agg.result, align 8, !tbaa !10 + %b = getelementptr inbounds nuw i8, ptr addrspace(200) %agg.result, i32 8 + store double 3.000000e+00, ptr addrspace(200) %b, align 8, !tbaa !12 + ret void +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +define dso_local void @_Z6test14d(ptr addrspace(200) dead_on_unwind noalias nocapture writable writeonly sret(%struct.TwoDoubles) align 8 initializes((0, 16)) %agg.result, double noundef %a) local_unnamed_addr addrspace(200) #2 { +; CHECK-LABEL: _Z6test14d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: lui a2, 262464 +; CHECK-NEXT: ct.csw zero, 0(ca0) +; CHECK-NEXT: ct.csw a2, 4(ca0) +; CHECK-NEXT: ct.csc ca1, 8(ca0) +; CHECK-NEXT: ct.cret +entry: + store double 5.000000e+00, ptr addrspace(200) %agg.result, align 8, !tbaa !10 + %b = getelementptr inbounds nuw i8, ptr addrspace(200) %agg.result, i32 8 + store double %a, ptr addrspace(200) %b, align 8, !tbaa !12 + ret void +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "cheri-compartment"="qoi_decode" "no-builtin-longjmp" "no-builtin-printf" "no-builtin-setjmp" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cheriot" "target-features"="+32bit,+c,+e,+m,+relax,+unaligned-scalar-mem,+xcheri,+xcheriot,+zmmul,-a,-b,-d,-experimental-sdext,-experimental-sdtrig,-experimental-smctr,-experimental-ssctr,-experimental-svukte,-experimental-xqcia,-experimental-xqciac,-experimental-xqcicli,-experimental-xqcicm,-experimental-xqcics,-experimental-xqcicsr,-experimental-xqciint,-experimental-xqcilo,-experimental-xqcilsm,-experimental-xqcisls,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-experimental-zvbc32e,-experimental-zvkgs,-f,-h,-i,-sha,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smdbltrp,-smepmp,-smmpm,-smnpm,-smrnmi,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssdbltrp,-ssnpm,-sspm,-ssqosid,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-supm,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-svvptc,-v,-xcheri-norvc,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xmipscmove,-xmipslsp,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zacas,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b" } +attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) "cheri-compartment"="qoi_decode" "no-builtin-longjmp" "no-builtin-printf" "no-builtin-setjmp" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cheriot" "target-features"="+32bit,+c,+e,+m,+relax,+unaligned-scalar-mem,+xcheri,+xcheriot,+zmmul,-a,-b,-d,-experimental-sdext,-experimental-sdtrig,-experimental-smctr,-experimental-ssctr,-experimental-svukte,-experimental-xqcia,-experimental-xqciac,-experimental-xqcicli,-experimental-xqcicm,-experimental-xqcics,-experimental-xqcicsr,-experimental-xqciint,-experimental-xqcilo,-experimental-xqcilsm,-experimental-xqcisls,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-experimental-zvbc32e,-experimental-zvkgs,-f,-h,-i,-sha,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smdbltrp,-smepmp,-smmpm,-smnpm,-smrnmi,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssdbltrp,-ssnpm,-sspm,-ssqosid,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-supm,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-svvptc,-v,-xcheri-norvc,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xmipscmove,-xmipslsp,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zacas,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b" } +attributes #2 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) "cheri-compartment"="qoi_decode" "no-builtin-longjmp" "no-builtin-printf" "no-builtin-setjmp" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cheriot" "target-features"="+32bit,+c,+e,+m,+relax,+unaligned-scalar-mem,+xcheri,+xcheriot,+zmmul,-a,-b,-d,-experimental-sdext,-experimental-sdtrig,-experimental-smctr,-experimental-ssctr,-experimental-svukte,-experimental-xqcia,-experimental-xqciac,-experimental-xqcicli,-experimental-xqcicm,-experimental-xqcics,-experimental-xqcicsr,-experimental-xqciint,-experimental-xqcilo,-experimental-xqcilsm,-experimental-xqcisls,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-experimental-zvbc32e,-experimental-zvkgs,-f,-h,-i,-sha,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smdbltrp,-smepmp,-smmpm,-smnpm,-smrnmi,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssdbltrp,-ssnpm,-sspm,-ssqosid,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-supm,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-svvptc,-v,-xcheri-norvc,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xmipscmove,-xmipslsp,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zacas,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b" } +attributes #3 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) } + +!llvm.linker.options = !{} +!llvm.module.flags = !{!0, !1, !2, !4} +!llvm.ident = !{!5} + +!0 = !{i32 1, !"wchar_size", i32 2} +!1 = !{i32 1, !"target-abi", !"cheriot"} +!2 = !{i32 6, !"riscv-isa", !3} +!3 = !{!"rv32e2p0_m2p0_c2p0_zmmul1p0_xcheri0p0_xcheriot1p0"} +!4 = !{i32 8, !"SmallDataLimit", i32 0} +!5 = !{!"clang version 20.1.3 (git@github.com:resistor/llvm-project-1.git 5d99e3d52791d33f0f90e0e0d91152433e1cfba3)"} +!6 = !{!7, !7, i64 0} +!7 = !{!"double", !8, i64 0} +!8 = !{!"omnipotent char", !9, i64 0} +!9 = !{!"Simple C++ TBAA"} +!10 = !{!11, !7, i64 0} +!11 = !{!"_ZTS10TwoDoubles", !7, i64 0, !7, i64 8} +!12 = !{!11, !7, i64 8}