| /******************************************************************************* |
| * |
| * Module Name: dmcstyle - Support for C-style operator disassembly |
| * |
| ******************************************************************************/ |
| |
| /* |
| * Copyright (C) 2000 - 2020, Intel Corp. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions, and the following disclaimer, |
| * without modification. |
| * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
| * substantially similar to the "NO WARRANTY" disclaimer below |
| * ("Disclaimer") and any redistribution must be conditioned upon |
| * including a substantially similar Disclaimer requirement for further |
| * binary redistribution. |
| * 3. Neither the names of the above-listed copyright holders nor the names |
| * of any contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * NO WARRANTY |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
| * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGES. |
| */ |
| |
| #include "acpi.h" |
| #include "accommon.h" |
| #include "acparser.h" |
| #include "amlcode.h" |
| #include "acdebug.h" |
| #include "acconvert.h" |
| |
| |
| #define _COMPONENT ACPI_CA_DEBUGGER |
| ACPI_MODULE_NAME ("dmcstyle") |
| |
| |
| /* Local prototypes */ |
| |
| static char * |
| AcpiDmGetCompoundSymbol ( |
| UINT16 AslOpcode); |
| |
| static void |
| AcpiDmPromoteTarget ( |
| ACPI_PARSE_OBJECT *Op, |
| ACPI_PARSE_OBJECT *Target); |
| |
| static BOOLEAN |
| AcpiDmIsValidTarget ( |
| ACPI_PARSE_OBJECT *Op); |
| |
| static BOOLEAN |
| AcpiDmIsTargetAnOperand ( |
| ACPI_PARSE_OBJECT *Target, |
| ACPI_PARSE_OBJECT *Operand, |
| BOOLEAN TopLevel); |
| |
| static BOOLEAN |
| AcpiDmIsOptimizationIgnored ( |
| ACPI_PARSE_OBJECT *StoreOp, |
| ACPI_PARSE_OBJECT *StoreArgument); |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiDmCheckForSymbolicOpcode |
| * |
| * PARAMETERS: Op - Current parse object |
| * Walk - Current parse tree walk info |
| * |
| * RETURN: TRUE if opcode can be converted to symbolic, FALSE otherwise |
| * |
| * DESCRIPTION: This is the main code that implements disassembly of AML code |
| * to C-style operators. Called during descending phase of the |
| * parse tree walk. |
| * |
| ******************************************************************************/ |
| |
| BOOLEAN |
| AcpiDmCheckForSymbolicOpcode ( |
| ACPI_PARSE_OBJECT *Op, |
| ACPI_OP_WALK_INFO *Info) |
| { |
| char *OperatorSymbol = NULL; |
| ACPI_PARSE_OBJECT *Argument1; |
| ACPI_PARSE_OBJECT *Argument2; |
| ACPI_PARSE_OBJECT *Target; |
| ACPI_PARSE_OBJECT *Target2; |
| |
| |
| /* Exit immediately if ASL+ not enabled */ |
| |
| if (!AcpiGbl_CstyleDisassembly) |
| { |
| return (FALSE); |
| } |
| |
| /* Get the first operand */ |
| |
| Argument1 = AcpiPsGetArg (Op, 0); |
| if (!Argument1) |
| { |
| return (FALSE); |
| } |
| |
| /* Get the second operand */ |
| |
| Argument2 = Argument1->Common.Next; |
| |
| /* Setup the operator string for this opcode */ |
| |
| switch (Op->Common.AmlOpcode) |
| { |
| case AML_ADD_OP: |
| OperatorSymbol = " + "; |
| break; |
| |
| case AML_SUBTRACT_OP: |
| OperatorSymbol = " - "; |
| break; |
| |
| case AML_MULTIPLY_OP: |
| OperatorSymbol = " * "; |
| break; |
| |
| case AML_DIVIDE_OP: |
| OperatorSymbol = " / "; |
| break; |
| |
| case AML_MOD_OP: |
| OperatorSymbol = " % "; |
| break; |
| |
| case AML_SHIFT_LEFT_OP: |
| OperatorSymbol = " << "; |
| break; |
| |
| case AML_SHIFT_RIGHT_OP: |
| OperatorSymbol = " >> "; |
| break; |
| |
| case AML_BIT_AND_OP: |
| OperatorSymbol = " & "; |
| break; |
| |
| case AML_BIT_OR_OP: |
| OperatorSymbol = " | "; |
| break; |
| |
| case AML_BIT_XOR_OP: |
| OperatorSymbol = " ^ "; |
| break; |
| |
| /* Logical operators, no target */ |
| |
| case AML_LOGICAL_AND_OP: |
| OperatorSymbol = " && "; |
| break; |
| |
| case AML_LOGICAL_EQUAL_OP: |
| OperatorSymbol = " == "; |
| break; |
| |
| case AML_LOGICAL_GREATER_OP: |
| OperatorSymbol = " > "; |
| break; |
| |
| case AML_LOGICAL_LESS_OP: |
| OperatorSymbol = " < "; |
| break; |
| |
| case AML_LOGICAL_OR_OP: |
| OperatorSymbol = " || "; |
| break; |
| |
| case AML_LOGICAL_NOT_OP: |
| /* |
| * Check for the LNOT sub-opcodes. These correspond to |
| * LNotEqual, LLessEqual, and LGreaterEqual. There are |
| * no actual AML opcodes for these operators. |
| */ |
| switch (Argument1->Common.AmlOpcode) |
| { |
| case AML_LOGICAL_EQUAL_OP: |
| OperatorSymbol = " != "; |
| break; |
| |
| case AML_LOGICAL_GREATER_OP: |
| OperatorSymbol = " <= "; |
| break; |
| |
| case AML_LOGICAL_LESS_OP: |
| OperatorSymbol = " >= "; |
| break; |
| |
| default: |
| |
| /* Unary LNOT case, emit "!" immediately */ |
| |
| AcpiOsPrintf ("!"); |
| return (TRUE); |
| } |
| |
| Argument1->Common.DisasmOpcode = ACPI_DASM_LNOT_SUFFIX; |
| Op->Common.DisasmOpcode = ACPI_DASM_LNOT_PREFIX; |
| |
| /* Save symbol string in the next child (not peer) */ |
| |
| Argument2 = AcpiPsGetArg (Argument1, 0); |
| if (!Argument2) |
| { |
| return (FALSE); |
| } |
| |
| Argument2->Common.OperatorSymbol = OperatorSymbol; |
| return (TRUE); |
| |
| case AML_INDEX_OP: |
| /* |
| * Check for constant source operand. Note: although technically |
| * legal syntax, the iASL compiler does not support this with |
| * the symbolic operators for Index(). It doesn't make sense to |
| * use Index() with a constant anyway. |
| */ |
| if ((Argument1->Common.AmlOpcode == AML_STRING_OP) || |
| (Argument1->Common.AmlOpcode == AML_BUFFER_OP) || |
| (Argument1->Common.AmlOpcode == AML_PACKAGE_OP) || |
| (Argument1->Common.AmlOpcode == AML_VARIABLE_PACKAGE_OP)) |
| { |
| Op->Common.DisasmFlags |= ACPI_PARSEOP_CLOSING_PAREN; |
| return (FALSE); |
| } |
| |
| /* Index operator is [] */ |
| |
| Argument1->Common.OperatorSymbol = " ["; |
| Argument2->Common.OperatorSymbol = "]"; |
| break; |
| |
| /* Unary operators */ |
| |
| case AML_DECREMENT_OP: |
| OperatorSymbol = "--"; |
| break; |
| |
| case AML_INCREMENT_OP: |
| OperatorSymbol = "++"; |
| break; |
| |
| case AML_BIT_NOT_OP: |
| case AML_STORE_OP: |
| OperatorSymbol = NULL; |
| break; |
| |
| default: |
| return (FALSE); |
| } |
| |
| if (Argument1->Common.DisasmOpcode == ACPI_DASM_LNOT_SUFFIX) |
| { |
| return (TRUE); |
| } |
| |
| /* |
| * This is the key to how the disassembly of the C-style operators |
| * works. We save the operator symbol in the first child, thus |
| * deferring symbol output until after the first operand has been |
| * emitted. |
| */ |
| if (!Argument1->Common.OperatorSymbol) |
| { |
| Argument1->Common.OperatorSymbol = OperatorSymbol; |
| } |
| |
| /* |
| * Check for a valid target as the 3rd (or sometimes 2nd) operand |
| * |
| * Compound assignment operator support: |
| * Attempt to optimize constructs of the form: |
| * Add (Local1, 0xFF, Local1) |
| * to: |
| * Local1 += 0xFF |
| * |
| * Only the math operators and Store() have a target. |
| * Logicals have no target. |
| */ |
| switch (Op->Common.AmlOpcode) |
| { |
| case AML_ADD_OP: |
| case AML_SUBTRACT_OP: |
| case AML_MULTIPLY_OP: |
| case AML_DIVIDE_OP: |
| case AML_MOD_OP: |
| case AML_SHIFT_LEFT_OP: |
| case AML_SHIFT_RIGHT_OP: |
| case AML_BIT_AND_OP: |
| case AML_BIT_OR_OP: |
| case AML_BIT_XOR_OP: |
| |
| /* Target is 3rd operand */ |
| |
| Target = Argument2->Common.Next; |
| if (Op->Common.AmlOpcode == AML_DIVIDE_OP) |
| { |
| Target2 = Target->Common.Next; |
| |
| /* |
| * Divide has an extra target operand (Remainder). |
| * Default behavior is to simply ignore ASL+ conversion |
| * if the remainder target (modulo) is specified. |
| */ |
| if (!AcpiGbl_DoDisassemblerOptimizations) |
| { |
| if (AcpiDmIsValidTarget (Target)) |
| { |
| Argument1->Common.OperatorSymbol = NULL; |
| Op->Common.DisasmFlags |= ACPI_PARSEOP_LEGACY_ASL_ONLY; |
| return (FALSE); |
| } |
| |
| Target->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE; |
| Target = Target2; |
| } |
| else |
| { |
| /* |
| * Divide has an extra target operand (Remainder). |
| * If both targets are specified, it cannot be converted |
| * to a C-style operator. |
| */ |
| if (AcpiDmIsValidTarget (Target) && |
| AcpiDmIsValidTarget (Target2)) |
| { |
| Argument1->Common.OperatorSymbol = NULL; |
| Op->Common.DisasmFlags |= ACPI_PARSEOP_LEGACY_ASL_ONLY; |
| return (FALSE); |
| } |
| |
| if (AcpiDmIsValidTarget (Target)) /* Only first Target is valid (remainder) */ |
| { |
| /* Convert the Divide to Modulo */ |
| |
| Op->Common.AmlOpcode = AML_MOD_OP; |
| |
| Argument1->Common.OperatorSymbol = " % "; |
| Target2->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE; |
| } |
| else /* Only second Target (quotient) is valid */ |
| { |
| Target->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE; |
| Target = Target2; |
| } |
| } |
| } |
| |
| /* Parser should ensure there is at least a placeholder target */ |
| |
| if (!Target) |
| { |
| return (FALSE); |
| } |
| |
| if (!AcpiDmIsValidTarget (Target)) |
| { |
| /* Not a valid target (placeholder only, from parser) */ |
| break; |
| } |
| |
| /* |
| * Promote the target up to the first child in the parse |
| * tree. This is done because the target will be output |
| * first, in the form: |
| * <Target> = Operands... |
| */ |
| AcpiDmPromoteTarget (Op, Target); |
| |
| /* Check operands for conversion to a "Compound Assignment" */ |
| |
| switch (Op->Common.AmlOpcode) |
| { |
| /* Commutative operators */ |
| |
| case AML_ADD_OP: |
| case AML_MULTIPLY_OP: |
| case AML_BIT_AND_OP: |
| case AML_BIT_OR_OP: |
| case AML_BIT_XOR_OP: |
| /* |
| * For the commutative operators, we can convert to a |
| * compound statement only if at least one (either) operand |
| * is the same as the target. |
| * |
| * Add (A, B, A) --> A += B |
| * Add (B, A, A) --> A += B |
| * Add (B, C, A) --> A = (B + C) |
| */ |
| if ((AcpiDmIsTargetAnOperand (Target, Argument1, TRUE)) || |
| (AcpiDmIsTargetAnOperand (Target, Argument2, TRUE))) |
| { |
| Target->Common.OperatorSymbol = |
| AcpiDmGetCompoundSymbol (Op->Common.AmlOpcode); |
| |
| /* Convert operator to compound assignment */ |
| |
| Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND_ASSIGNMENT; |
| Argument1->Common.OperatorSymbol = NULL; |
| return (TRUE); |
| } |
| break; |
| |
| /* Non-commutative operators */ |
| |
| case AML_SUBTRACT_OP: |
| case AML_DIVIDE_OP: |
| case AML_MOD_OP: |
| case AML_SHIFT_LEFT_OP: |
| case AML_SHIFT_RIGHT_OP: |
| /* |
| * For the non-commutative operators, we can convert to a |
| * compound statement only if the target is the same as the |
| * first operand. |
| * |
| * Subtract (A, B, A) --> A -= B |
| * Subtract (B, A, A) --> A = (B - A) |
| */ |
| if ((AcpiDmIsTargetAnOperand (Target, Argument1, TRUE))) |
| { |
| Target->Common.OperatorSymbol = |
| AcpiDmGetCompoundSymbol (Op->Common.AmlOpcode); |
| |
| /* Convert operator to compound assignment */ |
| |
| Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND_ASSIGNMENT; |
| Argument1->Common.OperatorSymbol = NULL; |
| return (TRUE); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* |
| * If we are within a C-style expression, emit an extra open |
| * paren. Implemented by examining the parent op. |
| */ |
| switch (Op->Common.Parent->Common.AmlOpcode) |
| { |
| case AML_ADD_OP: |
| case AML_SUBTRACT_OP: |
| case AML_MULTIPLY_OP: |
| case AML_DIVIDE_OP: |
| case AML_MOD_OP: |
| case AML_SHIFT_LEFT_OP: |
| case AML_SHIFT_RIGHT_OP: |
| case AML_BIT_AND_OP: |
| case AML_BIT_OR_OP: |
| case AML_BIT_XOR_OP: |
| case AML_LOGICAL_AND_OP: |
| case AML_LOGICAL_EQUAL_OP: |
| case AML_LOGICAL_GREATER_OP: |
| case AML_LOGICAL_LESS_OP: |
| case AML_LOGICAL_OR_OP: |
| |
| Op->Common.DisasmFlags |= ACPI_PARSEOP_ASSIGNMENT; |
| AcpiOsPrintf ("("); |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Normal output for ASL/AML operators with a target operand */ |
| |
| Target->Common.OperatorSymbol = " = ("; |
| return (TRUE); |
| |
| /* Binary operators, no parens */ |
| |
| case AML_DECREMENT_OP: |
| case AML_INCREMENT_OP: |
| return (TRUE); |
| |
| case AML_INDEX_OP: |
| |
| /* Target is optional, 3rd operand */ |
| |
| Target = Argument2->Common.Next; |
| if (AcpiDmIsValidTarget (Target)) |
| { |
| AcpiDmPromoteTarget (Op, Target); |
| |
| if (!Target->Common.OperatorSymbol) |
| { |
| Target->Common.OperatorSymbol = " = "; |
| } |
| } |
| return (TRUE); |
| |
| case AML_STORE_OP: |
| /* |
| * For Store, the Target is the 2nd operand. We know the target |
| * is valid, because it is not optional. |
| * |
| * Ignore any optimizations/folding if flag is set. |
| * Used for iASL/disassembler test suite only. |
| */ |
| if (AcpiDmIsOptimizationIgnored (Op, Argument1)) |
| { |
| return (FALSE); |
| } |
| |
| /* |
| * Perform conversion. |
| * In the parse tree, simply swap the target with the |
| * source so that the target is processed first. |
| */ |
| Target = Argument1->Common.Next; |
| if (!Target) |
| { |
| return (FALSE); |
| } |
| |
| AcpiDmPromoteTarget (Op, Target); |
| if (!Target->Common.OperatorSymbol) |
| { |
| Target->Common.OperatorSymbol = " = "; |
| } |
| return (TRUE); |
| |
| case AML_BIT_NOT_OP: |
| |
| /* Target is optional, 2nd operand */ |
| |
| Target = Argument1->Common.Next; |
| if (!Target) |
| { |
| return (FALSE); |
| } |
| |
| if (AcpiDmIsValidTarget (Target)) |
| { |
| /* Valid target, not a placeholder */ |
| |
| AcpiDmPromoteTarget (Op, Target); |
| Target->Common.OperatorSymbol = " = ~"; |
| } |
| else |
| { |
| /* No target. Emit this prefix operator immediately */ |
| |
| AcpiOsPrintf ("~"); |
| } |
| return (TRUE); |
| |
| default: |
| break; |
| } |
| |
| /* All other operators, emit an open paren */ |
| |
| AcpiOsPrintf ("("); |
| return (TRUE); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiDmIsOptimizationIgnored |
| * |
| * PARAMETERS: StoreOp - Store operator parse object |
| * StoreArgument - Target associate with the Op |
| * |
| * RETURN: TRUE if this Store operator should not be converted/removed. |
| * |
| * DESCRIPTION: The following function implements "Do not optimize if a |
| * store is immediately followed by a math/bit operator that |
| * has no target". |
| * |
| * Function is ignored if DoDisassemblerOptimizations is TRUE. |
| * This is the default, ignore this function. |
| * |
| * Disables these types of optimizations, and simply emits |
| * legacy ASL code: |
| * Store (Add (INT1, 4), INT2) --> Add (INT1, 4, INT2) |
| * --> INT2 = INT1 + 4 |
| * |
| * Store (Not (INT1), INT2) --> Not (INT1, INT2) |
| * --> INT2 = ~INT1 |
| * |
| * Used only for the ASL test suite. For the test suite, we |
| * don't want to perform some optimizations to ensure binary |
| * compatibility with the generation of the legacy ASL->AML. |
| * In other words, for all test modules we want exactly: |
| * (ASL+ -> AML) == (ASL- -> AML) |
| * |
| ******************************************************************************/ |
| |
| static BOOLEAN |
| AcpiDmIsOptimizationIgnored ( |
| ACPI_PARSE_OBJECT *StoreOp, |
| ACPI_PARSE_OBJECT *StoreArgument) |
| { |
| ACPI_PARSE_OBJECT *Argument1; |
| ACPI_PARSE_OBJECT *Argument2; |
| ACPI_PARSE_OBJECT *Target; |
| |
| |
| /* No optimizations/folding for the typical case */ |
| |
| if (AcpiGbl_DoDisassemblerOptimizations) |
| { |
| return (FALSE); |
| } |
| |
| /* |
| * Only a small subset of ASL/AML operators can be optimized. |
| * Can only optimize/fold if there is no target (or targets) |
| * specified for the operator. And of course, the operator |
| * is surrrounded by a Store() operator. |
| */ |
| switch (StoreArgument->Common.AmlOpcode) |
| { |
| case AML_ADD_OP: |
| case AML_SUBTRACT_OP: |
| case AML_MULTIPLY_OP: |
| case AML_MOD_OP: |
| case AML_SHIFT_LEFT_OP: |
| case AML_SHIFT_RIGHT_OP: |
| case AML_BIT_AND_OP: |
| case AML_BIT_OR_OP: |
| case AML_BIT_XOR_OP: |
| case AML_INDEX_OP: |
| |
| /* These operators have two arguments and one target */ |
| |
| Argument1 = StoreArgument->Common.Value.Arg; |
| Argument2 = Argument1->Common.Next; |
| Target = Argument2->Common.Next; |
| |
| if (!AcpiDmIsValidTarget (Target)) |
| { |
| StoreOp->Common.DisasmFlags |= ACPI_PARSEOP_LEGACY_ASL_ONLY; |
| return (TRUE); |
| } |
| break; |
| |
| case AML_DIVIDE_OP: |
| |
| /* This operator has two arguments and two targets */ |
| |
| Argument1 = StoreArgument->Common.Value.Arg; |
| Argument2 = Argument1->Common.Next; |
| Target = Argument2->Common.Next; |
| |
| if (!AcpiDmIsValidTarget (Target) || |
| !AcpiDmIsValidTarget (Target->Common.Next)) |
| { |
| StoreOp->Common.DisasmFlags |= ACPI_PARSEOP_LEGACY_ASL_ONLY; |
| return (TRUE); |
| } |
| break; |
| |
| case AML_BIT_NOT_OP: |
| |
| /* This operator has one operand and one target */ |
| |
| Argument1 = StoreArgument->Common.Value.Arg; |
| Target = Argument1->Common.Next; |
| |
| if (!AcpiDmIsValidTarget (Target)) |
| { |
| StoreOp->Common.DisasmFlags |= ACPI_PARSEOP_LEGACY_ASL_ONLY; |
| return (TRUE); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return (FALSE); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiDmCloseOperator |
| * |
| * PARAMETERS: Op - Current parse object |
| * |
| * RETURN: None |
| * |
| * DESCRIPTION: Closes an operator by adding a closing parentheses if and |
| * when necessary. Called during ascending phase of the |
| * parse tree walk. |
| * |
| ******************************************************************************/ |
| |
| void |
| AcpiDmCloseOperator ( |
| ACPI_PARSE_OBJECT *Op) |
| { |
| |
| /* Always emit paren if ASL+ disassembly disabled */ |
| |
| if (!AcpiGbl_CstyleDisassembly) |
| { |
| AcpiOsPrintf (")"); |
| ASL_CV_PRINT_ONE_COMMENT (Op, AML_COMMENT_END_NODE, NULL, 0); |
| return; |
| } |
| |
| if (Op->Common.DisasmFlags & ACPI_PARSEOP_LEGACY_ASL_ONLY) |
| { |
| AcpiOsPrintf (")"); |
| ASL_CV_PRINT_ONE_COMMENT (Op, AML_COMMENT_END_NODE, NULL, 0); |
| return; |
| } |
| |
| /* Check if we need to add an additional closing paren */ |
| |
| switch (Op->Common.AmlOpcode) |
| { |
| case AML_ADD_OP: |
| case AML_SUBTRACT_OP: |
| case AML_MULTIPLY_OP: |
| case AML_DIVIDE_OP: |
| case AML_MOD_OP: |
| case AML_SHIFT_LEFT_OP: |
| case AML_SHIFT_RIGHT_OP: |
| case AML_BIT_AND_OP: |
| case AML_BIT_OR_OP: |
| case AML_BIT_XOR_OP: |
| case AML_LOGICAL_AND_OP: |
| case AML_LOGICAL_EQUAL_OP: |
| case AML_LOGICAL_GREATER_OP: |
| case AML_LOGICAL_LESS_OP: |
| case AML_LOGICAL_OR_OP: |
| |
| /* Emit paren only if this is not a compound assignment */ |
| |
| if (Op->Common.DisasmFlags & ACPI_PARSEOP_COMPOUND_ASSIGNMENT) |
| { |
| ASL_CV_PRINT_ONE_COMMENT (Op, AML_COMMENT_END_NODE, NULL, 0); |
| return; |
| } |
| |
| /* Emit extra close paren for assignment within an expression */ |
| |
| if (Op->Common.DisasmFlags & ACPI_PARSEOP_ASSIGNMENT) |
| { |
| AcpiOsPrintf (")"); |
| } |
| break; |
| |
| case AML_INDEX_OP: |
| |
| /* This is case for unsupported Index() source constants */ |
| |
| if (Op->Common.DisasmFlags & ACPI_PARSEOP_CLOSING_PAREN) |
| { |
| AcpiOsPrintf (")"); |
| } |
| ASL_CV_PRINT_ONE_COMMENT (Op, AML_COMMENT_END_NODE, NULL, 0); |
| return; |
| |
| /* No need for parens for these */ |
| |
| case AML_DECREMENT_OP: |
| case AML_INCREMENT_OP: |
| case AML_LOGICAL_NOT_OP: |
| case AML_BIT_NOT_OP: |
| case AML_STORE_OP: |
| ASL_CV_PRINT_ONE_COMMENT (Op, AML_COMMENT_END_NODE, NULL, 0); |
| return; |
| |
| default: |
| |
| /* Always emit paren for non-ASL+ operators */ |
| break; |
| } |
| |
| AcpiOsPrintf (")"); |
| ASL_CV_PRINT_ONE_COMMENT (Op, AML_COMMENT_END_NODE, NULL, 0); |
| |
| return; |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiDmGetCompoundSymbol |
| * |
| * PARAMETERS: AslOpcode |
| * |
| * RETURN: String containing the compound assignment symbol |
| * |
| * DESCRIPTION: Detect opcodes that can be converted to compound assignment, |
| * return the appropriate operator string. |
| * |
| ******************************************************************************/ |
| |
| static char * |
| AcpiDmGetCompoundSymbol ( |
| UINT16 AmlOpcode) |
| { |
| char *Symbol; |
| |
| |
| switch (AmlOpcode) |
| { |
| case AML_ADD_OP: |
| Symbol = " += "; |
| break; |
| |
| case AML_SUBTRACT_OP: |
| Symbol = " -= "; |
| break; |
| |
| case AML_MULTIPLY_OP: |
| Symbol = " *= "; |
| break; |
| |
| case AML_DIVIDE_OP: |
| Symbol = " /= "; |
| break; |
| |
| case AML_MOD_OP: |
| Symbol = " %= "; |
| break; |
| |
| case AML_SHIFT_LEFT_OP: |
| Symbol = " <<= "; |
| break; |
| |
| case AML_SHIFT_RIGHT_OP: |
| Symbol = " >>= "; |
| break; |
| |
| case AML_BIT_AND_OP: |
| Symbol = " &= "; |
| break; |
| |
| case AML_BIT_OR_OP: |
| Symbol = " |= "; |
| break; |
| |
| case AML_BIT_XOR_OP: |
| Symbol = " ^= "; |
| break; |
| |
| default: |
| |
| /* No operator string for all other opcodes */ |
| |
| return (NULL); |
| } |
| |
| return (Symbol); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiDmPromoteTarget |
| * |
| * PARAMETERS: Op - Operator parse object |
| * Target - Target associate with the Op |
| * |
| * RETURN: None |
| * |
| * DESCRIPTION: Transform the parse tree by moving the target up to the first |
| * child of the Op. |
| * |
| ******************************************************************************/ |
| |
| static void |
| AcpiDmPromoteTarget ( |
| ACPI_PARSE_OBJECT *Op, |
| ACPI_PARSE_OBJECT *Target) |
| { |
| ACPI_PARSE_OBJECT *Child; |
| |
| |
| /* Link target directly to the Op as first child */ |
| |
| Child = Op->Common.Value.Arg; |
| Op->Common.Value.Arg = Target; |
| Target->Common.Next = Child; |
| |
| /* Find the last peer, it is linked to the target. Unlink it. */ |
| |
| while (Child->Common.Next != Target) |
| { |
| Child = Child->Common.Next; |
| } |
| |
| Child->Common.Next = NULL; |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiDmIsValidTarget |
| * |
| * PARAMETERS: Target - Target Op from the parse tree |
| * |
| * RETURN: TRUE if the Target is real. FALSE if it is just a placeholder |
| * Op that was inserted by the parser. |
| * |
| * DESCRIPTION: Determine if a Target Op is a placeholder Op or a real Target. |
| * In other words, determine if the optional target is used or |
| * not. Note: If Target is NULL, something is seriously wrong, |
| * probably with the parse tree. |
| * |
| ******************************************************************************/ |
| |
| static BOOLEAN |
| AcpiDmIsValidTarget ( |
| ACPI_PARSE_OBJECT *Target) |
| { |
| |
| if (!Target) |
| { |
| return (FALSE); |
| } |
| |
| if ((Target->Common.AmlOpcode == AML_INT_NAMEPATH_OP) && |
| (Target->Common.Value.Arg == NULL)) |
| { |
| return (FALSE); |
| } |
| |
| return (TRUE); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiDmIsTargetAnOperand |
| * |
| * PARAMETERS: Target - Target associated with the expression |
| * Operand - An operand associated with expression |
| * |
| * RETURN: TRUE if expression can be converted to a compound assignment. |
| * FALSE otherwise. |
| * |
| * DESCRIPTION: Determine if the Target duplicates the operand, in order to |
| * detect if the expression can be converted to a compound |
| * assignment. (+=, *=, etc.) |
| * |
| ******************************************************************************/ |
| |
| static BOOLEAN |
| AcpiDmIsTargetAnOperand ( |
| ACPI_PARSE_OBJECT *Target, |
| ACPI_PARSE_OBJECT *Operand, |
| BOOLEAN TopLevel) |
| { |
| const ACPI_OPCODE_INFO *OpInfo; |
| BOOLEAN Same; |
| |
| |
| /* |
| * Opcodes must match. Note: ignoring the difference between nameseg |
| * and namepath for now. May be needed later. |
| */ |
| if (Target->Common.AmlOpcode != Operand->Common.AmlOpcode) |
| { |
| return (FALSE); |
| } |
| |
| /* Nodes should match, even if they are NULL */ |
| |
| if (Target->Common.Node != Operand->Common.Node) |
| { |
| return (FALSE); |
| } |
| |
| /* Determine if a child exists */ |
| |
| OpInfo = AcpiPsGetOpcodeInfo (Operand->Common.AmlOpcode); |
| if (OpInfo->Flags & AML_HAS_ARGS) |
| { |
| Same = AcpiDmIsTargetAnOperand (Target->Common.Value.Arg, |
| Operand->Common.Value.Arg, FALSE); |
| if (!Same) |
| { |
| return (FALSE); |
| } |
| } |
| |
| /* Check the next peer, as long as we are not at the top level */ |
| |
| if ((!TopLevel) && |
| Target->Common.Next) |
| { |
| Same = AcpiDmIsTargetAnOperand (Target->Common.Next, |
| Operand->Common.Next, FALSE); |
| if (!Same) |
| { |
| return (FALSE); |
| } |
| } |
| |
| /* Suppress the duplicate operand at the top-level */ |
| |
| if (TopLevel) |
| { |
| Operand->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE; |
| } |
| return (TRUE); |
| } |