1 /*******************************************************************************
2  *
3  * Module Name: dmcstyle - Support for C-style operator disassembly
4  *
5  ******************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2016, Intel Corp.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions, and the following disclaimer,
16  *    without modification.
17  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18  *    substantially similar to the "NO WARRANTY" disclaimer below
19  *    ("Disclaimer") and any redistribution must be conditioned upon
20  *    including a substantially similar Disclaimer requirement for further
21  *    binary redistribution.
22  * 3. Neither the names of the above-listed copyright holders nor the names
23  *    of any contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * Alternatively, this software may be distributed under the terms of the
27  * GNU General Public License ("GPL") version 2 as published by the Free
28  * Software Foundation.
29  *
30  * NO WARRANTY
31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41  * POSSIBILITY OF SUCH DAMAGES.
42  */
43 
44 #include "acpi.h"
45 #include "accommon.h"
46 #include "acparser.h"
47 #include "amlcode.h"
48 #include "acdebug.h"
49 
50 
51 #define _COMPONENT          ACPI_CA_DEBUGGER
52         ACPI_MODULE_NAME    ("dmcstyle")
53 
54 
55 /* Local prototypes */
56 
57 static char *
58 AcpiDmGetCompoundSymbol (
59    UINT16                   AslOpcode);
60 
61 static void
62 AcpiDmPromoteTarget (
63     ACPI_PARSE_OBJECT       *Op,
64     ACPI_PARSE_OBJECT       *Target);
65 
66 static BOOLEAN
67 AcpiDmIsValidTarget (
68     ACPI_PARSE_OBJECT       *Op);
69 
70 static BOOLEAN
71 AcpiDmIsTargetAnOperand (
72     ACPI_PARSE_OBJECT       *Target,
73     ACPI_PARSE_OBJECT       *Operand,
74     BOOLEAN                 TopLevel);
75 
76 
77 /*******************************************************************************
78  *
79  * FUNCTION:    AcpiDmCheckForSymbolicOpcode
80  *
81  * PARAMETERS:  Op                  - Current parse object
82  *              Walk                - Current parse tree walk info
83  *
84  * RETURN:      TRUE if opcode can be converted to symbolic, FALSE otherwise
85  *
86  * DESCRIPTION: This is the main code that implements disassembly of AML code
87  *              to C-style operators. Called during descending phase of the
88  *              parse tree walk.
89  *
90  ******************************************************************************/
91 
92 BOOLEAN
AcpiDmCheckForSymbolicOpcode(ACPI_PARSE_OBJECT * Op,ACPI_OP_WALK_INFO * Info)93 AcpiDmCheckForSymbolicOpcode (
94     ACPI_PARSE_OBJECT       *Op,
95     ACPI_OP_WALK_INFO       *Info)
96 {
97     char                    *OperatorSymbol = NULL;
98     ACPI_PARSE_OBJECT       *Child1;
99     ACPI_PARSE_OBJECT       *Child2;
100     ACPI_PARSE_OBJECT       *Target;
101 
102 
103     /* Exit immediately if ASL+ not enabled */
104 
105     if (!AcpiGbl_CstyleDisassembly)
106     {
107         return (FALSE);
108     }
109 
110     /* Get the first operand */
111 
112     Child1 = AcpiPsGetArg (Op, 0);
113     if (!Child1)
114     {
115         return (FALSE);
116     }
117 
118     /* Get the second operand */
119 
120     Child2 = Child1->Common.Next;
121 
122     /* Setup the operator string for this opcode */
123 
124     switch (Op->Common.AmlOpcode)
125     {
126     case AML_ADD_OP:
127         OperatorSymbol = " + ";
128         break;
129 
130     case AML_SUBTRACT_OP:
131         OperatorSymbol = " - ";
132         break;
133 
134     case AML_MULTIPLY_OP:
135         OperatorSymbol = " * ";
136         break;
137 
138     case AML_DIVIDE_OP:
139         OperatorSymbol = " / ";
140         break;
141 
142     case AML_MOD_OP:
143         OperatorSymbol = " % ";
144         break;
145 
146     case AML_SHIFT_LEFT_OP:
147         OperatorSymbol = " << ";
148         break;
149 
150     case AML_SHIFT_RIGHT_OP:
151         OperatorSymbol = " >> ";
152         break;
153 
154     case AML_BIT_AND_OP:
155         OperatorSymbol = " & ";
156         break;
157 
158     case AML_BIT_OR_OP:
159         OperatorSymbol = " | ";
160         break;
161 
162     case AML_BIT_XOR_OP:
163         OperatorSymbol = " ^ ";
164         break;
165 
166     /* Logical operators, no target */
167 
168     case AML_LAND_OP:
169         OperatorSymbol = " && ";
170         break;
171 
172     case AML_LEQUAL_OP:
173         OperatorSymbol = " == ";
174         break;
175 
176     case AML_LGREATER_OP:
177         OperatorSymbol = " > ";
178         break;
179 
180     case AML_LLESS_OP:
181         OperatorSymbol = " < ";
182         break;
183 
184     case AML_LOR_OP:
185         OperatorSymbol = " || ";
186         break;
187 
188     case AML_LNOT_OP:
189         /*
190          * Check for the LNOT sub-opcodes. These correspond to
191          * LNotEqual, LLessEqual, and LGreaterEqual. There are
192          * no actual AML opcodes for these operators.
193          */
194         switch (Child1->Common.AmlOpcode)
195         {
196         case AML_LEQUAL_OP:
197             OperatorSymbol = " != ";
198             break;
199 
200         case AML_LGREATER_OP:
201             OperatorSymbol = " <= ";
202             break;
203 
204         case AML_LLESS_OP:
205             OperatorSymbol = " >= ";
206             break;
207 
208         default:
209 
210             /* Unary LNOT case, emit "!" immediately */
211 
212             AcpiOsPrintf ("!");
213             return (TRUE);
214         }
215 
216         Child1->Common.DisasmOpcode = ACPI_DASM_LNOT_SUFFIX;
217         Op->Common.DisasmOpcode = ACPI_DASM_LNOT_PREFIX;
218 
219         /* Save symbol string in the next child (not peer) */
220 
221         Child2 = AcpiPsGetArg (Child1, 0);
222         if (!Child2)
223         {
224             return (FALSE);
225         }
226 
227         Child2->Common.OperatorSymbol = OperatorSymbol;
228         return (TRUE);
229 
230     case AML_INDEX_OP:
231         /*
232          * Check for constant source operand. Note: although technically
233          * legal syntax, the iASL compiler does not support this with
234          * the symbolic operators for Index(). It doesn't make sense to
235          * use Index() with a constant anyway.
236          */
237         if ((Child1->Common.AmlOpcode == AML_STRING_OP)  ||
238             (Child1->Common.AmlOpcode == AML_BUFFER_OP)  ||
239             (Child1->Common.AmlOpcode == AML_PACKAGE_OP) ||
240             (Child1->Common.AmlOpcode == AML_VAR_PACKAGE_OP))
241         {
242             Op->Common.DisasmFlags |= ACPI_PARSEOP_CLOSING_PAREN;
243             return (FALSE);
244         }
245 
246         /* Index operator is [] */
247 
248         Child1->Common.OperatorSymbol = " [";
249         Child2->Common.OperatorSymbol = "]";
250         break;
251 
252     /* Unary operators */
253 
254     case AML_DECREMENT_OP:
255         OperatorSymbol = "--";
256         break;
257 
258     case AML_INCREMENT_OP:
259         OperatorSymbol = "++";
260         break;
261 
262     case AML_BIT_NOT_OP:
263     case AML_STORE_OP:
264         OperatorSymbol = NULL;
265         break;
266 
267     default:
268         return (FALSE);
269     }
270 
271     if (Child1->Common.DisasmOpcode == ACPI_DASM_LNOT_SUFFIX)
272     {
273         return (TRUE);
274     }
275 
276     /*
277      * This is the key to how the disassembly of the C-style operators
278      * works. We save the operator symbol in the first child, thus
279      * deferring symbol output until after the first operand has been
280      * emitted.
281      */
282     if (!Child1->Common.OperatorSymbol)
283     {
284         Child1->Common.OperatorSymbol = OperatorSymbol;
285     }
286 
287     /*
288      * Check for a valid target as the 3rd (or sometimes 2nd) operand
289      *
290      * Compound assignment operator support:
291      * Attempt to optimize constructs of the form:
292      *      Add (Local1, 0xFF, Local1)
293      * to:
294      *      Local1 += 0xFF
295      *
296      * Only the math operators and Store() have a target.
297      * Logicals have no target.
298      */
299     switch (Op->Common.AmlOpcode)
300     {
301     case AML_ADD_OP:
302     case AML_SUBTRACT_OP:
303     case AML_MULTIPLY_OP:
304     case AML_DIVIDE_OP:
305     case AML_MOD_OP:
306     case AML_SHIFT_LEFT_OP:
307     case AML_SHIFT_RIGHT_OP:
308     case AML_BIT_AND_OP:
309     case AML_BIT_OR_OP:
310     case AML_BIT_XOR_OP:
311 
312         /* Target is 3rd operand */
313 
314         Target = Child2->Common.Next;
315         if (Op->Common.AmlOpcode == AML_DIVIDE_OP)
316         {
317             /*
318              * Divide has an extra target operand (Remainder).
319              * If this extra target is specified, it cannot be converted
320              * to a C-style operator
321              */
322             if (AcpiDmIsValidTarget (Target))
323             {
324                 Child1->Common.OperatorSymbol = NULL;
325                 return (FALSE);
326             }
327 
328             Target->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
329             Target = Target->Common.Next;
330         }
331 
332         /* Parser should ensure there is at least a placeholder target */
333 
334         if (!Target)
335         {
336             return (FALSE);
337         }
338 
339         if (!AcpiDmIsValidTarget (Target))
340         {
341             /* Not a valid target (placeholder only, from parser) */
342             break;
343         }
344 
345         /*
346          * Promote the target up to the first child in the parse
347          * tree. This is done because the target will be output
348          * first, in the form:
349          *     <Target> = Operands...
350          */
351         AcpiDmPromoteTarget (Op, Target);
352 
353         /* Check operands for conversion to a "Compound Assignment" */
354 
355         switch (Op->Common.AmlOpcode)
356         {
357             /* Commutative operators */
358 
359         case AML_ADD_OP:
360         case AML_MULTIPLY_OP:
361         case AML_BIT_AND_OP:
362         case AML_BIT_OR_OP:
363         case AML_BIT_XOR_OP:
364             /*
365              * For the commutative operators, we can convert to a
366              * compound statement only if at least one (either) operand
367              * is the same as the target.
368              *
369              *      Add (A, B, A) --> A += B
370              *      Add (B, A, A) --> A += B
371              *      Add (B, C, A) --> A = (B + C)
372              */
373             if ((AcpiDmIsTargetAnOperand (Target, Child1, TRUE)) ||
374                 (AcpiDmIsTargetAnOperand (Target, Child2, TRUE)))
375             {
376                 Target->Common.OperatorSymbol =
377                     AcpiDmGetCompoundSymbol (Op->Common.AmlOpcode);
378 
379                 /* Convert operator to compound assignment */
380 
381                 Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND;
382                 Child1->Common.OperatorSymbol = NULL;
383                 return (TRUE);
384             }
385             break;
386 
387             /* Non-commutative operators */
388 
389         case AML_SUBTRACT_OP:
390         case AML_DIVIDE_OP:
391         case AML_MOD_OP:
392         case AML_SHIFT_LEFT_OP:
393         case AML_SHIFT_RIGHT_OP:
394             /*
395              * For the non-commutative operators, we can convert to a
396              * compound statement only if the target is the same as the
397              * first operand.
398              *
399              *      Subtract (A, B, A) --> A -= B
400              *      Subtract (B, A, A) --> A = (B - A)
401              */
402             if ((AcpiDmIsTargetAnOperand (Target, Child1, TRUE)))
403             {
404                 Target->Common.OperatorSymbol =
405                     AcpiDmGetCompoundSymbol (Op->Common.AmlOpcode);
406 
407                 /* Convert operator to compound assignment */
408 
409                 Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND;
410                 Child1->Common.OperatorSymbol = NULL;
411                 return (TRUE);
412             }
413             break;
414 
415         default:
416             break;
417         }
418 
419         /*
420          * If we are within a C-style expression, emit an extra open
421          * paren. Implemented by examining the parent op.
422          */
423         switch (Op->Common.Parent->Common.AmlOpcode)
424         {
425         case AML_ADD_OP:
426         case AML_SUBTRACT_OP:
427         case AML_MULTIPLY_OP:
428         case AML_DIVIDE_OP:
429         case AML_MOD_OP:
430         case AML_SHIFT_LEFT_OP:
431         case AML_SHIFT_RIGHT_OP:
432         case AML_BIT_AND_OP:
433         case AML_BIT_OR_OP:
434         case AML_BIT_XOR_OP:
435         case AML_LAND_OP:
436         case AML_LEQUAL_OP:
437         case AML_LGREATER_OP:
438         case AML_LLESS_OP:
439         case AML_LOR_OP:
440 
441             Op->Common.DisasmFlags |= ACPI_PARSEOP_ASSIGNMENT;
442             AcpiOsPrintf ("(");
443             break;
444 
445         default:
446             break;
447         }
448 
449         /* Normal output for ASL/AML operators with a target operand */
450 
451         Target->Common.OperatorSymbol = " = (";
452         return (TRUE);
453 
454     /* Binary operators, no parens */
455 
456     case AML_DECREMENT_OP:
457     case AML_INCREMENT_OP:
458         return (TRUE);
459 
460     case AML_INDEX_OP:
461 
462         /* Target is optional, 3rd operand */
463 
464         Target = Child2->Common.Next;
465         if (AcpiDmIsValidTarget (Target))
466         {
467             AcpiDmPromoteTarget (Op, Target);
468 
469             if (!Target->Common.OperatorSymbol)
470             {
471                 Target->Common.OperatorSymbol = " = ";
472             }
473         }
474         return (TRUE);
475 
476     case AML_STORE_OP:
477         /*
478          * Target is the 2nd operand.
479          * We know the target is valid, it is not optional.
480          * In the parse tree, simply swap the target with the
481          * source so that the target is processed first.
482          */
483         Target = Child1->Common.Next;
484         if (!Target)
485         {
486             return (FALSE);
487         }
488 
489         AcpiDmPromoteTarget (Op, Target);
490         if (!Target->Common.OperatorSymbol)
491         {
492             Target->Common.OperatorSymbol = " = ";
493         }
494         return (TRUE);
495 
496     case AML_BIT_NOT_OP:
497 
498         /* Target is optional, 2nd operand */
499 
500         Target = Child1->Common.Next;
501         if (!Target)
502         {
503             return (FALSE);
504         }
505 
506         if (AcpiDmIsValidTarget (Target))
507         {
508             /* Valid target, not a placeholder */
509 
510             AcpiDmPromoteTarget (Op, Target);
511             Target->Common.OperatorSymbol = " = ~";
512         }
513         else
514         {
515             /* No target. Emit this prefix operator immediately */
516 
517             AcpiOsPrintf ("~");
518         }
519         return (TRUE);
520 
521     default:
522         break;
523     }
524 
525     /* All other operators, emit an open paren */
526 
527     AcpiOsPrintf ("(");
528     return (TRUE);
529 }
530 
531 
532 /*******************************************************************************
533  *
534  * FUNCTION:    AcpiDmCloseOperator
535  *
536  * PARAMETERS:  Op                  - Current parse object
537  *
538  * RETURN:      None
539  *
540  * DESCRIPTION: Closes an operator by adding a closing parentheses if and
541  *              when necessary. Called during ascending phase of the
542  *              parse tree walk.
543  *
544  ******************************************************************************/
545 
546 void
AcpiDmCloseOperator(ACPI_PARSE_OBJECT * Op)547 AcpiDmCloseOperator (
548     ACPI_PARSE_OBJECT       *Op)
549 {
550 
551     /* Always emit paren if ASL+ disassembly disabled */
552 
553     if (!AcpiGbl_CstyleDisassembly)
554     {
555         AcpiOsPrintf (")");
556         return;
557     }
558 
559     /* Check if we need to add an additional closing paren */
560 
561     switch (Op->Common.AmlOpcode)
562     {
563     case AML_ADD_OP:
564     case AML_SUBTRACT_OP:
565     case AML_MULTIPLY_OP:
566     case AML_DIVIDE_OP:
567     case AML_MOD_OP:
568     case AML_SHIFT_LEFT_OP:
569     case AML_SHIFT_RIGHT_OP:
570     case AML_BIT_AND_OP:
571     case AML_BIT_OR_OP:
572     case AML_BIT_XOR_OP:
573     case AML_LAND_OP:
574     case AML_LEQUAL_OP:
575     case AML_LGREATER_OP:
576     case AML_LLESS_OP:
577     case AML_LOR_OP:
578 
579         /* Emit paren only if this is not a compound assignment */
580 
581         if (Op->Common.DisasmFlags & ACPI_PARSEOP_COMPOUND)
582         {
583             return;
584         }
585 
586         /* Emit extra close paren for assignment within an expression */
587 
588         if (Op->Common.DisasmFlags & ACPI_PARSEOP_ASSIGNMENT)
589         {
590             AcpiOsPrintf (")");
591         }
592         break;
593 
594     case AML_INDEX_OP:
595 
596         /* This is case for unsupported Index() source constants */
597 
598         if (Op->Common.DisasmFlags & ACPI_PARSEOP_CLOSING_PAREN)
599         {
600             AcpiOsPrintf (")");
601         }
602         return;
603 
604     /* No need for parens for these */
605 
606     case AML_DECREMENT_OP:
607     case AML_INCREMENT_OP:
608     case AML_LNOT_OP:
609     case AML_BIT_NOT_OP:
610     case AML_STORE_OP:
611         return;
612 
613     default:
614 
615         /* Always emit paren for non-ASL+ operators */
616         break;
617     }
618 
619     AcpiOsPrintf (")");
620 }
621 
622 
623 /*******************************************************************************
624  *
625  * FUNCTION:    AcpiDmGetCompoundSymbol
626  *
627  * PARAMETERS:  AslOpcode
628  *
629  * RETURN:      String containing the compound assignment symbol
630  *
631  * DESCRIPTION: Detect opcodes that can be converted to compound assignment,
632  *              return the appropriate operator string.
633  *
634  ******************************************************************************/
635 
636 static char *
AcpiDmGetCompoundSymbol(UINT16 AmlOpcode)637 AcpiDmGetCompoundSymbol (
638    UINT16                   AmlOpcode)
639 {
640     char                    *Symbol;
641 
642 
643     switch (AmlOpcode)
644     {
645     case AML_ADD_OP:
646         Symbol = " += ";
647         break;
648 
649     case AML_SUBTRACT_OP:
650         Symbol = " -= ";
651         break;
652 
653     case AML_MULTIPLY_OP:
654         Symbol = " *= ";
655         break;
656 
657     case AML_DIVIDE_OP:
658         Symbol = " /= ";
659         break;
660 
661     case AML_MOD_OP:
662         Symbol = " %= ";
663         break;
664 
665     case AML_SHIFT_LEFT_OP:
666         Symbol = " <<= ";
667         break;
668 
669     case AML_SHIFT_RIGHT_OP:
670         Symbol = " >>= ";
671         break;
672 
673     case AML_BIT_AND_OP:
674         Symbol = " &= ";
675         break;
676 
677     case AML_BIT_OR_OP:
678         Symbol = " |= ";
679         break;
680 
681     case AML_BIT_XOR_OP:
682         Symbol = " ^= ";
683         break;
684 
685     default:
686 
687         /* No operator string for all other opcodes */
688 
689         return (NULL);
690     }
691 
692     return (Symbol);
693 }
694 
695 
696 /*******************************************************************************
697  *
698  * FUNCTION:    AcpiDmPromoteTarget
699  *
700  * PARAMETERS:  Op                  - Operator parse object
701  *              Target              - Target associate with the Op
702  *
703  * RETURN:      None
704  *
705  * DESCRIPTION: Transform the parse tree by moving the target up to the first
706  *              child of the Op.
707  *
708  ******************************************************************************/
709 
710 static void
AcpiDmPromoteTarget(ACPI_PARSE_OBJECT * Op,ACPI_PARSE_OBJECT * Target)711 AcpiDmPromoteTarget (
712     ACPI_PARSE_OBJECT       *Op,
713     ACPI_PARSE_OBJECT       *Target)
714 {
715     ACPI_PARSE_OBJECT       *Child;
716 
717 
718     /* Link target directly to the Op as first child */
719 
720     Child = Op->Common.Value.Arg;
721     Op->Common.Value.Arg = Target;
722     Target->Common.Next = Child;
723 
724     /* Find the last peer, it is linked to the target. Unlink it. */
725 
726     while (Child->Common.Next != Target)
727     {
728         Child = Child->Common.Next;
729     }
730 
731     Child->Common.Next = NULL;
732 }
733 
734 
735 /*******************************************************************************
736  *
737  * FUNCTION:    AcpiDmIsValidTarget
738  *
739  * PARAMETERS:  Target              - Target Op from the parse tree
740  *
741  * RETURN:      TRUE if the Target is real. FALSE if it is just a placeholder
742  *              Op that was inserted by the parser.
743  *
744  * DESCRIPTION: Determine if a Target Op is a placeholder Op or a real Target.
745  *              In other words, determine if the optional target is used or
746  *              not. Note: If Target is NULL, something is seriously wrong,
747  *              probably with the parse tree.
748  *
749  ******************************************************************************/
750 
751 static BOOLEAN
AcpiDmIsValidTarget(ACPI_PARSE_OBJECT * Target)752 AcpiDmIsValidTarget (
753     ACPI_PARSE_OBJECT       *Target)
754 {
755 
756     if (!Target)
757     {
758         return (FALSE);
759     }
760 
761     if ((Target->Common.AmlOpcode == AML_INT_NAMEPATH_OP) &&
762         (Target->Common.Value.Arg == NULL))
763     {
764         return (FALSE);
765     }
766 
767     return (TRUE);
768 }
769 
770 
771 /*******************************************************************************
772  *
773  * FUNCTION:    AcpiDmIsTargetAnOperand
774  *
775  * PARAMETERS:  Target              - Target associated with the expression
776  *              Operand             - An operand associated with expression
777  *
778  * RETURN:      TRUE if expression can be converted to a compound assignment.
779  *              FALSE otherwise.
780  *
781  * DESCRIPTION: Determine if the Target duplicates the operand, in order to
782  *              detect if the expression can be converted to a compound
783  *              assigment. (+=, *=, etc.)
784  *
785  ******************************************************************************/
786 
787 static BOOLEAN
AcpiDmIsTargetAnOperand(ACPI_PARSE_OBJECT * Target,ACPI_PARSE_OBJECT * Operand,BOOLEAN TopLevel)788 AcpiDmIsTargetAnOperand (
789     ACPI_PARSE_OBJECT       *Target,
790     ACPI_PARSE_OBJECT       *Operand,
791     BOOLEAN                 TopLevel)
792 {
793     const ACPI_OPCODE_INFO  *OpInfo;
794     BOOLEAN                 Same;
795 
796 
797     /*
798      * Opcodes must match. Note: ignoring the difference between nameseg
799      * and namepath for now. May be needed later.
800      */
801     if (Target->Common.AmlOpcode != Operand->Common.AmlOpcode)
802     {
803         return (FALSE);
804     }
805 
806     /* Nodes should match, even if they are NULL */
807 
808     if (Target->Common.Node != Operand->Common.Node)
809     {
810         return (FALSE);
811     }
812 
813     /* Determine if a child exists */
814 
815     OpInfo = AcpiPsGetOpcodeInfo (Operand->Common.AmlOpcode);
816     if (OpInfo->Flags & AML_HAS_ARGS)
817     {
818         Same = AcpiDmIsTargetAnOperand (Target->Common.Value.Arg,
819             Operand->Common.Value.Arg, FALSE);
820         if (!Same)
821         {
822             return (FALSE);
823         }
824     }
825 
826     /* Check the next peer, as long as we are not at the top level */
827 
828     if ((!TopLevel) &&
829          Target->Common.Next)
830     {
831         Same = AcpiDmIsTargetAnOperand (Target->Common.Next,
832             Operand->Common.Next, FALSE);
833         if (!Same)
834         {
835             return (FALSE);
836         }
837     }
838 
839     /* Supress the duplicate operand at the top-level */
840 
841     if (TopLevel)
842     {
843         Operand->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
844     }
845     return (TRUE);
846 }
847