1d88c1a5aSDimitry Andric //===- ARMRegisterBankInfo.cpp -----------------------------------*- C++ -*-==//
2d88c1a5aSDimitry Andric //
3d88c1a5aSDimitry Andric //                     The LLVM Compiler Infrastructure
4d88c1a5aSDimitry Andric //
5d88c1a5aSDimitry Andric // This file is distributed under the University of Illinois Open Source
6d88c1a5aSDimitry Andric // License. See LICENSE.TXT for details.
7d88c1a5aSDimitry Andric //
8d88c1a5aSDimitry Andric //===----------------------------------------------------------------------===//
9d88c1a5aSDimitry Andric /// \file
10d88c1a5aSDimitry Andric /// This file implements the targeting of the RegisterBankInfo class for ARM.
11d88c1a5aSDimitry Andric /// \todo This should be generated by TableGen.
12d88c1a5aSDimitry Andric //===----------------------------------------------------------------------===//
13d88c1a5aSDimitry Andric 
14d88c1a5aSDimitry Andric #include "ARMRegisterBankInfo.h"
15d88c1a5aSDimitry Andric #include "ARMInstrInfo.h" // For the register classes
167a7e6055SDimitry Andric #include "ARMSubtarget.h"
17d88c1a5aSDimitry Andric #include "llvm/CodeGen/GlobalISel/RegisterBank.h"
18d88c1a5aSDimitry Andric #include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h"
19d88c1a5aSDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h"
20d88c1a5aSDimitry Andric #include "llvm/Target/TargetRegisterInfo.h"
21d88c1a5aSDimitry Andric 
227a7e6055SDimitry Andric #define GET_TARGET_REGBANK_IMPL
237a7e6055SDimitry Andric #include "ARMGenRegisterBank.inc"
247a7e6055SDimitry Andric 
25d88c1a5aSDimitry Andric using namespace llvm;
26d88c1a5aSDimitry Andric 
27d88c1a5aSDimitry Andric #ifndef LLVM_BUILD_GLOBAL_ISEL
28d88c1a5aSDimitry Andric #error "You shouldn't build this"
29d88c1a5aSDimitry Andric #endif
30d88c1a5aSDimitry Andric 
31d88c1a5aSDimitry Andric // FIXME: TableGen this.
32d88c1a5aSDimitry Andric // If it grows too much and TableGen still isn't ready to do the job, extract it
33d88c1a5aSDimitry Andric // into an ARMGenRegisterBankInfo.def (similar to AArch64).
34d88c1a5aSDimitry Andric namespace llvm {
35d88c1a5aSDimitry Andric namespace ARM {
367a7e6055SDimitry Andric enum PartialMappingIdx {
377a7e6055SDimitry Andric   PMI_GPR,
387a7e6055SDimitry Andric   PMI_SPR,
397a7e6055SDimitry Andric   PMI_DPR,
407a7e6055SDimitry Andric   PMI_Min = PMI_GPR,
41f1a29dd3SDimitry Andric };
42f1a29dd3SDimitry Andric 
437a7e6055SDimitry Andric RegisterBankInfo::PartialMapping PartMappings[]{
447a7e6055SDimitry Andric     // GPR Partial Mapping
457a7e6055SDimitry Andric     {0, 32, GPRRegBank},
467a7e6055SDimitry Andric     // SPR Partial Mapping
477a7e6055SDimitry Andric     {0, 32, FPRRegBank},
487a7e6055SDimitry Andric     // DPR Partial Mapping
497a7e6055SDimitry Andric     {0, 64, FPRRegBank},
507a7e6055SDimitry Andric };
51d88c1a5aSDimitry Andric 
527a7e6055SDimitry Andric #ifndef NDEBUG
537a7e6055SDimitry Andric static bool checkPartMapping(const RegisterBankInfo::PartialMapping &PM,
547a7e6055SDimitry Andric                              unsigned Start, unsigned Length,
557a7e6055SDimitry Andric                              unsigned RegBankID) {
567a7e6055SDimitry Andric   return PM.StartIdx == Start && PM.Length == Length &&
577a7e6055SDimitry Andric          PM.RegBank->getID() == RegBankID;
587a7e6055SDimitry Andric }
597a7e6055SDimitry Andric 
607a7e6055SDimitry Andric static void checkPartialMappings() {
617a7e6055SDimitry Andric   assert(
627a7e6055SDimitry Andric       checkPartMapping(PartMappings[PMI_GPR - PMI_Min], 0, 32, GPRRegBankID) &&
637a7e6055SDimitry Andric       "Wrong mapping for GPR");
647a7e6055SDimitry Andric   assert(
657a7e6055SDimitry Andric       checkPartMapping(PartMappings[PMI_SPR - PMI_Min], 0, 32, FPRRegBankID) &&
667a7e6055SDimitry Andric       "Wrong mapping for SPR");
677a7e6055SDimitry Andric   assert(
687a7e6055SDimitry Andric       checkPartMapping(PartMappings[PMI_DPR - PMI_Min], 0, 64, FPRRegBankID) &&
697a7e6055SDimitry Andric       "Wrong mapping for DPR");
707a7e6055SDimitry Andric }
717a7e6055SDimitry Andric #endif
727a7e6055SDimitry Andric 
737a7e6055SDimitry Andric enum ValueMappingIdx {
747a7e6055SDimitry Andric   InvalidIdx = 0,
757a7e6055SDimitry Andric   GPR3OpsIdx = 1,
767a7e6055SDimitry Andric   SPR3OpsIdx = 4,
777a7e6055SDimitry Andric   DPR3OpsIdx = 7,
787a7e6055SDimitry Andric };
79d88c1a5aSDimitry Andric 
80d88c1a5aSDimitry Andric RegisterBankInfo::ValueMapping ValueMappings[] = {
817a7e6055SDimitry Andric     // invalid
827a7e6055SDimitry Andric     {nullptr, 0},
837a7e6055SDimitry Andric     // 3 ops in GPRs
847a7e6055SDimitry Andric     {&PartMappings[PMI_GPR - PMI_Min], 1},
857a7e6055SDimitry Andric     {&PartMappings[PMI_GPR - PMI_Min], 1},
867a7e6055SDimitry Andric     {&PartMappings[PMI_GPR - PMI_Min], 1},
877a7e6055SDimitry Andric     // 3 ops in SPRs
887a7e6055SDimitry Andric     {&PartMappings[PMI_SPR - PMI_Min], 1},
897a7e6055SDimitry Andric     {&PartMappings[PMI_SPR - PMI_Min], 1},
907a7e6055SDimitry Andric     {&PartMappings[PMI_SPR - PMI_Min], 1},
917a7e6055SDimitry Andric     // 3 ops in DPRs
927a7e6055SDimitry Andric     {&PartMappings[PMI_DPR - PMI_Min], 1},
937a7e6055SDimitry Andric     {&PartMappings[PMI_DPR - PMI_Min], 1},
947a7e6055SDimitry Andric     {&PartMappings[PMI_DPR - PMI_Min], 1}};
957a7e6055SDimitry Andric 
967a7e6055SDimitry Andric #ifndef NDEBUG
977a7e6055SDimitry Andric static bool checkValueMapping(const RegisterBankInfo::ValueMapping &VM,
987a7e6055SDimitry Andric                               RegisterBankInfo::PartialMapping *BreakDown) {
997a7e6055SDimitry Andric   return VM.NumBreakDowns == 1 && VM.BreakDown == BreakDown;
1007a7e6055SDimitry Andric }
1017a7e6055SDimitry Andric 
1027a7e6055SDimitry Andric static void checkValueMappings() {
1037a7e6055SDimitry Andric   assert(checkValueMapping(ValueMappings[GPR3OpsIdx],
1047a7e6055SDimitry Andric                            &PartMappings[PMI_GPR - PMI_Min]) &&
1057a7e6055SDimitry Andric          "Wrong value mapping for 3 GPR ops instruction");
1067a7e6055SDimitry Andric   assert(checkValueMapping(ValueMappings[GPR3OpsIdx + 1],
1077a7e6055SDimitry Andric                            &PartMappings[PMI_GPR - PMI_Min]) &&
1087a7e6055SDimitry Andric          "Wrong value mapping for 3 GPR ops instruction");
1097a7e6055SDimitry Andric   assert(checkValueMapping(ValueMappings[GPR3OpsIdx + 2],
1107a7e6055SDimitry Andric                            &PartMappings[PMI_GPR - PMI_Min]) &&
1117a7e6055SDimitry Andric          "Wrong value mapping for 3 GPR ops instruction");
1127a7e6055SDimitry Andric 
1137a7e6055SDimitry Andric   assert(checkValueMapping(ValueMappings[SPR3OpsIdx],
1147a7e6055SDimitry Andric                            &PartMappings[PMI_SPR - PMI_Min]) &&
1157a7e6055SDimitry Andric          "Wrong value mapping for 3 SPR ops instruction");
1167a7e6055SDimitry Andric   assert(checkValueMapping(ValueMappings[SPR3OpsIdx + 1],
1177a7e6055SDimitry Andric                            &PartMappings[PMI_SPR - PMI_Min]) &&
1187a7e6055SDimitry Andric          "Wrong value mapping for 3 SPR ops instruction");
1197a7e6055SDimitry Andric   assert(checkValueMapping(ValueMappings[SPR3OpsIdx + 2],
1207a7e6055SDimitry Andric                            &PartMappings[PMI_SPR - PMI_Min]) &&
1217a7e6055SDimitry Andric          "Wrong value mapping for 3 SPR ops instruction");
1227a7e6055SDimitry Andric 
1237a7e6055SDimitry Andric   assert(checkValueMapping(ValueMappings[DPR3OpsIdx],
1247a7e6055SDimitry Andric                            &PartMappings[PMI_DPR - PMI_Min]) &&
1257a7e6055SDimitry Andric          "Wrong value mapping for 3 DPR ops instruction");
1267a7e6055SDimitry Andric   assert(checkValueMapping(ValueMappings[DPR3OpsIdx + 1],
1277a7e6055SDimitry Andric                            &PartMappings[PMI_DPR - PMI_Min]) &&
1287a7e6055SDimitry Andric          "Wrong value mapping for 3 DPR ops instruction");
1297a7e6055SDimitry Andric   assert(checkValueMapping(ValueMappings[DPR3OpsIdx + 2],
1307a7e6055SDimitry Andric                            &PartMappings[PMI_DPR - PMI_Min]) &&
1317a7e6055SDimitry Andric          "Wrong value mapping for 3 DPR ops instruction");
1327a7e6055SDimitry Andric }
1337a7e6055SDimitry Andric #endif
134d88c1a5aSDimitry Andric } // end namespace arm
135d88c1a5aSDimitry Andric } // end namespace llvm
136d88c1a5aSDimitry Andric 
137d88c1a5aSDimitry Andric ARMRegisterBankInfo::ARMRegisterBankInfo(const TargetRegisterInfo &TRI)
1387a7e6055SDimitry Andric     : ARMGenRegisterBankInfo() {
139d88c1a5aSDimitry Andric   static bool AlreadyInit = false;
140d88c1a5aSDimitry Andric   // We have only one set of register banks, whatever the subtarget
141d88c1a5aSDimitry Andric   // is. Therefore, the initialization of the RegBanks table should be
142d88c1a5aSDimitry Andric   // done only once. Indeed the table of all register banks
143d88c1a5aSDimitry Andric   // (ARM::RegBanks) is unique in the compiler. At some point, it
144d88c1a5aSDimitry Andric   // will get tablegen'ed and the whole constructor becomes empty.
145d88c1a5aSDimitry Andric   if (AlreadyInit)
146d88c1a5aSDimitry Andric     return;
147d88c1a5aSDimitry Andric   AlreadyInit = true;
148d88c1a5aSDimitry Andric 
149d88c1a5aSDimitry Andric   const RegisterBank &RBGPR = getRegBank(ARM::GPRRegBankID);
150d88c1a5aSDimitry Andric   (void)RBGPR;
151d88c1a5aSDimitry Andric   assert(&ARM::GPRRegBank == &RBGPR && "The order in RegBanks is messed up");
152f1a29dd3SDimitry Andric 
153f1a29dd3SDimitry Andric   // Initialize the GPR bank.
154d88c1a5aSDimitry Andric   assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRRegClassID)) &&
155d88c1a5aSDimitry Andric          "Subclass not added?");
156d88c1a5aSDimitry Andric   assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRwithAPSRRegClassID)) &&
157d88c1a5aSDimitry Andric          "Subclass not added?");
158d88c1a5aSDimitry Andric   assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRnopcRegClassID)) &&
159d88c1a5aSDimitry Andric          "Subclass not added?");
160d88c1a5aSDimitry Andric   assert(RBGPR.covers(*TRI.getRegClass(ARM::rGPRRegClassID)) &&
161d88c1a5aSDimitry Andric          "Subclass not added?");
162d88c1a5aSDimitry Andric   assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPRRegClassID)) &&
163d88c1a5aSDimitry Andric          "Subclass not added?");
164d88c1a5aSDimitry Andric   assert(RBGPR.covers(*TRI.getRegClass(ARM::tcGPRRegClassID)) &&
165d88c1a5aSDimitry Andric          "Subclass not added?");
166d88c1a5aSDimitry Andric   assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPR_and_tcGPRRegClassID)) &&
167d88c1a5aSDimitry Andric          "Subclass not added?");
168d88c1a5aSDimitry Andric   assert(RBGPR.getSize() == 32 && "GPRs should hold up to 32-bit");
1697a7e6055SDimitry Andric 
1707a7e6055SDimitry Andric #ifndef NDEBUG
1717a7e6055SDimitry Andric   ARM::checkPartialMappings();
1727a7e6055SDimitry Andric   ARM::checkValueMappings();
1737a7e6055SDimitry Andric #endif
174d88c1a5aSDimitry Andric }
175d88c1a5aSDimitry Andric 
176d88c1a5aSDimitry Andric const RegisterBank &ARMRegisterBankInfo::getRegBankFromRegClass(
177d88c1a5aSDimitry Andric     const TargetRegisterClass &RC) const {
178d88c1a5aSDimitry Andric   using namespace ARM;
179d88c1a5aSDimitry Andric 
180d88c1a5aSDimitry Andric   switch (RC.getID()) {
181d88c1a5aSDimitry Andric   case GPRRegClassID:
1827a7e6055SDimitry Andric   case GPRnopcRegClassID:
1837a7e6055SDimitry Andric   case GPRspRegClassID:
184d88c1a5aSDimitry Andric   case tGPR_and_tcGPRRegClassID:
1857a7e6055SDimitry Andric   case tGPRRegClassID:
186d88c1a5aSDimitry Andric     return getRegBank(ARM::GPRRegBankID);
1877a7e6055SDimitry Andric   case SPR_8RegClassID:
1887a7e6055SDimitry Andric   case SPRRegClassID:
1897a7e6055SDimitry Andric   case DPR_8RegClassID:
1907a7e6055SDimitry Andric   case DPRRegClassID:
1917a7e6055SDimitry Andric     return getRegBank(ARM::FPRRegBankID);
192d88c1a5aSDimitry Andric   default:
193d88c1a5aSDimitry Andric     llvm_unreachable("Unsupported register kind");
194d88c1a5aSDimitry Andric   }
195d88c1a5aSDimitry Andric 
196d88c1a5aSDimitry Andric   llvm_unreachable("Switch should handle all register classes");
197d88c1a5aSDimitry Andric }
198d88c1a5aSDimitry Andric 
1990f5676f4SDimitry Andric const RegisterBankInfo::InstructionMapping &
200d88c1a5aSDimitry Andric ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
201d88c1a5aSDimitry Andric   auto Opc = MI.getOpcode();
202d88c1a5aSDimitry Andric 
203d88c1a5aSDimitry Andric   // Try the default logic for non-generic instructions that are either copies
204d88c1a5aSDimitry Andric   // or already have some operands assigned to banks.
205d88c1a5aSDimitry Andric   if (!isPreISelGenericOpcode(Opc)) {
2060f5676f4SDimitry Andric     const InstructionMapping &Mapping = getInstrMappingImpl(MI);
207d88c1a5aSDimitry Andric     if (Mapping.isValid())
208d88c1a5aSDimitry Andric       return Mapping;
209d88c1a5aSDimitry Andric   }
210d88c1a5aSDimitry Andric 
211d88c1a5aSDimitry Andric   using namespace TargetOpcode;
212d88c1a5aSDimitry Andric 
2137a7e6055SDimitry Andric   const MachineFunction &MF = *MI.getParent()->getParent();
2147a7e6055SDimitry Andric   const MachineRegisterInfo &MRI = MF.getRegInfo();
2157a7e6055SDimitry Andric   LLT Ty = MRI.getType(MI.getOperand(0).getReg());
2167a7e6055SDimitry Andric 
217d88c1a5aSDimitry Andric   unsigned NumOperands = MI.getNumOperands();
2187a7e6055SDimitry Andric   const ValueMapping *OperandsMapping = &ARM::ValueMappings[ARM::GPR3OpsIdx];
219d88c1a5aSDimitry Andric 
220d88c1a5aSDimitry Andric   switch (Opc) {
221d88c1a5aSDimitry Andric   case G_ADD:
2226bc11b14SDimitry Andric   case G_SUB:
2236bc11b14SDimitry Andric   case G_MUL:
224db17bf38SDimitry Andric   case G_AND:
225db17bf38SDimitry Andric   case G_OR:
226db17bf38SDimitry Andric   case G_XOR:
22751690af2SDimitry Andric   case G_SDIV:
22851690af2SDimitry Andric   case G_UDIV:
2297a7e6055SDimitry Andric   case G_SEXT:
2307a7e6055SDimitry Andric   case G_ZEXT:
2315517e702SDimitry Andric   case G_ANYEXT:
23251690af2SDimitry Andric   case G_TRUNC:
2337a7e6055SDimitry Andric   case G_GEP:
234d88c1a5aSDimitry Andric     // FIXME: We're abusing the fact that everything lives in a GPR for now; in
235d88c1a5aSDimitry Andric     // the real world we would use different mappings.
2367a7e6055SDimitry Andric     OperandsMapping = &ARM::ValueMappings[ARM::GPR3OpsIdx];
237d88c1a5aSDimitry Andric     break;
2387a7e6055SDimitry Andric   case G_LOAD:
2397a7e6055SDimitry Andric   case G_STORE:
2407a7e6055SDimitry Andric     OperandsMapping =
2417a7e6055SDimitry Andric         Ty.getSizeInBits() == 64
2427a7e6055SDimitry Andric             ? getOperandsMapping({&ARM::ValueMappings[ARM::DPR3OpsIdx],
2437a7e6055SDimitry Andric                                   &ARM::ValueMappings[ARM::GPR3OpsIdx]})
2447a7e6055SDimitry Andric             : &ARM::ValueMappings[ARM::GPR3OpsIdx];
2457a7e6055SDimitry Andric     break;
2467a7e6055SDimitry Andric   case G_FADD:
2477a7e6055SDimitry Andric     assert((Ty.getSizeInBits() == 32 || Ty.getSizeInBits() == 64) &&
2487a7e6055SDimitry Andric            "Unsupported size for G_FADD");
2497a7e6055SDimitry Andric     OperandsMapping = Ty.getSizeInBits() == 64
2507a7e6055SDimitry Andric                           ? &ARM::ValueMappings[ARM::DPR3OpsIdx]
2517a7e6055SDimitry Andric                           : &ARM::ValueMappings[ARM::SPR3OpsIdx];
2527a7e6055SDimitry Andric     break;
2537a7e6055SDimitry Andric   case G_CONSTANT:
254d88c1a5aSDimitry Andric   case G_FRAME_INDEX:
2557a7e6055SDimitry Andric     OperandsMapping =
2567a7e6055SDimitry Andric         getOperandsMapping({&ARM::ValueMappings[ARM::GPR3OpsIdx], nullptr});
257d88c1a5aSDimitry Andric     break;
258edd7eaddSDimitry Andric   case G_ICMP: {
259edd7eaddSDimitry Andric     LLT Ty2 = MRI.getType(MI.getOperand(2).getReg());
260edd7eaddSDimitry Andric     (void)Ty2;
261edd7eaddSDimitry Andric     assert(Ty2.getSizeInBits() == 32 && "Unsupported size for G_ICMP");
262edd7eaddSDimitry Andric     OperandsMapping =
263edd7eaddSDimitry Andric         getOperandsMapping({&ARM::ValueMappings[ARM::GPR3OpsIdx], nullptr,
264edd7eaddSDimitry Andric                             &ARM::ValueMappings[ARM::GPR3OpsIdx],
265edd7eaddSDimitry Andric                             &ARM::ValueMappings[ARM::GPR3OpsIdx]});
266edd7eaddSDimitry Andric     break;
267edd7eaddSDimitry Andric   }
268db17bf38SDimitry Andric   case G_MERGE_VALUES: {
269db17bf38SDimitry Andric     // We only support G_MERGE_VALUES for creating a double precision floating
270db17bf38SDimitry Andric     // point value out of two GPRs.
2717a7e6055SDimitry Andric     LLT Ty1 = MRI.getType(MI.getOperand(1).getReg());
272db17bf38SDimitry Andric     LLT Ty2 = MRI.getType(MI.getOperand(2).getReg());
2737a7e6055SDimitry Andric     if (Ty.getSizeInBits() != 64 || Ty1.getSizeInBits() != 32 ||
2747a7e6055SDimitry Andric         Ty2.getSizeInBits() != 32)
2750f5676f4SDimitry Andric       return getInvalidInstructionMapping();
2767a7e6055SDimitry Andric     OperandsMapping =
2777a7e6055SDimitry Andric         getOperandsMapping({&ARM::ValueMappings[ARM::DPR3OpsIdx],
278db17bf38SDimitry Andric                             &ARM::ValueMappings[ARM::GPR3OpsIdx],
279db17bf38SDimitry Andric                             &ARM::ValueMappings[ARM::GPR3OpsIdx]});
2807a7e6055SDimitry Andric     break;
2817a7e6055SDimitry Andric   }
282db17bf38SDimitry Andric   case G_UNMERGE_VALUES: {
283db17bf38SDimitry Andric     // We only support G_UNMERGE_VALUES for splitting a double precision
284db17bf38SDimitry Andric     // floating point value into two GPRs.
2857a7e6055SDimitry Andric     LLT Ty1 = MRI.getType(MI.getOperand(1).getReg());
286db17bf38SDimitry Andric     LLT Ty2 = MRI.getType(MI.getOperand(2).getReg());
287db17bf38SDimitry Andric     if (Ty.getSizeInBits() != 32 || Ty1.getSizeInBits() != 32 ||
288db17bf38SDimitry Andric         Ty2.getSizeInBits() != 64)
2890f5676f4SDimitry Andric       return getInvalidInstructionMapping();
290db17bf38SDimitry Andric     OperandsMapping =
291db17bf38SDimitry Andric         getOperandsMapping({&ARM::ValueMappings[ARM::GPR3OpsIdx],
292db17bf38SDimitry Andric                             &ARM::ValueMappings[ARM::GPR3OpsIdx],
293db17bf38SDimitry Andric                             &ARM::ValueMappings[ARM::DPR3OpsIdx]});
2947a7e6055SDimitry Andric     break;
2957a7e6055SDimitry Andric   }
296d88c1a5aSDimitry Andric   default:
2970f5676f4SDimitry Andric     return getInvalidInstructionMapping();
298d88c1a5aSDimitry Andric   }
299d88c1a5aSDimitry Andric 
3007a7e6055SDimitry Andric #ifndef NDEBUG
3017a7e6055SDimitry Andric   for (unsigned i = 0; i < NumOperands; i++) {
3027a7e6055SDimitry Andric     for (const auto &Mapping : OperandsMapping[i]) {
3037a7e6055SDimitry Andric       assert(
3047a7e6055SDimitry Andric           (Mapping.RegBank->getID() != ARM::FPRRegBankID ||
3057a7e6055SDimitry Andric            MF.getSubtarget<ARMSubtarget>().hasVFP2()) &&
3067a7e6055SDimitry Andric           "Trying to use floating point register bank on target without vfp");
3077a7e6055SDimitry Andric     }
3087a7e6055SDimitry Andric   }
3097a7e6055SDimitry Andric #endif
3107a7e6055SDimitry Andric 
3110f5676f4SDimitry Andric   return getInstructionMapping(DefaultMappingID, /*Cost=*/1, OperandsMapping,
3120f5676f4SDimitry Andric                                NumOperands);
313d88c1a5aSDimitry Andric }
314