xref: /freebsd/sys/contrib/dev/acpica/compiler/aslfold.c (revision fcb560670601b2a4d87bb31d7531c8dcc37ee71b)
1 /******************************************************************************
2  *
3  * Module Name: aslfold - Constant folding
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2014, 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 <contrib/dev/acpica/compiler/aslcompiler.h>
45 #include "aslcompiler.y.h"
46 #include <contrib/dev/acpica/include/amlcode.h>
47 
48 #include <contrib/dev/acpica/include/acdispat.h>
49 #include <contrib/dev/acpica/include/acparser.h>
50 
51 #define _COMPONENT          ACPI_COMPILER
52         ACPI_MODULE_NAME    ("aslfold")
53 
54 /* Local prototypes */
55 
56 static ACPI_STATUS
57 OpcAmlEvaluationWalk1 (
58     ACPI_PARSE_OBJECT       *Op,
59     UINT32                  Level,
60     void                    *Context);
61 
62 static ACPI_STATUS
63 OpcAmlEvaluationWalk2 (
64     ACPI_PARSE_OBJECT       *Op,
65     UINT32                  Level,
66     void                    *Context);
67 
68 static ACPI_STATUS
69 OpcAmlCheckForConstant (
70     ACPI_PARSE_OBJECT       *Op,
71     UINT32                  Level,
72     void                    *Context);
73 
74 static void
75 OpcUpdateIntegerNode (
76     ACPI_PARSE_OBJECT       *Op,
77     UINT64                  Value);
78 
79 
80 /*******************************************************************************
81  *
82  * FUNCTION:    OpcAmlEvaluationWalk1
83  *
84  * PARAMETERS:  ASL_WALK_CALLBACK
85  *
86  * RETURN:      Status
87  *
88  * DESCRIPTION: Descending callback for AML execution of constant subtrees
89  *
90  ******************************************************************************/
91 
92 static ACPI_STATUS
93 OpcAmlEvaluationWalk1 (
94     ACPI_PARSE_OBJECT       *Op,
95     UINT32                  Level,
96     void                    *Context)
97 {
98     ACPI_WALK_STATE         *WalkState = Context;
99     ACPI_STATUS             Status;
100     ACPI_PARSE_OBJECT       *OutOp;
101 
102 
103     WalkState->Op = Op;
104     WalkState->Opcode = Op->Common.AmlOpcode;
105     WalkState->OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
106 
107     /* Copy child pointer to Arg for compatibility with Interpreter */
108 
109     if (Op->Asl.Child)
110     {
111         Op->Common.Value.Arg = Op->Asl.Child;
112     }
113 
114     /* Call AML dispatcher */
115 
116     Status = AcpiDsExecBeginOp (WalkState, &OutOp);
117     if (ACPI_FAILURE (Status))
118     {
119         AcpiOsPrintf ("Constant interpretation failed - %s\n",
120                         AcpiFormatException (Status));
121     }
122 
123     return (Status);
124 }
125 
126 
127 /*******************************************************************************
128  *
129  * FUNCTION:    OpcAmlEvaluationWalk2
130  *
131  * PARAMETERS:  ASL_WALK_CALLBACK
132  *
133  * RETURN:      Status
134  *
135  * DESCRIPTION: Ascending callback for AML execution of constant subtrees
136  *
137  ******************************************************************************/
138 
139 static ACPI_STATUS
140 OpcAmlEvaluationWalk2 (
141     ACPI_PARSE_OBJECT       *Op,
142     UINT32                  Level,
143     void                    *Context)
144 {
145     ACPI_WALK_STATE         *WalkState = Context;
146     ACPI_STATUS             Status;
147 
148 
149     WalkState->Op = Op;
150     WalkState->Opcode = Op->Common.AmlOpcode;
151     WalkState->OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
152 
153     /* Copy child pointer to Arg for compatibility with Interpreter */
154 
155     if (Op->Asl.Child)
156     {
157         Op->Common.Value.Arg = Op->Asl.Child;
158     }
159 
160     /* Call AML dispatcher */
161 
162     Status = AcpiDsExecEndOp (WalkState);
163     if (ACPI_FAILURE (Status))
164     {
165         AcpiOsPrintf ("Constant interpretation failed - %s\n",
166                         AcpiFormatException (Status));
167     }
168 
169     return (Status);
170 }
171 
172 
173 /*******************************************************************************
174  *
175  * FUNCTION:    OpcAmlCheckForConstant
176  *
177  * PARAMETERS:  ASL_WALK_CALLBACK
178  *
179  * RETURN:      Status
180  *
181  * DESCRIPTION: Check one Op for a type 3/4/5 AML opcode
182  *
183  ******************************************************************************/
184 
185 static ACPI_STATUS
186 OpcAmlCheckForConstant (
187     ACPI_PARSE_OBJECT       *Op,
188     UINT32                  Level,
189     void                    *Context)
190 {
191     ACPI_WALK_STATE         *WalkState = Context;
192 
193 
194     WalkState->Op = Op;
195     WalkState->Opcode = Op->Common.AmlOpcode;
196     WalkState->OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
197 
198     DbgPrint (ASL_PARSE_OUTPUT, "[%.4d] Opcode: %12.12s ",
199                 Op->Asl.LogicalLineNumber, Op->Asl.ParseOpName);
200 
201     /*
202      * These opcodes do not appear in the OpcodeInfo table, but
203      * they represent constants, so abort the constant walk now.
204      */
205     if ((WalkState->Opcode == AML_RAW_DATA_BYTE) ||
206         (WalkState->Opcode == AML_RAW_DATA_WORD) ||
207         (WalkState->Opcode == AML_RAW_DATA_DWORD) ||
208         (WalkState->Opcode == AML_RAW_DATA_QWORD))
209     {
210         WalkState->WalkType = ACPI_WALK_CONST_OPTIONAL;
211         return (AE_TYPE);
212     }
213 
214     if (!(WalkState->OpInfo->Flags & AML_CONSTANT))
215     {
216         /* The opcode is not a Type 3/4/5 opcode */
217 
218         if (Op->Asl.CompileFlags & NODE_IS_TARGET)
219         {
220             DbgPrint (ASL_PARSE_OUTPUT,
221                 "**** Valid Target, cannot reduce ****\n");
222         }
223         else
224         {
225             DbgPrint (ASL_PARSE_OUTPUT,
226                 "**** Not a Type 3/4/5 opcode ****\n");
227         }
228 
229         if (WalkState->WalkType == ACPI_WALK_CONST_OPTIONAL)
230         {
231             /*
232              * We are looking at at normal expression to see if it can be
233              * reduced. It can't. No error
234              */
235             return (AE_TYPE);
236         }
237 
238         /*
239          * This is an expression that MUST reduce to a constant, and it
240          * can't be reduced. This is an error
241          */
242         if (Op->Asl.CompileFlags & NODE_IS_TARGET)
243         {
244             AslError (ASL_ERROR, ASL_MSG_INVALID_TARGET, Op,
245                 Op->Asl.ParseOpName);
246         }
247         else
248         {
249             AslError (ASL_ERROR, ASL_MSG_INVALID_CONSTANT_OP, Op,
250                 Op->Asl.ParseOpName);
251         }
252 
253         return (AE_TYPE);
254     }
255 
256     /* Debug output */
257 
258     DbgPrint (ASL_PARSE_OUTPUT, "TYPE_345");
259 
260     if (Op->Asl.CompileFlags & NODE_IS_TARGET)
261     {
262         DbgPrint (ASL_PARSE_OUTPUT, " TARGET");
263     }
264     if (Op->Asl.CompileFlags & NODE_IS_TERM_ARG)
265     {
266         DbgPrint (ASL_PARSE_OUTPUT, " TERMARG");
267     }
268 
269     DbgPrint (ASL_PARSE_OUTPUT, "\n");
270     return (AE_OK);
271 }
272 
273 
274 /*******************************************************************************
275  *
276  * FUNCTION:    OpcAmlConstantWalk
277  *
278  * PARAMETERS:  ASL_WALK_CALLBACK
279  *
280  * RETURN:      Status
281  *
282  * DESCRIPTION: Reduce an Op and its subtree to a constant if possible
283  *
284  ******************************************************************************/
285 
286 ACPI_STATUS
287 OpcAmlConstantWalk (
288     ACPI_PARSE_OBJECT       *Op,
289     UINT32                  Level,
290     void                    *Context)
291 {
292     ACPI_WALK_STATE         *WalkState;
293     ACPI_STATUS             Status = AE_OK;
294     ACPI_OPERAND_OBJECT     *ObjDesc;
295     ACPI_PARSE_OBJECT       *RootOp;
296     ACPI_PARSE_OBJECT       *OriginalParentOp;
297     UINT8                   WalkType;
298 
299 
300     /*
301      * Only interested in subtrees that could possibly contain
302      * expressions that can be evaluated at this time
303      */
304     if ((!(Op->Asl.CompileFlags & NODE_COMPILE_TIME_CONST)) ||
305           (Op->Asl.CompileFlags & NODE_IS_TARGET))
306     {
307         return (AE_OK);
308     }
309 
310     /* Set the walk type based on the reduction used for this op */
311 
312     if (Op->Asl.CompileFlags & NODE_IS_TERM_ARG)
313     {
314         /* Op is a TermArg, constant folding is merely optional */
315 
316         if (!Gbl_FoldConstants)
317         {
318             return (AE_CTRL_DEPTH);
319         }
320 
321         WalkType = ACPI_WALK_CONST_OPTIONAL;
322     }
323     else
324     {
325         /* Op is a DataObject, the expression MUST reduced to a constant */
326 
327         WalkType = ACPI_WALK_CONST_REQUIRED;
328     }
329 
330     /* Create a new walk state */
331 
332     WalkState = AcpiDsCreateWalkState (0, NULL, NULL, NULL);
333     if (!WalkState)
334     {
335         return (AE_NO_MEMORY);
336     }
337 
338     WalkState->NextOp = NULL;
339     WalkState->Params = NULL;
340     WalkState->WalkType = WalkType;
341     WalkState->CallerReturnDesc = &ObjDesc;
342 
343     /*
344      * Examine the entire subtree -- all nodes must be constants
345      * or type 3/4/5 opcodes
346      */
347     Status = TrWalkParseTree (Op, ASL_WALK_VISIT_DOWNWARD,
348         OpcAmlCheckForConstant, NULL, WalkState);
349 
350     /*
351      * Did we find an entire subtree that contains all constants and type 3/4/5
352      * opcodes?  (Only AE_OK or AE_TYPE returned from above)
353      */
354     if (Status == AE_TYPE)
355     {
356         /* Subtree cannot be reduced to a constant */
357 
358         if (WalkState->WalkType == ACPI_WALK_CONST_OPTIONAL)
359         {
360             AcpiDsDeleteWalkState (WalkState);
361             return (AE_OK);
362         }
363 
364         /* Don't descend any further, and use a default "constant" value */
365 
366         Status = AE_CTRL_DEPTH;
367     }
368     else
369     {
370         /* Subtree can be reduced */
371 
372         /* Allocate a new temporary root for this subtree */
373 
374         RootOp = TrAllocateNode (PARSEOP_INTEGER);
375         if (!RootOp)
376         {
377             return (AE_NO_MEMORY);
378         }
379 
380         RootOp->Common.AmlOpcode = AML_INT_EVAL_SUBTREE_OP;
381 
382         OriginalParentOp = Op->Common.Parent;
383         Op->Common.Parent = RootOp;
384 
385         /* Hand off the subtree to the AML interpreter */
386 
387         Status = TrWalkParseTree (Op, ASL_WALK_VISIT_TWICE,
388             OpcAmlEvaluationWalk1, OpcAmlEvaluationWalk2, WalkState);
389         Op->Common.Parent = OriginalParentOp;
390 
391         /* TBD: we really *should* release the RootOp node */
392 
393         if (ACPI_SUCCESS (Status))
394         {
395             TotalFolds++;
396 
397             /* Get the final result */
398 
399             Status = AcpiDsResultPop (&ObjDesc, WalkState);
400         }
401 
402         /* Check for error from the ACPICA core */
403 
404         if (ACPI_FAILURE (Status))
405         {
406             AslCoreSubsystemError (Op, Status,
407                 "Failure during constant evaluation", FALSE);
408         }
409     }
410 
411     if (ACPI_FAILURE (Status))
412     {
413         /* We could not resolve the subtree for some reason */
414 
415         AslError (ASL_ERROR, ASL_MSG_CONSTANT_EVALUATION, Op,
416             Op->Asl.ParseOpName);
417 
418         /* Set the subtree value to ZERO anyway. Eliminates further errors */
419 
420         OpcUpdateIntegerNode (Op, 0);
421     }
422     else
423     {
424         AslError (ASL_OPTIMIZATION, ASL_MSG_CONSTANT_FOLDED, Op,
425             Op->Asl.ParseOpName);
426 
427         /*
428          * Because we know we executed type 3/4/5 opcodes above, we know that
429          * the result must be either an Integer, String, or Buffer.
430          */
431         switch (ObjDesc->Common.Type)
432         {
433         case ACPI_TYPE_INTEGER:
434 
435             OpcUpdateIntegerNode (Op, ObjDesc->Integer.Value);
436 
437             DbgPrint (ASL_PARSE_OUTPUT,
438                 "Constant expression reduced to (%s) %8.8X%8.8X\n",
439                 Op->Asl.ParseOpName,
440                 ACPI_FORMAT_UINT64 (Op->Common.Value.Integer));
441             break;
442 
443         case ACPI_TYPE_STRING:
444 
445             Op->Asl.ParseOpcode = PARSEOP_STRING_LITERAL;
446             Op->Common.AmlOpcode = AML_STRING_OP;
447             Op->Asl.AmlLength = ACPI_STRLEN (ObjDesc->String.Pointer) + 1;
448             Op->Common.Value.String = ObjDesc->String.Pointer;
449 
450             DbgPrint (ASL_PARSE_OUTPUT,
451                 "Constant expression reduced to (STRING) %s\n",
452                 Op->Common.Value.String);
453 
454             break;
455 
456         case ACPI_TYPE_BUFFER:
457 
458             Op->Asl.ParseOpcode = PARSEOP_BUFFER;
459             Op->Common.AmlOpcode = AML_BUFFER_OP;
460             Op->Asl.CompileFlags = NODE_AML_PACKAGE;
461             UtSetParseOpName (Op);
462 
463             /* Child node is the buffer length */
464 
465             RootOp = TrAllocateNode (PARSEOP_INTEGER);
466 
467             RootOp->Asl.AmlOpcode = AML_DWORD_OP;
468             RootOp->Asl.Value.Integer = ObjDesc->Buffer.Length;
469             RootOp->Asl.Parent = Op;
470 
471             (void) OpcSetOptimalIntegerSize (RootOp);
472 
473             Op->Asl.Child = RootOp;
474             Op = RootOp;
475             UtSetParseOpName (Op);
476 
477             /* Peer to the child is the raw buffer data */
478 
479             RootOp = TrAllocateNode (PARSEOP_RAW_DATA);
480             RootOp->Asl.AmlOpcode = AML_RAW_DATA_BUFFER;
481             RootOp->Asl.AmlLength = ObjDesc->Buffer.Length;
482             RootOp->Asl.Value.String = (char *) ObjDesc->Buffer.Pointer;
483             RootOp->Asl.Parent = Op->Asl.Parent;
484 
485             Op->Asl.Next = RootOp;
486             Op = RootOp;
487 
488             DbgPrint (ASL_PARSE_OUTPUT,
489                 "Constant expression reduced to (BUFFER) length %X\n",
490                 ObjDesc->Buffer.Length);
491             break;
492 
493         default:
494 
495             printf ("Unsupported return type: %s\n",
496                 AcpiUtGetObjectTypeName (ObjDesc));
497             break;
498         }
499     }
500 
501     UtSetParseOpName (Op);
502     Op->Asl.Child = NULL;
503 
504     AcpiDsDeleteWalkState (WalkState);
505     return (AE_CTRL_DEPTH);
506 }
507 
508 
509 /*******************************************************************************
510  *
511  * FUNCTION:    OpcUpdateIntegerNode
512  *
513  * PARAMETERS:  Op                  - Current parse object
514  *
515  * RETURN:      None
516  *
517  * DESCRIPTION: Update node to the correct integer type.
518  *
519  ******************************************************************************/
520 
521 static void
522 OpcUpdateIntegerNode (
523     ACPI_PARSE_OBJECT       *Op,
524     UINT64                  Value)
525 {
526 
527     Op->Common.Value.Integer = Value;
528 
529     /*
530      * The AmlLength is used by the parser to indicate a constant,
531      * (if non-zero). Length is either (1/2/4/8)
532      */
533     switch (Op->Asl.AmlLength)
534     {
535     case 1:
536 
537         TrUpdateNode (PARSEOP_BYTECONST, Op);
538         Op->Asl.AmlOpcode = AML_RAW_DATA_BYTE;
539         break;
540 
541     case 2:
542 
543         TrUpdateNode (PARSEOP_WORDCONST, Op);
544         Op->Asl.AmlOpcode = AML_RAW_DATA_WORD;
545         break;
546 
547     case 4:
548 
549         TrUpdateNode (PARSEOP_DWORDCONST, Op);
550         Op->Asl.AmlOpcode = AML_RAW_DATA_DWORD;
551         break;
552 
553     case 8:
554 
555         TrUpdateNode (PARSEOP_QWORDCONST, Op);
556         Op->Asl.AmlOpcode = AML_RAW_DATA_QWORD;
557         break;
558 
559     case 0:
560     default:
561 
562         OpcSetOptimalIntegerSize (Op);
563         TrUpdateNode (PARSEOP_INTEGER, Op);
564         break;
565     }
566 
567     Op->Asl.AmlLength = 0;
568 }
569