1e8d8bef9SDimitry Andric //===- PPCRegisterBankInfo.cpp --------------------------------------------===// 2e8d8bef9SDimitry Andric // 3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e8d8bef9SDimitry Andric // 7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8e8d8bef9SDimitry Andric /// \file 9e8d8bef9SDimitry Andric /// This file implements the targeting of the RegisterBankInfo class for 10e8d8bef9SDimitry Andric /// PowerPC. 11e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 12e8d8bef9SDimitry Andric 13e8d8bef9SDimitry Andric #include "PPCRegisterBankInfo.h" 14e8d8bef9SDimitry Andric #include "PPCRegisterInfo.h" 15e8d8bef9SDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 16e8d8bef9SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 17e8d8bef9SDimitry Andric #include "llvm/Support/Debug.h" 18e8d8bef9SDimitry Andric 19e8d8bef9SDimitry Andric #define DEBUG_TYPE "ppc-reg-bank-info" 20e8d8bef9SDimitry Andric 21e8d8bef9SDimitry Andric #define GET_TARGET_REGBANK_IMPL 22e8d8bef9SDimitry Andric #include "PPCGenRegisterBank.inc" 23e8d8bef9SDimitry Andric 24*bdd1243dSDimitry Andric // This file will be TableGen'ed at some point. 25*bdd1243dSDimitry Andric #include "PPCGenRegisterBankInfo.def" 26*bdd1243dSDimitry Andric 27e8d8bef9SDimitry Andric using namespace llvm; 28e8d8bef9SDimitry Andric 2981ad6265SDimitry Andric PPCRegisterBankInfo::PPCRegisterBankInfo(const TargetRegisterInfo &TRI) {} 30*bdd1243dSDimitry Andric 31*bdd1243dSDimitry Andric const RegisterBank & 32*bdd1243dSDimitry Andric PPCRegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC, 33*bdd1243dSDimitry Andric LLT Ty) const { 34*bdd1243dSDimitry Andric switch (RC.getID()) { 35*bdd1243dSDimitry Andric case PPC::G8RCRegClassID: 36*bdd1243dSDimitry Andric case PPC::G8RC_NOX0RegClassID: 37*bdd1243dSDimitry Andric case PPC::G8RC_and_G8RC_NOX0RegClassID: 38*bdd1243dSDimitry Andric case PPC::GPRCRegClassID: 39*bdd1243dSDimitry Andric case PPC::GPRC_NOR0RegClassID: 40*bdd1243dSDimitry Andric case PPC::GPRC_and_GPRC_NOR0RegClassID: 41*bdd1243dSDimitry Andric return getRegBank(PPC::GPRRegBankID); 42*bdd1243dSDimitry Andric case PPC::VSFRCRegClassID: 43*bdd1243dSDimitry Andric case PPC::SPILLTOVSRRC_and_VSFRCRegClassID: 44*bdd1243dSDimitry Andric case PPC::SPILLTOVSRRC_and_VFRCRegClassID: 45*bdd1243dSDimitry Andric case PPC::SPILLTOVSRRC_and_F4RCRegClassID: 46*bdd1243dSDimitry Andric case PPC::F8RCRegClassID: 47*bdd1243dSDimitry Andric case PPC::VFRCRegClassID: 48*bdd1243dSDimitry Andric case PPC::VSSRCRegClassID: 49*bdd1243dSDimitry Andric case PPC::F4RCRegClassID: 50*bdd1243dSDimitry Andric return getRegBank(PPC::FPRRegBankID); 51*bdd1243dSDimitry Andric case PPC::CRRCRegClassID: 52*bdd1243dSDimitry Andric case PPC::CRBITRCRegClassID: 53*bdd1243dSDimitry Andric return getRegBank(PPC::CRRegBankID); 54*bdd1243dSDimitry Andric default: 55*bdd1243dSDimitry Andric llvm_unreachable("Unexpected register class"); 56*bdd1243dSDimitry Andric } 57*bdd1243dSDimitry Andric } 58*bdd1243dSDimitry Andric 59*bdd1243dSDimitry Andric const RegisterBankInfo::InstructionMapping & 60*bdd1243dSDimitry Andric PPCRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { 61*bdd1243dSDimitry Andric const unsigned Opc = MI.getOpcode(); 62*bdd1243dSDimitry Andric 63*bdd1243dSDimitry Andric // Try the default logic for non-generic instructions that are either copies 64*bdd1243dSDimitry Andric // or already have some operands assigned to banks. 65*bdd1243dSDimitry Andric if (!isPreISelGenericOpcode(Opc) || Opc == TargetOpcode::G_PHI) { 66*bdd1243dSDimitry Andric const RegisterBankInfo::InstructionMapping &Mapping = 67*bdd1243dSDimitry Andric getInstrMappingImpl(MI); 68*bdd1243dSDimitry Andric if (Mapping.isValid()) 69*bdd1243dSDimitry Andric return Mapping; 70*bdd1243dSDimitry Andric } 71*bdd1243dSDimitry Andric 72*bdd1243dSDimitry Andric const MachineFunction &MF = *MI.getParent()->getParent(); 73*bdd1243dSDimitry Andric const MachineRegisterInfo &MRI = MF.getRegInfo(); 74*bdd1243dSDimitry Andric const TargetSubtargetInfo &STI = MF.getSubtarget(); 75*bdd1243dSDimitry Andric const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); 76*bdd1243dSDimitry Andric 77*bdd1243dSDimitry Andric unsigned NumOperands = MI.getNumOperands(); 78*bdd1243dSDimitry Andric const ValueMapping *OperandsMapping = nullptr; 79*bdd1243dSDimitry Andric unsigned Cost = 1; 80*bdd1243dSDimitry Andric unsigned MappingID = DefaultMappingID; 81*bdd1243dSDimitry Andric 82*bdd1243dSDimitry Andric switch (Opc) { 83*bdd1243dSDimitry Andric // Arithmetic ops. 84*bdd1243dSDimitry Andric case TargetOpcode::G_ADD: 85*bdd1243dSDimitry Andric case TargetOpcode::G_SUB: 86*bdd1243dSDimitry Andric // Bitwise ops. 87*bdd1243dSDimitry Andric case TargetOpcode::G_AND: 88*bdd1243dSDimitry Andric case TargetOpcode::G_OR: 89*bdd1243dSDimitry Andric case TargetOpcode::G_XOR: 90*bdd1243dSDimitry Andric // Extension ops. 91*bdd1243dSDimitry Andric case TargetOpcode::G_SEXT: 92*bdd1243dSDimitry Andric case TargetOpcode::G_ZEXT: 93*bdd1243dSDimitry Andric case TargetOpcode::G_ANYEXT: 94*bdd1243dSDimitry Andric assert(NumOperands <= 3 && 95*bdd1243dSDimitry Andric "This code is for instructions with 3 or less operands"); 96*bdd1243dSDimitry Andric OperandsMapping = getValueMapping(PMI_GPR64); 97*bdd1243dSDimitry Andric break; 98*bdd1243dSDimitry Andric case TargetOpcode::G_FADD: 99*bdd1243dSDimitry Andric case TargetOpcode::G_FSUB: 100*bdd1243dSDimitry Andric case TargetOpcode::G_FMUL: 101*bdd1243dSDimitry Andric case TargetOpcode::G_FDIV: { 102*bdd1243dSDimitry Andric Register SrcReg = MI.getOperand(1).getReg(); 103*bdd1243dSDimitry Andric unsigned Size = getSizeInBits(SrcReg, MRI, TRI); 104*bdd1243dSDimitry Andric 105*bdd1243dSDimitry Andric assert((Size == 32 || Size == 64) && "Unsupported floating point types!\n"); 106*bdd1243dSDimitry Andric OperandsMapping = getValueMapping(Size == 32 ? PMI_FPR32 : PMI_FPR64); 107*bdd1243dSDimitry Andric break; 108*bdd1243dSDimitry Andric } 109*bdd1243dSDimitry Andric case TargetOpcode::G_FCMP: { 110*bdd1243dSDimitry Andric unsigned CmpSize = MRI.getType(MI.getOperand(2).getReg()).getSizeInBits(); 111*bdd1243dSDimitry Andric 112*bdd1243dSDimitry Andric OperandsMapping = getOperandsMapping( 113*bdd1243dSDimitry Andric {getValueMapping(PMI_CR), nullptr, 114*bdd1243dSDimitry Andric getValueMapping(CmpSize == 32 ? PMI_FPR32 : PMI_FPR64), 115*bdd1243dSDimitry Andric getValueMapping(CmpSize == 32 ? PMI_FPR32 : PMI_FPR64)}); 116*bdd1243dSDimitry Andric break; 117*bdd1243dSDimitry Andric } 118*bdd1243dSDimitry Andric case TargetOpcode::G_CONSTANT: 119*bdd1243dSDimitry Andric OperandsMapping = getOperandsMapping({getValueMapping(PMI_GPR64), nullptr}); 120*bdd1243dSDimitry Andric break; 121*bdd1243dSDimitry Andric case TargetOpcode::G_FPTOUI: 122*bdd1243dSDimitry Andric case TargetOpcode::G_FPTOSI: { 123*bdd1243dSDimitry Andric Register SrcReg = MI.getOperand(1).getReg(); 124*bdd1243dSDimitry Andric unsigned Size = getSizeInBits(SrcReg, MRI, TRI); 125*bdd1243dSDimitry Andric 126*bdd1243dSDimitry Andric OperandsMapping = getOperandsMapping( 127*bdd1243dSDimitry Andric {getValueMapping(PMI_GPR64), 128*bdd1243dSDimitry Andric getValueMapping(Size == 32 ? PMI_FPR32 : PMI_FPR64)}); 129*bdd1243dSDimitry Andric break; 130*bdd1243dSDimitry Andric } 131*bdd1243dSDimitry Andric case TargetOpcode::G_UITOFP: 132*bdd1243dSDimitry Andric case TargetOpcode::G_SITOFP: { 133*bdd1243dSDimitry Andric Register SrcReg = MI.getOperand(0).getReg(); 134*bdd1243dSDimitry Andric unsigned Size = getSizeInBits(SrcReg, MRI, TRI); 135*bdd1243dSDimitry Andric 136*bdd1243dSDimitry Andric OperandsMapping = 137*bdd1243dSDimitry Andric getOperandsMapping({getValueMapping(Size == 32 ? PMI_FPR32 : PMI_FPR64), 138*bdd1243dSDimitry Andric getValueMapping(PMI_GPR64)}); 139*bdd1243dSDimitry Andric break; 140*bdd1243dSDimitry Andric } 141*bdd1243dSDimitry Andric case TargetOpcode::G_LOAD: { 142*bdd1243dSDimitry Andric unsigned Size = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); 143*bdd1243dSDimitry Andric // Check if that load feeds fp instructions. 144*bdd1243dSDimitry Andric if (any_of(MRI.use_nodbg_instructions(MI.getOperand(0).getReg()), 145*bdd1243dSDimitry Andric [&](const MachineInstr &UseMI) { 146*bdd1243dSDimitry Andric // If we have at least one direct use in a FP instruction, 147*bdd1243dSDimitry Andric // assume this was a floating point load in the IR. If it was 148*bdd1243dSDimitry Andric // not, we would have had a bitcast before reaching that 149*bdd1243dSDimitry Andric // instruction. 150*bdd1243dSDimitry Andric // 151*bdd1243dSDimitry Andric // Int->FP conversion operations are also captured in 152*bdd1243dSDimitry Andric // onlyDefinesFP(). 153*bdd1243dSDimitry Andric return onlyUsesFP(UseMI, MRI, TRI); 154*bdd1243dSDimitry Andric })) 155*bdd1243dSDimitry Andric OperandsMapping = getOperandsMapping( 156*bdd1243dSDimitry Andric {getValueMapping(Size == 64 ? PMI_FPR64 : PMI_FPR32), 157*bdd1243dSDimitry Andric getValueMapping(PMI_GPR64)}); 158*bdd1243dSDimitry Andric else 159*bdd1243dSDimitry Andric OperandsMapping = getOperandsMapping( 160*bdd1243dSDimitry Andric {getValueMapping(Size == 64 ? PMI_GPR64 : PMI_GPR32), 161*bdd1243dSDimitry Andric getValueMapping(PMI_GPR64)}); 162*bdd1243dSDimitry Andric break; 163*bdd1243dSDimitry Andric } 164*bdd1243dSDimitry Andric case TargetOpcode::G_STORE: { 165*bdd1243dSDimitry Andric // Check if the store is fed by fp instructions. 166*bdd1243dSDimitry Andric MachineInstr *DefMI = MRI.getVRegDef(MI.getOperand(0).getReg()); 167*bdd1243dSDimitry Andric unsigned Size = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); 168*bdd1243dSDimitry Andric if (onlyDefinesFP(*DefMI, MRI, TRI)) 169*bdd1243dSDimitry Andric OperandsMapping = getOperandsMapping( 170*bdd1243dSDimitry Andric {getValueMapping(Size == 64 ? PMI_FPR64 : PMI_FPR32), 171*bdd1243dSDimitry Andric getValueMapping(PMI_GPR64)}); 172*bdd1243dSDimitry Andric else 173*bdd1243dSDimitry Andric OperandsMapping = getOperandsMapping( 174*bdd1243dSDimitry Andric {getValueMapping(Size == 64 ? PMI_GPR64 : PMI_GPR32), 175*bdd1243dSDimitry Andric getValueMapping(PMI_GPR64)}); 176*bdd1243dSDimitry Andric break; 177*bdd1243dSDimitry Andric } 178*bdd1243dSDimitry Andric case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: { 179*bdd1243dSDimitry Andric // FIXME: We have to check every operand in this MI and compute value 180*bdd1243dSDimitry Andric // mapping accordingly. 181*bdd1243dSDimitry Andric SmallVector<const ValueMapping *, 8> OpdsMapping(NumOperands); 182*bdd1243dSDimitry Andric OperandsMapping = getOperandsMapping(OpdsMapping); 183*bdd1243dSDimitry Andric break; 184*bdd1243dSDimitry Andric } 185*bdd1243dSDimitry Andric default: 186*bdd1243dSDimitry Andric return getInvalidInstructionMapping(); 187*bdd1243dSDimitry Andric } 188*bdd1243dSDimitry Andric 189*bdd1243dSDimitry Andric return getInstructionMapping(MappingID, Cost, OperandsMapping, NumOperands); 190*bdd1243dSDimitry Andric } 191*bdd1243dSDimitry Andric 192*bdd1243dSDimitry Andric /// Returns whether opcode \p Opc is a pre-isel generic floating-point opcode, 193*bdd1243dSDimitry Andric /// having only floating-point operands. 194*bdd1243dSDimitry Andric /// FIXME: this is copied from target AArch64. Needs some code refactor here to 195*bdd1243dSDimitry Andric /// put this function in GlobalISel/Utils.cpp. 196*bdd1243dSDimitry Andric static bool isPreISelGenericFloatingPointOpcode(unsigned Opc) { 197*bdd1243dSDimitry Andric switch (Opc) { 198*bdd1243dSDimitry Andric case TargetOpcode::G_FADD: 199*bdd1243dSDimitry Andric case TargetOpcode::G_FSUB: 200*bdd1243dSDimitry Andric case TargetOpcode::G_FMUL: 201*bdd1243dSDimitry Andric case TargetOpcode::G_FMA: 202*bdd1243dSDimitry Andric case TargetOpcode::G_FDIV: 203*bdd1243dSDimitry Andric case TargetOpcode::G_FCONSTANT: 204*bdd1243dSDimitry Andric case TargetOpcode::G_FPEXT: 205*bdd1243dSDimitry Andric case TargetOpcode::G_FPTRUNC: 206*bdd1243dSDimitry Andric case TargetOpcode::G_FCEIL: 207*bdd1243dSDimitry Andric case TargetOpcode::G_FFLOOR: 208*bdd1243dSDimitry Andric case TargetOpcode::G_FNEARBYINT: 209*bdd1243dSDimitry Andric case TargetOpcode::G_FNEG: 210*bdd1243dSDimitry Andric case TargetOpcode::G_FCOS: 211*bdd1243dSDimitry Andric case TargetOpcode::G_FSIN: 212*bdd1243dSDimitry Andric case TargetOpcode::G_FLOG10: 213*bdd1243dSDimitry Andric case TargetOpcode::G_FLOG: 214*bdd1243dSDimitry Andric case TargetOpcode::G_FLOG2: 215*bdd1243dSDimitry Andric case TargetOpcode::G_FSQRT: 216*bdd1243dSDimitry Andric case TargetOpcode::G_FABS: 217*bdd1243dSDimitry Andric case TargetOpcode::G_FEXP: 218*bdd1243dSDimitry Andric case TargetOpcode::G_FRINT: 219*bdd1243dSDimitry Andric case TargetOpcode::G_INTRINSIC_TRUNC: 220*bdd1243dSDimitry Andric case TargetOpcode::G_INTRINSIC_ROUND: 221*bdd1243dSDimitry Andric case TargetOpcode::G_FMAXNUM: 222*bdd1243dSDimitry Andric case TargetOpcode::G_FMINNUM: 223*bdd1243dSDimitry Andric case TargetOpcode::G_FMAXIMUM: 224*bdd1243dSDimitry Andric case TargetOpcode::G_FMINIMUM: 225*bdd1243dSDimitry Andric return true; 226*bdd1243dSDimitry Andric } 227*bdd1243dSDimitry Andric return false; 228*bdd1243dSDimitry Andric } 229*bdd1243dSDimitry Andric 230*bdd1243dSDimitry Andric /// \returns true if a given intrinsic \p ID only uses and defines FPRs. 231*bdd1243dSDimitry Andric static bool isFPIntrinsic(unsigned ID) { 232*bdd1243dSDimitry Andric // TODO: Add more intrinsics. 233*bdd1243dSDimitry Andric return false; 234*bdd1243dSDimitry Andric } 235*bdd1243dSDimitry Andric 236*bdd1243dSDimitry Andric /// FIXME: this is copied from target AArch64. Needs some code refactor here to 237*bdd1243dSDimitry Andric /// put this function in class RegisterBankInfo. 238*bdd1243dSDimitry Andric bool PPCRegisterBankInfo::hasFPConstraints(const MachineInstr &MI, 239*bdd1243dSDimitry Andric const MachineRegisterInfo &MRI, 240*bdd1243dSDimitry Andric const TargetRegisterInfo &TRI, 241*bdd1243dSDimitry Andric unsigned Depth) const { 242*bdd1243dSDimitry Andric unsigned Op = MI.getOpcode(); 243*bdd1243dSDimitry Andric if (Op == TargetOpcode::G_INTRINSIC && isFPIntrinsic(MI.getIntrinsicID())) 244*bdd1243dSDimitry Andric return true; 245*bdd1243dSDimitry Andric 246*bdd1243dSDimitry Andric // Do we have an explicit floating point instruction? 247*bdd1243dSDimitry Andric if (isPreISelGenericFloatingPointOpcode(Op)) 248*bdd1243dSDimitry Andric return true; 249*bdd1243dSDimitry Andric 250*bdd1243dSDimitry Andric // No. Check if we have a copy-like instruction. If we do, then we could 251*bdd1243dSDimitry Andric // still be fed by floating point instructions. 252*bdd1243dSDimitry Andric if (Op != TargetOpcode::COPY && !MI.isPHI() && 253*bdd1243dSDimitry Andric !isPreISelGenericOptimizationHint(Op)) 254*bdd1243dSDimitry Andric return false; 255*bdd1243dSDimitry Andric 256*bdd1243dSDimitry Andric // Check if we already know the register bank. 257*bdd1243dSDimitry Andric auto *RB = getRegBank(MI.getOperand(0).getReg(), MRI, TRI); 258*bdd1243dSDimitry Andric if (RB == &PPC::FPRRegBank) 259*bdd1243dSDimitry Andric return true; 260*bdd1243dSDimitry Andric if (RB == &PPC::GPRRegBank) 261*bdd1243dSDimitry Andric return false; 262*bdd1243dSDimitry Andric 263*bdd1243dSDimitry Andric // We don't know anything. 264*bdd1243dSDimitry Andric // 265*bdd1243dSDimitry Andric // If we have a phi, we may be able to infer that it will be assigned a FPR 266*bdd1243dSDimitry Andric // based off of its inputs. 267*bdd1243dSDimitry Andric if (!MI.isPHI() || Depth > MaxFPRSearchDepth) 268*bdd1243dSDimitry Andric return false; 269*bdd1243dSDimitry Andric 270*bdd1243dSDimitry Andric return any_of(MI.explicit_uses(), [&](const MachineOperand &Op) { 271*bdd1243dSDimitry Andric return Op.isReg() && 272*bdd1243dSDimitry Andric onlyDefinesFP(*MRI.getVRegDef(Op.getReg()), MRI, TRI, Depth + 1); 273*bdd1243dSDimitry Andric }); 274*bdd1243dSDimitry Andric } 275*bdd1243dSDimitry Andric 276*bdd1243dSDimitry Andric /// FIXME: this is copied from target AArch64. Needs some code refactor here to 277*bdd1243dSDimitry Andric /// put this function in class RegisterBankInfo. 278*bdd1243dSDimitry Andric bool PPCRegisterBankInfo::onlyUsesFP(const MachineInstr &MI, 279*bdd1243dSDimitry Andric const MachineRegisterInfo &MRI, 280*bdd1243dSDimitry Andric const TargetRegisterInfo &TRI, 281*bdd1243dSDimitry Andric unsigned Depth) const { 282*bdd1243dSDimitry Andric switch (MI.getOpcode()) { 283*bdd1243dSDimitry Andric case TargetOpcode::G_FPTOSI: 284*bdd1243dSDimitry Andric case TargetOpcode::G_FPTOUI: 285*bdd1243dSDimitry Andric case TargetOpcode::G_FCMP: 286*bdd1243dSDimitry Andric case TargetOpcode::G_LROUND: 287*bdd1243dSDimitry Andric case TargetOpcode::G_LLROUND: 288*bdd1243dSDimitry Andric return true; 289*bdd1243dSDimitry Andric default: 290*bdd1243dSDimitry Andric break; 291*bdd1243dSDimitry Andric } 292*bdd1243dSDimitry Andric return hasFPConstraints(MI, MRI, TRI, Depth); 293*bdd1243dSDimitry Andric } 294*bdd1243dSDimitry Andric 295*bdd1243dSDimitry Andric /// FIXME: this is copied from target AArch64. Needs some code refactor here to 296*bdd1243dSDimitry Andric /// put this function in class RegisterBankInfo. 297*bdd1243dSDimitry Andric bool PPCRegisterBankInfo::onlyDefinesFP(const MachineInstr &MI, 298*bdd1243dSDimitry Andric const MachineRegisterInfo &MRI, 299*bdd1243dSDimitry Andric const TargetRegisterInfo &TRI, 300*bdd1243dSDimitry Andric unsigned Depth) const { 301*bdd1243dSDimitry Andric switch (MI.getOpcode()) { 302*bdd1243dSDimitry Andric case TargetOpcode::G_SITOFP: 303*bdd1243dSDimitry Andric case TargetOpcode::G_UITOFP: 304*bdd1243dSDimitry Andric return true; 305*bdd1243dSDimitry Andric default: 306*bdd1243dSDimitry Andric break; 307*bdd1243dSDimitry Andric } 308*bdd1243dSDimitry Andric return hasFPConstraints(MI, MRI, TRI, Depth); 309*bdd1243dSDimitry Andric } 310*bdd1243dSDimitry Andric 311*bdd1243dSDimitry Andric RegisterBankInfo::InstructionMappings 312*bdd1243dSDimitry Andric PPCRegisterBankInfo::getInstrAlternativeMappings(const MachineInstr &MI) const { 313*bdd1243dSDimitry Andric // TODO Implement. 314*bdd1243dSDimitry Andric return RegisterBankInfo::getInstrAlternativeMappings(MI); 315*bdd1243dSDimitry Andric } 316