• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2015-2016 The Khronos Group Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "source/opcode.h"
16 
17 #include <assert.h>
18 #include <string.h>
19 
20 #include <algorithm>
21 #include <cstdlib>
22 
23 #include "source/instruction.h"
24 #include "source/macro.h"
25 #include "source/spirv_constant.h"
26 #include "source/spirv_endian.h"
27 #include "source/spirv_target_env.h"
28 #include "spirv-tools/libspirv.h"
29 
30 namespace {
31 struct OpcodeDescPtrLen {
32   const spv_opcode_desc_t* ptr;
33   uint32_t len;
34 };
35 
36 #include "core.insts-unified1.inc"
37 
38 static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries),
39                                                 kOpcodeTableEntries};
40 
41 // Represents a vendor tool entry in the SPIR-V XML Regsitry.
42 struct VendorTool {
43   uint32_t value;
44   const char* vendor;
45   const char* tool;         // Might be empty string.
46   const char* vendor_tool;  // Combiantion of vendor and tool.
47 };
48 
49 const VendorTool vendor_tools[] = {
50 #include "generators.inc"
51 };
52 
53 }  // anonymous namespace
54 
55 // TODO(dneto): Move this to another file.  It doesn't belong with opcode
56 // processing.
spvGeneratorStr(uint32_t generator)57 const char* spvGeneratorStr(uint32_t generator) {
58   auto where = std::find_if(
59       std::begin(vendor_tools), std::end(vendor_tools),
60       [generator](const VendorTool& vt) { return generator == vt.value; });
61   if (where != std::end(vendor_tools)) return where->vendor_tool;
62   return "Unknown";
63 }
64 
spvOpcodeMake(uint16_t wordCount,SpvOp opcode)65 uint32_t spvOpcodeMake(uint16_t wordCount, SpvOp opcode) {
66   return ((uint32_t)opcode) | (((uint32_t)wordCount) << 16);
67 }
68 
spvOpcodeSplit(const uint32_t word,uint16_t * pWordCount,uint16_t * pOpcode)69 void spvOpcodeSplit(const uint32_t word, uint16_t* pWordCount,
70                     uint16_t* pOpcode) {
71   if (pWordCount) {
72     *pWordCount = (uint16_t)((0xffff0000 & word) >> 16);
73   }
74   if (pOpcode) {
75     *pOpcode = 0x0000ffff & word;
76   }
77 }
78 
spvOpcodeTableGet(spv_opcode_table * pInstTable,spv_target_env)79 spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) {
80   if (!pInstTable) return SPV_ERROR_INVALID_POINTER;
81 
82   // Descriptions of each opcode.  Each entry describes the format of the
83   // instruction that follows a particular opcode.
84 
85   *pInstTable = &kOpcodeTable;
86   return SPV_SUCCESS;
87 }
88 
spvOpcodeTableNameLookup(spv_target_env env,const spv_opcode_table table,const char * name,spv_opcode_desc * pEntry)89 spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
90                                       const spv_opcode_table table,
91                                       const char* name,
92                                       spv_opcode_desc* pEntry) {
93   if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER;
94   if (!table) return SPV_ERROR_INVALID_TABLE;
95 
96   // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be
97   // preferable but the table requires sorting on the Opcode name, but it's
98   // static const initialized and matches the order of the spec.
99   const size_t nameLength = strlen(name);
100   for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) {
101     const spv_opcode_desc_t& entry = table->entries[opcodeIndex];
102     // We considers the current opcode as available as long as
103     // 1. The target environment satisfies the minimal requirement of the
104     //    opcode; or
105     // 2. There is at least one extension enabling this opcode.
106     //
107     // Note that the second rule assumes the extension enabling this instruction
108     // is indeed requested in the SPIR-V code; checking that should be
109     // validator's work.
110     if ((spvVersionForTargetEnv(env) >= entry.minVersion ||
111          entry.numExtensions > 0u || entry.numCapabilities > 0u) &&
112         nameLength == strlen(entry.name) &&
113         !strncmp(name, entry.name, nameLength)) {
114       // NOTE: Found out Opcode!
115       *pEntry = &entry;
116       return SPV_SUCCESS;
117     }
118   }
119 
120   return SPV_ERROR_INVALID_LOOKUP;
121 }
122 
spvOpcodeTableValueLookup(spv_target_env env,const spv_opcode_table table,const SpvOp opcode,spv_opcode_desc * pEntry)123 spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
124                                        const spv_opcode_table table,
125                                        const SpvOp opcode,
126                                        spv_opcode_desc* pEntry) {
127   if (!table) return SPV_ERROR_INVALID_TABLE;
128   if (!pEntry) return SPV_ERROR_INVALID_POINTER;
129 
130   const auto beg = table->entries;
131   const auto end = table->entries + table->count;
132 
133   spv_opcode_desc_t needle = {"",    opcode, 0, nullptr, 0,  {},
134                               false, false,  0, nullptr, ~0u};
135 
136   auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
137     return lhs.opcode < rhs.opcode;
138   };
139 
140   // We need to loop here because there can exist multiple symbols for the same
141   // opcode value, and they can be introduced in different target environments,
142   // which means they can have different minimal version requirements.
143   // Assumes the underlying table is already sorted ascendingly according to
144   // opcode value.
145   for (auto it = std::lower_bound(beg, end, needle, comp);
146        it != end && it->opcode == opcode; ++it) {
147     // We considers the current opcode as available as long as
148     // 1. The target environment satisfies the minimal requirement of the
149     //    opcode; or
150     // 2. There is at least one extension enabling this opcode.
151     //
152     // Note that the second rule assumes the extension enabling this instruction
153     // is indeed requested in the SPIR-V code; checking that should be
154     // validator's work.
155     if (spvVersionForTargetEnv(env) >= it->minVersion ||
156         it->numExtensions > 0u || it->numCapabilities > 0u) {
157       *pEntry = it;
158       return SPV_SUCCESS;
159     }
160   }
161 
162   return SPV_ERROR_INVALID_LOOKUP;
163 }
164 
spvInstructionCopy(const uint32_t * words,const SpvOp opcode,const uint16_t wordCount,const spv_endianness_t endian,spv_instruction_t * pInst)165 void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
166                         const uint16_t wordCount, const spv_endianness_t endian,
167                         spv_instruction_t* pInst) {
168   pInst->opcode = opcode;
169   pInst->words.resize(wordCount);
170   for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) {
171     pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian);
172     if (!wordIndex) {
173       uint16_t thisWordCount;
174       uint16_t thisOpcode;
175       spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode);
176       assert(opcode == static_cast<SpvOp>(thisOpcode) &&
177              wordCount == thisWordCount && "Endianness failed!");
178     }
179   }
180 }
181 
spvOpcodeString(const SpvOp opcode)182 const char* spvOpcodeString(const SpvOp opcode) {
183   const auto beg = kOpcodeTableEntries;
184   const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
185   spv_opcode_desc_t needle = {"",    opcode, 0, nullptr, 0,  {},
186                               false, false,  0, nullptr, ~0u};
187   auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
188     return lhs.opcode < rhs.opcode;
189   };
190   auto it = std::lower_bound(beg, end, needle, comp);
191   if (it != end && it->opcode == opcode) {
192     return it->name;
193   }
194 
195   assert(0 && "Unreachable!");
196   return "unknown";
197 }
198 
spvOpcodeIsScalarType(const SpvOp opcode)199 int32_t spvOpcodeIsScalarType(const SpvOp opcode) {
200   switch (opcode) {
201     case SpvOpTypeInt:
202     case SpvOpTypeFloat:
203     case SpvOpTypeBool:
204       return true;
205     default:
206       return false;
207   }
208 }
209 
spvOpcodeIsSpecConstant(const SpvOp opcode)210 int32_t spvOpcodeIsSpecConstant(const SpvOp opcode) {
211   switch (opcode) {
212     case SpvOpSpecConstantTrue:
213     case SpvOpSpecConstantFalse:
214     case SpvOpSpecConstant:
215     case SpvOpSpecConstantComposite:
216     case SpvOpSpecConstantOp:
217       return true;
218     default:
219       return false;
220   }
221 }
222 
spvOpcodeIsConstant(const SpvOp opcode)223 int32_t spvOpcodeIsConstant(const SpvOp opcode) {
224   switch (opcode) {
225     case SpvOpConstantTrue:
226     case SpvOpConstantFalse:
227     case SpvOpConstant:
228     case SpvOpConstantComposite:
229     case SpvOpConstantSampler:
230     case SpvOpConstantNull:
231     case SpvOpSpecConstantTrue:
232     case SpvOpSpecConstantFalse:
233     case SpvOpSpecConstant:
234     case SpvOpSpecConstantComposite:
235     case SpvOpSpecConstantOp:
236       return true;
237     default:
238       return false;
239   }
240 }
241 
spvOpcodeIsConstantOrUndef(const SpvOp opcode)242 bool spvOpcodeIsConstantOrUndef(const SpvOp opcode) {
243   return opcode == SpvOpUndef || spvOpcodeIsConstant(opcode);
244 }
245 
spvOpcodeIsScalarSpecConstant(const SpvOp opcode)246 bool spvOpcodeIsScalarSpecConstant(const SpvOp opcode) {
247   switch (opcode) {
248     case SpvOpSpecConstantTrue:
249     case SpvOpSpecConstantFalse:
250     case SpvOpSpecConstant:
251       return true;
252     default:
253       return false;
254   }
255 }
256 
spvOpcodeIsComposite(const SpvOp opcode)257 int32_t spvOpcodeIsComposite(const SpvOp opcode) {
258   switch (opcode) {
259     case SpvOpTypeVector:
260     case SpvOpTypeMatrix:
261     case SpvOpTypeArray:
262     case SpvOpTypeStruct:
263       return true;
264     default:
265       return false;
266   }
267 }
268 
spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode)269 bool spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode) {
270   switch (opcode) {
271     case SpvOpVariable:
272     case SpvOpAccessChain:
273     case SpvOpInBoundsAccessChain:
274     case SpvOpFunctionParameter:
275     case SpvOpImageTexelPointer:
276     case SpvOpCopyObject:
277     case SpvOpSelect:
278     case SpvOpPhi:
279     case SpvOpFunctionCall:
280     case SpvOpPtrAccessChain:
281     case SpvOpLoad:
282     case SpvOpConstantNull:
283       return true;
284     default:
285       return false;
286   }
287 }
288 
spvOpcodeReturnsLogicalPointer(const SpvOp opcode)289 int32_t spvOpcodeReturnsLogicalPointer(const SpvOp opcode) {
290   switch (opcode) {
291     case SpvOpVariable:
292     case SpvOpAccessChain:
293     case SpvOpInBoundsAccessChain:
294     case SpvOpFunctionParameter:
295     case SpvOpImageTexelPointer:
296     case SpvOpCopyObject:
297       return true;
298     default:
299       return false;
300   }
301 }
302 
spvOpcodeGeneratesType(SpvOp op)303 int32_t spvOpcodeGeneratesType(SpvOp op) {
304   switch (op) {
305     case SpvOpTypeVoid:
306     case SpvOpTypeBool:
307     case SpvOpTypeInt:
308     case SpvOpTypeFloat:
309     case SpvOpTypeVector:
310     case SpvOpTypeMatrix:
311     case SpvOpTypeImage:
312     case SpvOpTypeSampler:
313     case SpvOpTypeSampledImage:
314     case SpvOpTypeArray:
315     case SpvOpTypeRuntimeArray:
316     case SpvOpTypeStruct:
317     case SpvOpTypeOpaque:
318     case SpvOpTypePointer:
319     case SpvOpTypeFunction:
320     case SpvOpTypeEvent:
321     case SpvOpTypeDeviceEvent:
322     case SpvOpTypeReserveId:
323     case SpvOpTypeQueue:
324     case SpvOpTypePipe:
325     case SpvOpTypePipeStorage:
326     case SpvOpTypeNamedBarrier:
327     case SpvOpTypeAccelerationStructureNV:
328       return true;
329     default:
330       // In particular, OpTypeForwardPointer does not generate a type,
331       // but declares a storage class for a pointer type generated
332       // by a different instruction.
333       break;
334   }
335   return 0;
336 }
337 
spvOpcodeIsDecoration(const SpvOp opcode)338 bool spvOpcodeIsDecoration(const SpvOp opcode) {
339   switch (opcode) {
340     case SpvOpDecorate:
341     case SpvOpDecorateId:
342     case SpvOpMemberDecorate:
343     case SpvOpGroupDecorate:
344     case SpvOpGroupMemberDecorate:
345     case SpvOpDecorateStringGOOGLE:
346     case SpvOpMemberDecorateStringGOOGLE:
347       return true;
348     default:
349       break;
350   }
351   return false;
352 }
353 
spvOpcodeIsLoad(const SpvOp opcode)354 bool spvOpcodeIsLoad(const SpvOp opcode) {
355   switch (opcode) {
356     case SpvOpLoad:
357     case SpvOpImageSampleExplicitLod:
358     case SpvOpImageSampleImplicitLod:
359     case SpvOpImageSampleDrefImplicitLod:
360     case SpvOpImageSampleDrefExplicitLod:
361     case SpvOpImageSampleProjImplicitLod:
362     case SpvOpImageSampleProjExplicitLod:
363     case SpvOpImageSampleProjDrefImplicitLod:
364     case SpvOpImageSampleProjDrefExplicitLod:
365     case SpvOpImageFetch:
366     case SpvOpImageGather:
367     case SpvOpImageDrefGather:
368     case SpvOpImageRead:
369     case SpvOpImageSparseSampleImplicitLod:
370     case SpvOpImageSparseSampleExplicitLod:
371     case SpvOpImageSparseSampleDrefExplicitLod:
372     case SpvOpImageSparseSampleDrefImplicitLod:
373     case SpvOpImageSparseFetch:
374     case SpvOpImageSparseGather:
375     case SpvOpImageSparseDrefGather:
376     case SpvOpImageSparseRead:
377       return true;
378     default:
379       return false;
380   }
381 }
382 
spvOpcodeIsBranch(SpvOp opcode)383 bool spvOpcodeIsBranch(SpvOp opcode) {
384   switch (opcode) {
385     case SpvOpBranch:
386     case SpvOpBranchConditional:
387     case SpvOpSwitch:
388       return true;
389     default:
390       return false;
391   }
392 }
393 
spvOpcodeIsAtomicWithLoad(const SpvOp opcode)394 bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) {
395   switch (opcode) {
396     case SpvOpAtomicLoad:
397     case SpvOpAtomicExchange:
398     case SpvOpAtomicCompareExchange:
399     case SpvOpAtomicCompareExchangeWeak:
400     case SpvOpAtomicIIncrement:
401     case SpvOpAtomicIDecrement:
402     case SpvOpAtomicIAdd:
403     case SpvOpAtomicISub:
404     case SpvOpAtomicSMin:
405     case SpvOpAtomicUMin:
406     case SpvOpAtomicSMax:
407     case SpvOpAtomicUMax:
408     case SpvOpAtomicAnd:
409     case SpvOpAtomicOr:
410     case SpvOpAtomicXor:
411     case SpvOpAtomicFlagTestAndSet:
412       return true;
413     default:
414       return false;
415   }
416 }
417 
spvOpcodeIsAtomicOp(const SpvOp opcode)418 bool spvOpcodeIsAtomicOp(const SpvOp opcode) {
419   return (spvOpcodeIsAtomicWithLoad(opcode) || opcode == SpvOpAtomicStore ||
420           opcode == SpvOpAtomicFlagClear);
421 }
422 
spvOpcodeIsReturn(SpvOp opcode)423 bool spvOpcodeIsReturn(SpvOp opcode) {
424   switch (opcode) {
425     case SpvOpReturn:
426     case SpvOpReturnValue:
427       return true;
428     default:
429       return false;
430   }
431 }
432 
spvOpcodeIsReturnOrAbort(SpvOp opcode)433 bool spvOpcodeIsReturnOrAbort(SpvOp opcode) {
434   return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill ||
435          opcode == SpvOpUnreachable;
436 }
437 
spvOpcodeIsBlockTerminator(SpvOp opcode)438 bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
439   return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode);
440 }
441 
spvOpcodeIsBaseOpaqueType(SpvOp opcode)442 bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) {
443   switch (opcode) {
444     case SpvOpTypeImage:
445     case SpvOpTypeSampler:
446     case SpvOpTypeSampledImage:
447     case SpvOpTypeOpaque:
448     case SpvOpTypeEvent:
449     case SpvOpTypeDeviceEvent:
450     case SpvOpTypeReserveId:
451     case SpvOpTypeQueue:
452     case SpvOpTypePipe:
453     case SpvOpTypeForwardPointer:
454     case SpvOpTypePipeStorage:
455     case SpvOpTypeNamedBarrier:
456       return true;
457     default:
458       return false;
459   }
460 }
461 
spvOpcodeIsNonUniformGroupOperation(SpvOp opcode)462 bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode) {
463   switch (opcode) {
464     case SpvOpGroupNonUniformElect:
465     case SpvOpGroupNonUniformAll:
466     case SpvOpGroupNonUniformAny:
467     case SpvOpGroupNonUniformAllEqual:
468     case SpvOpGroupNonUniformBroadcast:
469     case SpvOpGroupNonUniformBroadcastFirst:
470     case SpvOpGroupNonUniformBallot:
471     case SpvOpGroupNonUniformInverseBallot:
472     case SpvOpGroupNonUniformBallotBitExtract:
473     case SpvOpGroupNonUniformBallotBitCount:
474     case SpvOpGroupNonUniformBallotFindLSB:
475     case SpvOpGroupNonUniformBallotFindMSB:
476     case SpvOpGroupNonUniformShuffle:
477     case SpvOpGroupNonUniformShuffleXor:
478     case SpvOpGroupNonUniformShuffleUp:
479     case SpvOpGroupNonUniformShuffleDown:
480     case SpvOpGroupNonUniformIAdd:
481     case SpvOpGroupNonUniformFAdd:
482     case SpvOpGroupNonUniformIMul:
483     case SpvOpGroupNonUniformFMul:
484     case SpvOpGroupNonUniformSMin:
485     case SpvOpGroupNonUniformUMin:
486     case SpvOpGroupNonUniformFMin:
487     case SpvOpGroupNonUniformSMax:
488     case SpvOpGroupNonUniformUMax:
489     case SpvOpGroupNonUniformFMax:
490     case SpvOpGroupNonUniformBitwiseAnd:
491     case SpvOpGroupNonUniformBitwiseOr:
492     case SpvOpGroupNonUniformBitwiseXor:
493     case SpvOpGroupNonUniformLogicalAnd:
494     case SpvOpGroupNonUniformLogicalOr:
495     case SpvOpGroupNonUniformLogicalXor:
496     case SpvOpGroupNonUniformQuadBroadcast:
497     case SpvOpGroupNonUniformQuadSwap:
498       return true;
499     default:
500       return false;
501   }
502 }
503 
spvOpcodeIsScalarizable(SpvOp opcode)504 bool spvOpcodeIsScalarizable(SpvOp opcode) {
505   switch (opcode) {
506     case SpvOpPhi:
507     case SpvOpCopyObject:
508     case SpvOpConvertFToU:
509     case SpvOpConvertFToS:
510     case SpvOpConvertSToF:
511     case SpvOpConvertUToF:
512     case SpvOpUConvert:
513     case SpvOpSConvert:
514     case SpvOpFConvert:
515     case SpvOpQuantizeToF16:
516     case SpvOpVectorInsertDynamic:
517     case SpvOpSNegate:
518     case SpvOpFNegate:
519     case SpvOpIAdd:
520     case SpvOpFAdd:
521     case SpvOpISub:
522     case SpvOpFSub:
523     case SpvOpIMul:
524     case SpvOpFMul:
525     case SpvOpUDiv:
526     case SpvOpSDiv:
527     case SpvOpFDiv:
528     case SpvOpUMod:
529     case SpvOpSRem:
530     case SpvOpSMod:
531     case SpvOpFRem:
532     case SpvOpFMod:
533     case SpvOpVectorTimesScalar:
534     case SpvOpIAddCarry:
535     case SpvOpISubBorrow:
536     case SpvOpUMulExtended:
537     case SpvOpSMulExtended:
538     case SpvOpShiftRightLogical:
539     case SpvOpShiftRightArithmetic:
540     case SpvOpShiftLeftLogical:
541     case SpvOpBitwiseOr:
542     case SpvOpBitwiseAnd:
543     case SpvOpNot:
544     case SpvOpBitFieldInsert:
545     case SpvOpBitFieldSExtract:
546     case SpvOpBitFieldUExtract:
547     case SpvOpBitReverse:
548     case SpvOpBitCount:
549     case SpvOpIsNan:
550     case SpvOpIsInf:
551     case SpvOpIsFinite:
552     case SpvOpIsNormal:
553     case SpvOpSignBitSet:
554     case SpvOpLessOrGreater:
555     case SpvOpOrdered:
556     case SpvOpUnordered:
557     case SpvOpLogicalEqual:
558     case SpvOpLogicalNotEqual:
559     case SpvOpLogicalOr:
560     case SpvOpLogicalAnd:
561     case SpvOpLogicalNot:
562     case SpvOpSelect:
563     case SpvOpIEqual:
564     case SpvOpINotEqual:
565     case SpvOpUGreaterThan:
566     case SpvOpSGreaterThan:
567     case SpvOpUGreaterThanEqual:
568     case SpvOpSGreaterThanEqual:
569     case SpvOpULessThan:
570     case SpvOpSLessThan:
571     case SpvOpULessThanEqual:
572     case SpvOpSLessThanEqual:
573     case SpvOpFOrdEqual:
574     case SpvOpFUnordEqual:
575     case SpvOpFOrdNotEqual:
576     case SpvOpFUnordNotEqual:
577     case SpvOpFOrdLessThan:
578     case SpvOpFUnordLessThan:
579     case SpvOpFOrdGreaterThan:
580     case SpvOpFUnordGreaterThan:
581     case SpvOpFOrdLessThanEqual:
582     case SpvOpFUnordLessThanEqual:
583     case SpvOpFOrdGreaterThanEqual:
584     case SpvOpFUnordGreaterThanEqual:
585       return true;
586     default:
587       return false;
588   }
589 }
590 
spvOpcodeIsDebug(SpvOp opcode)591 bool spvOpcodeIsDebug(SpvOp opcode) {
592   switch (opcode) {
593     case SpvOpName:
594     case SpvOpMemberName:
595     case SpvOpSource:
596     case SpvOpSourceContinued:
597     case SpvOpSourceExtension:
598     case SpvOpString:
599     case SpvOpLine:
600     case SpvOpNoLine:
601       return true;
602     default:
603       return false;
604   }
605 }
606