1*480093f4SDimitry Andric //===-- VEFrameLowering.cpp - VE Frame Information ------------------------===// 2*480093f4SDimitry Andric // 3*480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*480093f4SDimitry Andric // 7*480093f4SDimitry Andric //===----------------------------------------------------------------------===// 8*480093f4SDimitry Andric // 9*480093f4SDimitry Andric // This file contains the VE implementation of TargetFrameLowering class. 10*480093f4SDimitry Andric // 11*480093f4SDimitry Andric //===----------------------------------------------------------------------===// 12*480093f4SDimitry Andric 13*480093f4SDimitry Andric #include "VEFrameLowering.h" 14*480093f4SDimitry Andric #include "VEInstrInfo.h" 15*480093f4SDimitry Andric #include "VESubtarget.h" 16*480093f4SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h" 17*480093f4SDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 18*480093f4SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 19*480093f4SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 20*480093f4SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 21*480093f4SDimitry Andric #include "llvm/CodeGen/RegisterScavenging.h" 22*480093f4SDimitry Andric #include "llvm/IR/DataLayout.h" 23*480093f4SDimitry Andric #include "llvm/IR/Function.h" 24*480093f4SDimitry Andric #include "llvm/Support/CommandLine.h" 25*480093f4SDimitry Andric #include "llvm/Target/TargetOptions.h" 26*480093f4SDimitry Andric #include "llvm/Support/MathExtras.h" 27*480093f4SDimitry Andric 28*480093f4SDimitry Andric using namespace llvm; 29*480093f4SDimitry Andric 30*480093f4SDimitry Andric VEFrameLowering::VEFrameLowering(const VESubtarget &ST) 31*480093f4SDimitry Andric : TargetFrameLowering(TargetFrameLowering::StackGrowsDown, Align(16), 0, 32*480093f4SDimitry Andric Align(16)) {} 33*480093f4SDimitry Andric 34*480093f4SDimitry Andric void VEFrameLowering::emitPrologueInsns(MachineFunction &MF, 35*480093f4SDimitry Andric MachineBasicBlock &MBB, 36*480093f4SDimitry Andric MachineBasicBlock::iterator MBBI, 37*480093f4SDimitry Andric int NumBytes, 38*480093f4SDimitry Andric bool RequireFPUpdate) const { 39*480093f4SDimitry Andric 40*480093f4SDimitry Andric DebugLoc dl; 41*480093f4SDimitry Andric const VEInstrInfo &TII = 42*480093f4SDimitry Andric *static_cast<const VEInstrInfo *>(MF.getSubtarget().getInstrInfo()); 43*480093f4SDimitry Andric // Insert following codes here as prologue 44*480093f4SDimitry Andric // 45*480093f4SDimitry Andric // st %fp, 0(,%sp) 46*480093f4SDimitry Andric // st %lr, 8(,%sp) 47*480093f4SDimitry Andric // st %got, 24(,%sp) 48*480093f4SDimitry Andric // st %plt, 32(,%sp) 49*480093f4SDimitry Andric // or %fp, 0, %sp 50*480093f4SDimitry Andric 51*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::STSri)) 52*480093f4SDimitry Andric .addReg(VE::SX11) 53*480093f4SDimitry Andric .addImm(0) 54*480093f4SDimitry Andric .addReg(VE::SX9); 55*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::STSri)) 56*480093f4SDimitry Andric .addReg(VE::SX11) 57*480093f4SDimitry Andric .addImm(8) 58*480093f4SDimitry Andric .addReg(VE::SX10); 59*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::STSri)) 60*480093f4SDimitry Andric .addReg(VE::SX11) 61*480093f4SDimitry Andric .addImm(24) 62*480093f4SDimitry Andric .addReg(VE::SX15); 63*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::STSri)) 64*480093f4SDimitry Andric .addReg(VE::SX11) 65*480093f4SDimitry Andric .addImm(32) 66*480093f4SDimitry Andric .addReg(VE::SX16); 67*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::ORri), VE::SX9) 68*480093f4SDimitry Andric .addReg(VE::SX11) 69*480093f4SDimitry Andric .addImm(0); 70*480093f4SDimitry Andric } 71*480093f4SDimitry Andric 72*480093f4SDimitry Andric void VEFrameLowering::emitEpilogueInsns(MachineFunction &MF, 73*480093f4SDimitry Andric MachineBasicBlock &MBB, 74*480093f4SDimitry Andric MachineBasicBlock::iterator MBBI, 75*480093f4SDimitry Andric int NumBytes, 76*480093f4SDimitry Andric bool RequireFPUpdate) const { 77*480093f4SDimitry Andric 78*480093f4SDimitry Andric DebugLoc dl; 79*480093f4SDimitry Andric const VEInstrInfo &TII = 80*480093f4SDimitry Andric *static_cast<const VEInstrInfo *>(MF.getSubtarget().getInstrInfo()); 81*480093f4SDimitry Andric // Insert following codes here as epilogue 82*480093f4SDimitry Andric // 83*480093f4SDimitry Andric // or %sp, 0, %fp 84*480093f4SDimitry Andric // ld %got, 32(,%sp) 85*480093f4SDimitry Andric // ld %plt, 24(,%sp) 86*480093f4SDimitry Andric // ld %lr, 8(,%sp) 87*480093f4SDimitry Andric // ld %fp, 0(,%sp) 88*480093f4SDimitry Andric 89*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::ORri), VE::SX11) 90*480093f4SDimitry Andric .addReg(VE::SX9) 91*480093f4SDimitry Andric .addImm(0); 92*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::LDSri), VE::SX16) 93*480093f4SDimitry Andric .addReg(VE::SX11) 94*480093f4SDimitry Andric .addImm(32); 95*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::LDSri), VE::SX15) 96*480093f4SDimitry Andric .addReg(VE::SX11) 97*480093f4SDimitry Andric .addImm(24); 98*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::LDSri), VE::SX10) 99*480093f4SDimitry Andric .addReg(VE::SX11) 100*480093f4SDimitry Andric .addImm(8); 101*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::LDSri), VE::SX9) 102*480093f4SDimitry Andric .addReg(VE::SX11) 103*480093f4SDimitry Andric .addImm(0); 104*480093f4SDimitry Andric } 105*480093f4SDimitry Andric 106*480093f4SDimitry Andric void VEFrameLowering::emitSPAdjustment(MachineFunction &MF, 107*480093f4SDimitry Andric MachineBasicBlock &MBB, 108*480093f4SDimitry Andric MachineBasicBlock::iterator MBBI, 109*480093f4SDimitry Andric int NumBytes) const { 110*480093f4SDimitry Andric DebugLoc dl; 111*480093f4SDimitry Andric const VEInstrInfo &TII = 112*480093f4SDimitry Andric *static_cast<const VEInstrInfo *>(MF.getSubtarget().getInstrInfo()); 113*480093f4SDimitry Andric 114*480093f4SDimitry Andric if (NumBytes >= -64 && NumBytes < 63) { 115*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::ADXri), VE::SX11) 116*480093f4SDimitry Andric .addReg(VE::SX11) 117*480093f4SDimitry Andric .addImm(NumBytes); 118*480093f4SDimitry Andric return; 119*480093f4SDimitry Andric } 120*480093f4SDimitry Andric 121*480093f4SDimitry Andric // Emit following codes. This clobbers SX13 which we always know is 122*480093f4SDimitry Andric // available here. 123*480093f4SDimitry Andric // lea %s13,%lo(NumBytes) 124*480093f4SDimitry Andric // and %s13,%s13,(32)0 125*480093f4SDimitry Andric // lea.sl %sp,%hi(NumBytes)(%sp, %s13) 126*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::LEAzzi), VE::SX13) 127*480093f4SDimitry Andric .addImm(LO32(NumBytes)); 128*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::ANDrm0), VE::SX13) 129*480093f4SDimitry Andric .addReg(VE::SX13) 130*480093f4SDimitry Andric .addImm(32); 131*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::LEASLrri), VE::SX11) 132*480093f4SDimitry Andric .addReg(VE::SX11) 133*480093f4SDimitry Andric .addReg(VE::SX13) 134*480093f4SDimitry Andric .addImm(HI32(NumBytes)); 135*480093f4SDimitry Andric } 136*480093f4SDimitry Andric 137*480093f4SDimitry Andric void VEFrameLowering::emitSPExtend(MachineFunction &MF, MachineBasicBlock &MBB, 138*480093f4SDimitry Andric MachineBasicBlock::iterator MBBI, 139*480093f4SDimitry Andric int NumBytes) const { 140*480093f4SDimitry Andric DebugLoc dl; 141*480093f4SDimitry Andric const VEInstrInfo &TII = 142*480093f4SDimitry Andric *static_cast<const VEInstrInfo *>(MF.getSubtarget().getInstrInfo()); 143*480093f4SDimitry Andric 144*480093f4SDimitry Andric // Emit following codes. It is not possible to insert multiple 145*480093f4SDimitry Andric // BasicBlocks in PEI pass, so we emit two pseudo instructions here. 146*480093f4SDimitry Andric // 147*480093f4SDimitry Andric // EXTEND_STACK // pseudo instrcution 148*480093f4SDimitry Andric // EXTEND_STACK_GUARD // pseudo instrcution 149*480093f4SDimitry Andric // 150*480093f4SDimitry Andric // EXTEND_STACK pseudo will be converted by ExpandPostRA pass into 151*480093f4SDimitry Andric // following instructions with multiple basic blocks later. 152*480093f4SDimitry Andric // 153*480093f4SDimitry Andric // thisBB: 154*480093f4SDimitry Andric // brge.l.t %sp, %sl, sinkBB 155*480093f4SDimitry Andric // syscallBB: 156*480093f4SDimitry Andric // ld %s61, 0x18(, %tp) // load param area 157*480093f4SDimitry Andric // or %s62, 0, %s0 // spill the value of %s0 158*480093f4SDimitry Andric // lea %s63, 0x13b // syscall # of grow 159*480093f4SDimitry Andric // shm.l %s63, 0x0(%s61) // store syscall # at addr:0 160*480093f4SDimitry Andric // shm.l %sl, 0x8(%s61) // store old limit at addr:8 161*480093f4SDimitry Andric // shm.l %sp, 0x10(%s61) // store new limit at addr:16 162*480093f4SDimitry Andric // monc // call monitor 163*480093f4SDimitry Andric // or %s0, 0, %s62 // restore the value of %s0 164*480093f4SDimitry Andric // sinkBB: 165*480093f4SDimitry Andric // 166*480093f4SDimitry Andric // EXTEND_STACK_GUARD pseudo will be simply eliminated by ExpandPostRA 167*480093f4SDimitry Andric // pass. This pseudo is required to be at the next of EXTEND_STACK 168*480093f4SDimitry Andric // pseudo in order to protect iteration loop in ExpandPostRA. 169*480093f4SDimitry Andric 170*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::EXTEND_STACK)); 171*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(VE::EXTEND_STACK_GUARD)); 172*480093f4SDimitry Andric } 173*480093f4SDimitry Andric 174*480093f4SDimitry Andric void VEFrameLowering::emitPrologue(MachineFunction &MF, 175*480093f4SDimitry Andric MachineBasicBlock &MBB) const { 176*480093f4SDimitry Andric assert(&MF.front() == &MBB && "Shrink-wrapping not yet supported"); 177*480093f4SDimitry Andric MachineFrameInfo &MFI = MF.getFrameInfo(); 178*480093f4SDimitry Andric const VESubtarget &Subtarget = MF.getSubtarget<VESubtarget>(); 179*480093f4SDimitry Andric const VEInstrInfo &TII = 180*480093f4SDimitry Andric *static_cast<const VEInstrInfo *>(Subtarget.getInstrInfo()); 181*480093f4SDimitry Andric const VERegisterInfo &RegInfo = 182*480093f4SDimitry Andric *static_cast<const VERegisterInfo *>(Subtarget.getRegisterInfo()); 183*480093f4SDimitry Andric MachineBasicBlock::iterator MBBI = MBB.begin(); 184*480093f4SDimitry Andric // Debug location must be unknown since the first debug location is used 185*480093f4SDimitry Andric // to determine the end of the prologue. 186*480093f4SDimitry Andric DebugLoc dl; 187*480093f4SDimitry Andric bool NeedsStackRealignment = RegInfo.needsStackRealignment(MF); 188*480093f4SDimitry Andric 189*480093f4SDimitry Andric // FIXME: unfortunately, returning false from canRealignStack 190*480093f4SDimitry Andric // actually just causes needsStackRealignment to return false, 191*480093f4SDimitry Andric // rather than reporting an error, as would be sensible. This is 192*480093f4SDimitry Andric // poor, but fixing that bogosity is going to be a large project. 193*480093f4SDimitry Andric // For now, just see if it's lied, and report an error here. 194*480093f4SDimitry Andric if (!NeedsStackRealignment && MFI.getMaxAlignment() > getStackAlignment()) 195*480093f4SDimitry Andric report_fatal_error("Function \"" + Twine(MF.getName()) + 196*480093f4SDimitry Andric "\" required " 197*480093f4SDimitry Andric "stack re-alignment, but LLVM couldn't handle it " 198*480093f4SDimitry Andric "(probably because it has a dynamic alloca)."); 199*480093f4SDimitry Andric 200*480093f4SDimitry Andric // Get the number of bytes to allocate from the FrameInfo 201*480093f4SDimitry Andric int NumBytes = (int)MFI.getStackSize(); 202*480093f4SDimitry Andric // The VE ABI requires a reserved 176-byte area in the user's stack, starting 203*480093f4SDimitry Andric // at %sp + 16. This is for the callee Register Save Area (RSA). 204*480093f4SDimitry Andric // 205*480093f4SDimitry Andric // We therefore need to add that offset to the total stack size 206*480093f4SDimitry Andric // after all the stack objects are placed by 207*480093f4SDimitry Andric // PrologEpilogInserter calculateFrameObjectOffsets. However, since the stack 208*480093f4SDimitry Andric // needs to be aligned *after* the extra size is added, we need to disable 209*480093f4SDimitry Andric // calculateFrameObjectOffsets's built-in stack alignment, by having 210*480093f4SDimitry Andric // targetHandlesStackFrameRounding return true. 211*480093f4SDimitry Andric 212*480093f4SDimitry Andric // Add the extra call frame stack size, if needed. (This is the same 213*480093f4SDimitry Andric // code as in PrologEpilogInserter, but also gets disabled by 214*480093f4SDimitry Andric // targetHandlesStackFrameRounding) 215*480093f4SDimitry Andric if (MFI.adjustsStack() && hasReservedCallFrame(MF)) 216*480093f4SDimitry Andric NumBytes += MFI.getMaxCallFrameSize(); 217*480093f4SDimitry Andric 218*480093f4SDimitry Andric // Adds the VE subtarget-specific spill area to the stack 219*480093f4SDimitry Andric // size. Also ensures target-required alignment. 220*480093f4SDimitry Andric NumBytes = Subtarget.getAdjustedFrameSize(NumBytes); 221*480093f4SDimitry Andric 222*480093f4SDimitry Andric // Finally, ensure that the size is sufficiently aligned for the 223*480093f4SDimitry Andric // data on the stack. 224*480093f4SDimitry Andric if (MFI.getMaxAlignment() > 0) { 225*480093f4SDimitry Andric NumBytes = alignTo(NumBytes, MFI.getMaxAlignment()); 226*480093f4SDimitry Andric } 227*480093f4SDimitry Andric 228*480093f4SDimitry Andric // Update stack size with corrected value. 229*480093f4SDimitry Andric MFI.setStackSize(NumBytes); 230*480093f4SDimitry Andric 231*480093f4SDimitry Andric // Emit Prologue instructions to save %lr 232*480093f4SDimitry Andric emitPrologueInsns(MF, MBB, MBBI, NumBytes, true); 233*480093f4SDimitry Andric 234*480093f4SDimitry Andric // Emit stack adjust instructions 235*480093f4SDimitry Andric emitSPAdjustment(MF, MBB, MBBI, -NumBytes); 236*480093f4SDimitry Andric 237*480093f4SDimitry Andric // Emit stack extend instructions 238*480093f4SDimitry Andric emitSPExtend(MF, MBB, MBBI, -NumBytes); 239*480093f4SDimitry Andric 240*480093f4SDimitry Andric unsigned regFP = RegInfo.getDwarfRegNum(VE::SX9, true); 241*480093f4SDimitry Andric 242*480093f4SDimitry Andric // Emit ".cfi_def_cfa_register 30". 243*480093f4SDimitry Andric unsigned CFIIndex = 244*480093f4SDimitry Andric MF.addFrameInst(MCCFIInstruction::createDefCfaRegister(nullptr, regFP)); 245*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) 246*480093f4SDimitry Andric .addCFIIndex(CFIIndex); 247*480093f4SDimitry Andric 248*480093f4SDimitry Andric // Emit ".cfi_window_save". 249*480093f4SDimitry Andric CFIIndex = MF.addFrameInst(MCCFIInstruction::createWindowSave(nullptr)); 250*480093f4SDimitry Andric BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) 251*480093f4SDimitry Andric .addCFIIndex(CFIIndex); 252*480093f4SDimitry Andric } 253*480093f4SDimitry Andric 254*480093f4SDimitry Andric MachineBasicBlock::iterator VEFrameLowering::eliminateCallFramePseudoInstr( 255*480093f4SDimitry Andric MachineFunction &MF, MachineBasicBlock &MBB, 256*480093f4SDimitry Andric MachineBasicBlock::iterator I) const { 257*480093f4SDimitry Andric if (!hasReservedCallFrame(MF)) { 258*480093f4SDimitry Andric MachineInstr &MI = *I; 259*480093f4SDimitry Andric int Size = MI.getOperand(0).getImm(); 260*480093f4SDimitry Andric if (MI.getOpcode() == VE::ADJCALLSTACKDOWN) 261*480093f4SDimitry Andric Size = -Size; 262*480093f4SDimitry Andric 263*480093f4SDimitry Andric if (Size) 264*480093f4SDimitry Andric emitSPAdjustment(MF, MBB, I, Size); 265*480093f4SDimitry Andric } 266*480093f4SDimitry Andric return MBB.erase(I); 267*480093f4SDimitry Andric } 268*480093f4SDimitry Andric 269*480093f4SDimitry Andric void VEFrameLowering::emitEpilogue(MachineFunction &MF, 270*480093f4SDimitry Andric MachineBasicBlock &MBB) const { 271*480093f4SDimitry Andric MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); 272*480093f4SDimitry Andric DebugLoc dl = MBBI->getDebugLoc(); 273*480093f4SDimitry Andric MachineFrameInfo &MFI = MF.getFrameInfo(); 274*480093f4SDimitry Andric 275*480093f4SDimitry Andric int NumBytes = (int)MFI.getStackSize(); 276*480093f4SDimitry Andric 277*480093f4SDimitry Andric // Emit Epilogue instructions to restore %lr 278*480093f4SDimitry Andric emitEpilogueInsns(MF, MBB, MBBI, NumBytes, true); 279*480093f4SDimitry Andric } 280*480093f4SDimitry Andric 281*480093f4SDimitry Andric bool VEFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { 282*480093f4SDimitry Andric // Reserve call frame if there are no variable sized objects on the stack. 283*480093f4SDimitry Andric return !MF.getFrameInfo().hasVarSizedObjects(); 284*480093f4SDimitry Andric } 285*480093f4SDimitry Andric 286*480093f4SDimitry Andric // hasFP - Return true if the specified function should have a dedicated frame 287*480093f4SDimitry Andric // pointer register. This is true if the function has variable sized allocas or 288*480093f4SDimitry Andric // if frame pointer elimination is disabled. 289*480093f4SDimitry Andric bool VEFrameLowering::hasFP(const MachineFunction &MF) const { 290*480093f4SDimitry Andric const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); 291*480093f4SDimitry Andric 292*480093f4SDimitry Andric const MachineFrameInfo &MFI = MF.getFrameInfo(); 293*480093f4SDimitry Andric return MF.getTarget().Options.DisableFramePointerElim(MF) || 294*480093f4SDimitry Andric RegInfo->needsStackRealignment(MF) || MFI.hasVarSizedObjects() || 295*480093f4SDimitry Andric MFI.isFrameAddressTaken(); 296*480093f4SDimitry Andric } 297*480093f4SDimitry Andric 298*480093f4SDimitry Andric int VEFrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI, 299*480093f4SDimitry Andric unsigned &FrameReg) const { 300*480093f4SDimitry Andric // Addressable stack objects are accessed using neg. offsets from 301*480093f4SDimitry Andric // %fp, or positive offsets from %sp. 302*480093f4SDimitry Andric int64_t FrameOffset = MF.getFrameInfo().getObjectOffset(FI); 303*480093f4SDimitry Andric FrameReg = VE::SX11; // %sp 304*480093f4SDimitry Andric return FrameOffset + MF.getFrameInfo().getStackSize(); 305*480093f4SDimitry Andric } 306*480093f4SDimitry Andric 307*480093f4SDimitry Andric bool VEFrameLowering::isLeafProc(MachineFunction &MF) const { 308*480093f4SDimitry Andric 309*480093f4SDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 310*480093f4SDimitry Andric MachineFrameInfo &MFI = MF.getFrameInfo(); 311*480093f4SDimitry Andric 312*480093f4SDimitry Andric return !MFI.hasCalls() // No calls 313*480093f4SDimitry Andric && !MRI.isPhysRegUsed(VE::SX18) // Registers within limits 314*480093f4SDimitry Andric // (s18 is first CSR) 315*480093f4SDimitry Andric && !MRI.isPhysRegUsed(VE::SX11) // %sp un-used 316*480093f4SDimitry Andric && !hasFP(MF); // Don't need %fp 317*480093f4SDimitry Andric } 318*480093f4SDimitry Andric 319*480093f4SDimitry Andric void VEFrameLowering::determineCalleeSaves(MachineFunction &MF, 320*480093f4SDimitry Andric BitVector &SavedRegs, 321*480093f4SDimitry Andric RegScavenger *RS) const { 322*480093f4SDimitry Andric TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); 323*480093f4SDimitry Andric 324*480093f4SDimitry Andric assert(isLeafProc(MF) && "TODO implement for non-leaf procs"); 325*480093f4SDimitry Andric } 326