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}