///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  Microsoft Research Singularity
//

// The AML stack IR is a proprietary linearized representation of an AML method.
// By converting the AML to a linearized IR, we can deal with cooperative
// multithreading more easily, because it's easier to represent and explicitly
// store the current state of the interpreter. This transformation also provides
// an opportunity to eliminate redundancies in the original representation.

using System;
using System.Collections;
using System.Diagnostics;

using Node = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.Node;
using NodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.NodePath;
using AbsoluteNodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.AbsoluteNodePath;

using Microsoft.Singularity.Hal.Acpi.AcpiObject;
using Microsoft.Singularity.Hal.Acpi.StackIR;
using Microsoft.Singularity.Hal.Acpi.AmlParserUnions;

namespace Microsoft.Singularity.Hal.Acpi.StackIR
{
    public abstract class StackIRNode
    {
        public abstract void Accept(StackIRNodeVisitor v);
    }

    public class Jump : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        int target;

        public int Target {
            get
            {
                return target;
            }
            set
            {
                target = value;
            }
        }
    }

    public class JumpIfNonZero : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        int thenTarget;

        public int ThenTarget {
            get
            {
                return thenTarget;
            }
            set
            {
                thenTarget = value;
            }
        }
    }

    public class PushArgObj : StackIRNode
    {
        int argNum;

        public PushArgObj(int argNum)
        {
            this.argNum = argNum;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public int ArgNum
        {
            get
            {
                return argNum;
            }
        }
    }

    public class PushLocalObj : StackIRNode
    {
        int localNum;

        public PushLocalObj(int localNum)
        {
            this.localNum = localNum;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public int LocalNum
        {
            get
            {
                return localNum;
            }
        }
    }

    public class PushDebugObj : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class PushNodePath : StackIRNode
    {
        NodePath value;

        public PushNodePath(NodePath value)
        {
            this.value = value;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public NodePath Value
        {
            get
            {
                return value;
            }
        }
    }

    public class PushConst : StackIRNode
    {
        AcpiObject.AcpiObject value;

        public PushConst(AcpiObject.AcpiObject value)
        {
            this.value = value;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public AcpiObject.AcpiObject Value
        {
            get
            {
                return value;
            }
        }
    }

    public class Discard : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    // Store a value in a location *and* push it on the stack
    public class Store : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }
    
    public class MethodCall : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Index : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }
    
    public class ShiftLeft : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class ShiftRight : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Concatenate : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Add : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Subtract : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Multiply : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Divide : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Remainder : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class FindSetLeftBit : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class FindSetRightBit : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class SizeOf : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class LogicalOp : StackIRNode
    {
        public enum Op
        {
            Less,
            LessEq,
            Equal,
            Greater,
            GreaterEq,
            And,
            Or,
            Not
        }

        Op op;

        public LogicalOp(Op op)
        {
            this.op = op;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public Op Operator
        {
            get
            {
                return op;
            }
        }
    }

    public class BitOp : StackIRNode
    {
        public enum Op
        {
            And,
            Or,
            NAnd,
            NOr,
            Not,
            XOr
        }

        Op op;

        public BitOp(Op op)
        {
            this.op = op;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public Op Operator
        {
            get
            {
                return op;
            }
        }
    }

    public class Return : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }
    
    public class CreateField : StackIRNode
    {
        NodePath nodePath;

        public CreateField(NodePath nodePath)
        {
            this.nodePath = nodePath;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public NodePath NodePath
        {
            get
            {
                return nodePath;
            }
        }
    }
    
    public class DefBuffer : StackIRNode
    {
        byte[] initializer;

        public DefBuffer(byte[] initializer)
        {
            this.initializer = initializer;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public byte[] Initializer
        {
            get
            {
                return (byte[])initializer.Clone();
            }
        }
    }
    
    public class DefName : StackIRNode
    {
        NodePath nodePath;

        public DefName(NodePath nodePath)
        {
            this.nodePath = nodePath;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public NodePath NodePath
        {
            get
            {
                return nodePath;
            }
        }
    }

    public class Load : StackIRNode
    {
        NodePath nodePath;

        public Load(NodePath nodePath)
        {
            this.nodePath = nodePath;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public NodePath NodePath
        {
            get
            {
                return nodePath;
            }
        }
    }

    public class Stall : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Match : StackIRNode
    {
        AmlParser.MatchOpcode matchOp1, matchOp2;

        public Match(AmlParser.MatchOpcode matchOp1, AmlParser.MatchOpcode matchOp2)
        {
            this.matchOp1 = matchOp1;
            this.matchOp2 = matchOp2;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }
    
    public class DerefOf : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Package : StackIRNode
    {
        int numElements;

        public Package(int numElements)
        {
            this.numElements = numElements;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Notify : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Sleep : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class RefOf : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class JumpIfNodePathExists : StackIRNode
    {
        private NodePath nodePath;

        public JumpIfNodePathExists(NodePath nodePath)
        {
            this.nodePath = nodePath;
        }

        public NodePath NodePath
        {
            get
            {
                return nodePath;
            }
        }

        int thenTarget;

        public int ThenTarget {
            get
            {
                return thenTarget;
            }
            set
            {
                thenTarget = value;
            }
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class OperationRegion : StackIRNode
    {
        RegionSpace operationSpace;
        NodePath nodePath;

        public OperationRegion(RegionSpace operationSpace, NodePath nodePath)
        {
            this.operationSpace = operationSpace;
            this.nodePath = nodePath;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public RegionSpace OperationSpace
        {
            get
            {
                return operationSpace;
            }
        }

        public NodePath NodePath
        {
            get
            {
                return nodePath;
            }
        }
    }

    public class Field : StackIRNode
    {
        AmlParser.FieldFlags fieldFlags;
        NodePath nodePath;
        FieldElement[] fieldElements;

        public Field(AmlParser.FieldFlags fieldFlags, NodePath nodePath, FieldElement[] fieldElements)
        {
            this.fieldFlags = fieldFlags;
            this.nodePath = nodePath;
            this.fieldElements = fieldElements;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }

        public AmlParser.FieldFlags FieldFlags
        {
            get
            {
                return fieldFlags;
            }
        }

        public NodePath NodePath
        {
            get
            {
                return nodePath;
            }
        }

        public FieldElement[] FieldElements
        {
            get
            {
                return fieldElements;
            }
        }
    }

    public class Acquire : StackIRNode
    {
        ushort timeout;

        public Acquire(ushort timeout)
        {
            this.timeout = timeout;
        }

        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class Release : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class ToBuffer : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class ToInteger : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public class ToString : StackIRNode
    {
        public override void Accept(StackIRNodeVisitor v)
        {
            v.Visit(this);
        }
    }

    public abstract class StackIRNodeVisitor
    {
        public abstract void Visit(Jump node);
        public abstract void Visit(JumpIfNonZero node);
        public abstract void Visit(JumpIfNodePathExists node);
        public abstract void Visit(PushArgObj node);
        public abstract void Visit(PushLocalObj node);
        public abstract void Visit(PushDebugObj node);
        public abstract void Visit(PushNodePath node);
        public abstract void Visit(PushConst node);
        public abstract void Visit(Discard node);
        public abstract void Visit(Store node);
        public abstract void Visit(MethodCall node);
        public abstract void Visit(Index node);
        public abstract void Visit(Add node);
        public abstract void Visit(Subtract node);
        public abstract void Visit(Multiply node);
        public abstract void Visit(Divide node);
        public abstract void Visit(Remainder node);
        public abstract void Visit(ShiftLeft node);
        public abstract void Visit(ShiftRight node);
        public abstract void Visit(Concatenate node);
        public abstract void Visit(LogicalOp node);
        public abstract void Visit(BitOp node);
        public abstract void Visit(Return node);
        public abstract void Visit(CreateField node);
        public abstract void Visit(FindSetLeftBit node);
        public abstract void Visit(FindSetRightBit node);
        public abstract void Visit(SizeOf node);
        public abstract void Visit(DefName node);
        public abstract void Visit(Load node);
        public abstract void Visit(Stall node);
        public abstract void Visit(Match node);
        public abstract void Visit(DerefOf node);
        public abstract void Visit(Package node);
        public abstract void Visit(DefBuffer node);
        public abstract void Visit(Notify node);
        public abstract void Visit(Sleep node);
        public abstract void Visit(RefOf node);
        public abstract void Visit(OperationRegion node);
        public abstract void Visit(Field node);
        public abstract void Visit(Acquire node);
        public abstract void Visit(Release node);
        public abstract void Visit(ToBuffer node);
        public abstract void Visit(ToInteger node);
        public abstract void Visit(ToString node);
    }
}

namespace Microsoft.Singularity.Hal.Acpi
{
    public class AmlToStackIRException : Exception
    {
        // TODO
    }

    public class AmlToStackIRVisitor : AmlParserNodeVisitor
    {
        private class StackIRNodeList
        {
            ArrayList list = new ArrayList();

            public void Add(StackIRNode node) {
                list.Add(node);
            }

            public StackIRNode[] ToArray() {
                return (StackIRNode[])list.ToArray(typeof(StackIRNode));
            }

            public StackIRNode Last
            {
                get
                {
                    return (StackIRNode)list[list.Count - 1];
                }
            }

            public int Count
            {
                get
                {
                    return list.Count;
                }
            }
        }

        StackIRNodeList result = new StackIRNodeList();

        public StackIRNode[] Result
        {
            get
            {
                // Tack on a return of an uninitialized object at the end
                // so it doesn't run off the end.
                result.Add(new PushConst(new UninitializedObject()));
                result.Add(new Return());

                return result.ToArray();
            }
        }

        public override void UnhandledNodeType(string nodeTypeName)
        {
            throw new AmlToStackIRException();
        }
        
        public void VisitSequence(AmlParserNode[] nodeSequence)
        {
            if (nodeSequence == null) {
                return;
            }
            foreach (AmlParserNode node in nodeSequence) {
                node.Accept(this);
                DiscardResults();
            }
        }

        public void DiscardResults()
        {
            // Certain operations produce results in contexts where we
            // don't need them. This throws them away if so.
            if (result.Count > 0 && result.Last is Store) {
                result.Add(new Discard());
            }
        }

        public void VisitSequenceReverse(AmlParserNode[] nodeSequence)
        {
            for(int i = nodeSequence.Length - 1; i >= 0; i--) {
                nodeSequence[i].Accept(this);
            }
        }

        public override void Visit(AmlParser.DefIfElse defIfElse)
        {
            // We create a sequence like this:
            // (push predicate)
            // jump if nonzero to thenBranch
            // (else branch instructions)
            // jump to end
            // elseBranch:
            // (then branch instructions)
            // end:

            defIfElse.predicate.Accept(this);
            JumpIfNonZero thenJump = new JumpIfNonZero();
            result.Add(thenJump);
            defIfElse.defElse.Accept(this);
            Jump jumpOverThenBrach = new Jump();
            result.Add(jumpOverThenBrach);
            thenJump.ThenTarget = result.Count;
            VisitSequence(defIfElse.termList);
            jumpOverThenBrach.Target = result.Count;
        }

        public override void Visit(AmlParser.Predicate predicate)
        {
            predicate.integer.Accept(this);
        }

        public override void Visit(AmlParser.DefElse defElse)
        {
            VisitSequence(defElse.termList);
        }

        public override void Visit(AmlParser.ArgObj argObj)
        {
            result.Add(new PushArgObj(argObj.op));
        }

        public override void Visit(AmlParser.LocalObj localObj)
        {
            result.Add(new PushLocalObj(localObj.op));
        }

        public override void Visit(AmlParser.IndexValue indexValue)
        {
            indexValue.integer.Accept(this);
        }

        public override void Visit(AmlParser.ShiftCount shiftCount)
        {
            shiftCount.integer.Accept(this);
        }

        public override void Visit(AmlParser.BuffPkgStrObj buffPkgStrObj)
        {
            buffPkgStrObj.termArg.Accept(this);
        }

        public override void Visit(AmlParser.Target target)
        {
            target.superName.Accept(this);
        }

        public override void Visit(AmlParser.Operand operand)
        {
            operand.integer.Accept(this);
        }

        public override void Visit(AmlParser.ArgObject argObject)
        {
            argObject.dataRefObject.Accept(this);
        }

        public override void Visit(AmlParser.BitIndex bitIndex)
        {
            bitIndex.integer.Accept(this);
        }

        public override void Visit(AmlParser.ByteIndex byteIndex)
        {
            byteIndex.integer.Accept(this);
        }

        public override void Visit(AmlParser.SourceBuff sourceBuff)
        {
            sourceBuff.buffer.Accept(this);
        }

        public override void Visit(AmlParser.StartIndex startIndex)
        {
            startIndex.integer.Accept(this);
        }

        public override void Visit(AmlParser.ObjReference objReference)
        {
            objReference.termArg.Accept(this);
        }

        public override void Visit(AmlParser.SearchPkg searchPkg)
        {
            searchPkg.package.Accept(this);
        }

        public override void Visit(AmlParser.NumBits numBits)
        {
            numBits.integer.Accept(this);
        }

        public override void Visit(AmlParser.NotifyValue notifyValue)
        {
            notifyValue.integer.Accept(this);
        }
        
        public override void Visit(AmlParser.NotifyObject notifyObject)
        {
            notifyObject.superName.Accept(this);
        }

        public override void Visit(AmlParser.MsecTime msecTime)
        {
            msecTime.integer.Accept(this);
        }

        public override void Visit(AmlParser.RegionLen regionLen)
        {
            regionLen.integer.Accept(this);
        }

        public override void Visit(AmlParser.RegionOffset regionOffset)
        {
            regionOffset.integer.Accept(this);
        }

        public override void Visit(AmlParser.MutexObject mutexObject)
        {
            mutexObject.superName.Accept(this);
        }
        
        public override void Visit(AmlParser.LengthArg lengthArg)
        {
            lengthArg.integer.Accept(this);
        }
        
        public override void Visit(AmlParser.DDBHandleObject ddbHandleObject)
        {
            ddbHandleObject.superName.Accept(this);
        }    

        public override void Visit(AmlParser.UsecTime usecTime)
        {
            usecTime.byteData.Accept(this);
        }    

        public override void Visit(AmlParser.Dividend dividend)
        {
            dividend.integer.Accept(this);
        }

        public override void Visit(AmlParser.Divisor divisor)
        {
            divisor.integer.Accept(this);
        }

        public override void Visit(AmlParser.Remainder remainder)
        {
            remainder.target.Accept(this);
        }

        public override void Visit(AmlParser.Quotient quotient)
        {
            quotient.target.Accept(this);
        }

        public override void Visit(AmlParser.Data data)
        {
            data.computationalData.Accept(this);
        }

        public override void Visit(AmlParser.DefStore defStore)
        {
            defStore.superName.Accept(this);
            defStore.termArg.Accept(this);
            result.Add(new Store());
        }

        public override void Visit(AmlParser.NameString nameString)
        {
            result.Add(new PushNodePath(nameString.nodePath));
        }

        public override void Visit(ComputationalData computationalData)
        {
            switch (computationalData.Tag) {
                case ComputationalData.TagValue.ByteConst:
                    byte byteConst = computationalData.GetAsByteConst();
                    result.Add(new PushConst(new AcpiObject.Integer(byteConst)));
                    break;
                case ComputationalData.TagValue.WordConst:
                    UInt16 wordConst = computationalData.GetAsWordConst();
                    result.Add(new PushConst(new AcpiObject.Integer(wordConst)));
                    break;
                case ComputationalData.TagValue.DWordConst:
                    UInt32 dWordConst = computationalData.GetAsDWordConst();
                    result.Add(new PushConst(new AcpiObject.Integer(dWordConst)));
                    break;
                case ComputationalData.TagValue.QWordConst:
                    UInt64 qWordConst = computationalData.GetAsQWordConst();
                    result.Add(new PushConst(new AcpiObject.Integer(qWordConst)));
                    break;
                case ComputationalData.TagValue.StringConst:
                    string stringConst = computationalData.GetAsStringConst();
                    result.Add(new PushConst(new AcpiObject.String(stringConst)));
                    break;
                case ComputationalData.TagValue.ConstObj:
                    AmlParser.ConstObj constObj = computationalData.GetAsConstObj();
                    switch (constObj.op) {
                        case AmlParser.ZeroOp:
                            result.Add(new PushConst(AcpiObject.IntegerConstant.Zero));
                            break;
                        case AmlParser.OneOp:
                            result.Add(new PushConst(AcpiObject.IntegerConstant.One));
                            break;
                        case AmlParser.OnesOp:
                            result.Add(new PushConst(AcpiObject.IntegerConstant.Ones));
                            break;
                    }
                    break;
                case ComputationalData.TagValue.RevisionOp:
                    result.Add(new PushConst(AcpiObject.IntegerConstant.Revision));
                    break;
                case ComputationalData.TagValue.DefBuffer:
                    AmlParser.DefBuffer defBuffer = computationalData.GetAsDefBuffer();
                    defBuffer.Accept(this);
                    break;
                default:
                    Debug.Assert(false, "Unhandled alternative in switch over 'ComputationalData'");
                    break;
            }
        }

        public override void Visit(AmlParser.DefName defName)
        {
            defName.dataRefObject.Accept(this);
            result.Add(new DefName(defName.nameString.nodePath));
        }

        public override void Visit(AmlParser.UserTermObj userTermObj)
        {
            if (userTermObj.termArgList.Length > 0) {
                VisitSequenceReverse(userTermObj.termArgList);
                result.Add(new PushNodePath(userTermObj.nameString.nodePath));
                result.Add(new MethodCall());
            }
            else {
                // The interpreter will determine at runtime if this is a method or not
                result.Add(new PushNodePath(userTermObj.nameString.nodePath));
            }
        }

        public override void Visit(AmlParser.DefCreateField defCreateField)
        {
            defCreateField.numBits.Accept(this);
            defCreateField.bitIndex.Accept(this);
            defCreateField.sourceBuff.Accept(this);
            result.Add(new CreateField(defCreateField.nameString.nodePath));
        }

        public override void Visit(AmlParser.DefCreateBitField defCreateBitField)
        {
            result.Add(new PushConst(new AcpiObject.Integer(1))); // number of bits
            defCreateBitField.bitIndex.Accept(this);
            defCreateBitField.sourceBuff.Accept(this);
            result.Add(new CreateField(defCreateBitField.nameString.nodePath));
        }

        public override void Visit(AmlParser.DefCreateByteField defCreateByteField)
        {
            result.Add(new PushConst(new AcpiObject.Integer(8))); // number of bits

            // Multiply byte index by 8 to get bit index
            defCreateByteField.byteIndex.Accept(this);
            result.Add(new PushConst(new AcpiObject.Integer(8)));
            result.Add(new Multiply());

            defCreateByteField.sourceBuff.Accept(this);
            result.Add(new CreateField(defCreateByteField.nameString.nodePath));
        }

        public override void Visit(AmlParser.DefCreateWordField defCreateWordField)
        {
            result.Add(new PushConst(new AcpiObject.Integer(16))); // number of bits

            // Multiply byte index by 8 to get bit index
            defCreateWordField.byteIndex.Accept(this);
            result.Add(new PushConst(new AcpiObject.Integer(8)));
            result.Add(new Multiply());

            defCreateWordField.sourceBuff.Accept(this);
            result.Add(new CreateField(defCreateWordField.nameString.nodePath));
        }

        public override void Visit(AmlParser.DefCreateDWordField defCreateDWordField)
        {
            result.Add(new PushConst(new AcpiObject.Integer(32))); // number of bits

            // Multiply byte index by 8 to get bit index
            defCreateDWordField.byteIndex.Accept(this);
            result.Add(new PushConst(new AcpiObject.Integer(8)));
            result.Add(new Multiply());

            defCreateDWordField.sourceBuff.Accept(this);
            result.Add(new CreateField(defCreateDWordField.nameString.nodePath));
        }

        public override void Visit(AmlParser.DefCreateQWordField defCreateQWordField)
        {
            result.Add(new PushConst(new AcpiObject.Integer(64))); // number of bits

            // Multiply byte index by 8 to get bit index
            defCreateQWordField.byteIndex.Accept(this);
            result.Add(new PushConst(new AcpiObject.Integer(8)));
            result.Add(new Multiply());

            defCreateQWordField.sourceBuff.Accept(this);
            result.Add(new CreateField(defCreateQWordField.nameString.nodePath));
        }

        public override void Visit(AmlParser.DefBuffer defBuffer)
        {
            defBuffer.bufferSize.integer.Accept(this);
            result.Add(new DefBuffer(defBuffer.byteList));
        }

        public override void Visit(AmlParser.DefDecrement defDecrement)
        {
            defDecrement.superName.Accept(this); // target

            result.Add(new PushConst(new AcpiObject.Integer(1)));
            defDecrement.superName.Accept(this); // left
            result.Add(new Subtract());

            result.Add(new Store());
        }

        public override void Visit(AmlParser.DefIncrement defIncrement)
        {
            defIncrement.superName.Accept(this); // target

            result.Add(new PushConst(new AcpiObject.Integer(1)));
            defIncrement.superName.Accept(this); // left
            result.Add(new Add());

            result.Add(new Store());
        }

        public override void Visit(AmlParser.DefPackage defPackage)
        {
            VisitSequenceReverse(defPackage.packageElementList);
            result.Add(new Microsoft.Singularity.Hal.Acpi.StackIR.Package(
                defPackage.numElements.byteData));
        }

        public override void Visit(AmlParser.DefFindSetLeftBit defFindSetLeftBit)
        {
            VisitUnaryOperator(defFindSetLeftBit.operand, defFindSetLeftBit.target, new FindSetLeftBit());
        }

        public override void Visit(AmlParser.DefFindSetRightBit defFindSetRightBit)
        {
            VisitUnaryOperator(defFindSetRightBit.operand, defFindSetRightBit.target, new FindSetRightBit());
        }

        public override void Visit(AmlParser.DefSizeOf defSizeOf)
        {
            defSizeOf.superName.Accept(this);
            result.Add(new SizeOf());
        }

        public override void Visit(AmlParser.DefIndex defIndex)
        {
            VisitBinaryOperator(defIndex.buffPkgStrObj, defIndex.indexValue, defIndex.target, new Index());
        }

        public override void Visit(AmlParser.DefAdd defAdd)
        {
            VisitBinaryOperator(defAdd.leftOperand, defAdd.rightOperand, defAdd.target, new Add());
        }

        public override void Visit(AmlParser.DefSubtract defSubtract)
        {
            VisitBinaryOperator(defSubtract.leftOperand, defSubtract.rightOperand, defSubtract.target, new Subtract());
        }

        public override void Visit(AmlParser.DefMultiply defMultiply)
        {
            VisitBinaryOperator(defMultiply.leftOperand, defMultiply.rightOperand, defMultiply.target, new Multiply());
        }

        public override void Visit(AmlParser.DefDivide defDivide)
        {
            const int divisorLocal = AmlStackFrame.FirstReservedLocal;
            const int dividendLocal = divisorLocal + 1;

            // Push all operands on stack first, since subexpressions may use the reserved locals
            if (!IsNullTarget(defDivide.remainder.target)) {
                defDivide.remainder.target.Accept(this);
            }
            if (!IsNullTarget(defDivide.quotient.target)) {
                defDivide.quotient.target.Accept(this);
            }
            result.Add(new PushLocalObj(divisorLocal));
            defDivide.divisor.Accept(this);
            result.Add(new PushLocalObj(dividendLocal));
            defDivide.dividend.Accept(this);

            result.Add(new Store()); // Store dividend expression in dividendLocal
            result.Add(new Store()); // store divisor expression in divisorLocal

            if (!IsNullTarget(defDivide.quotient.target)) {
                result.Add(new PushLocalObj(divisorLocal));
                result.Add(new PushLocalObj(dividendLocal));
                result.Add(new Divide());
                result.Add(new Store()); // Store quotient in quotient.target
            }

            if (!IsNullTarget(defDivide.remainder.target)) {
                result.Add(new PushLocalObj(divisorLocal));
                result.Add(new PushLocalObj(dividendLocal));
                result.Add(new Remainder());
                result.Add(new Store()); // Store remainder in remainder.target
            }
        }

        public override void Visit(AmlParser.DefShiftLeft defShiftLeft)
        {
            VisitBinaryOperator(defShiftLeft.operand, defShiftLeft.shiftCount, defShiftLeft.target, new ShiftLeft());
        }
        
        public override void Visit(AmlParser.DefShiftRight defShiftRight)
        {
            VisitBinaryOperator(defShiftRight.operand, defShiftRight.shiftCount, defShiftRight.target, new ShiftRight());
        }
        
        public override void Visit(AmlParser.DefConcat defConcat)
        {
            VisitBinaryOperator(defConcat.leftData, defConcat.rightData, defConcat.target, new Concatenate());
        }

        public override void Visit(AmlParser.DebugObj debugObj)
        {
            result.Add(new PushDebugObj());
        }

        public override void Visit(AmlParser.DefDerefOf defDerefOf)
        {
            defDerefOf.objReference.Accept(this);
            result.Add(new DerefOf());
        }

        public override void Visit(AmlParser.DefMatch defMatch)
        {
            defMatch.startIndex.Accept(this);
            defMatch.operand2.Accept(this);
            defMatch.operand1.Accept(this);
            defMatch.searchPkg.Accept(this);
            result.Add(new Match(defMatch.matchOpcode1, defMatch.matchOpcode2));
        }

        public override void Visit(AmlParser.DefLEqual defLEqual)
        {
            defLEqual.rightOperand.Accept(this);
            defLEqual.leftOperand.Accept(this);
            result.Add(new LogicalOp(LogicalOp.Op.Equal));
        }

        public override void Visit(AmlParser.DefLLess defLLess)
        {
            defLLess.rightOperand.Accept(this);
            defLLess.leftOperand.Accept(this);
            result.Add(new LogicalOp(LogicalOp.Op.Less));
        }

        public override void Visit(AmlParser.DefLLessEqual defLLessEqual)
        {
            defLLessEqual.rightOperand.Accept(this);
            defLLessEqual.leftOperand.Accept(this);
            result.Add(new LogicalOp(LogicalOp.Op.LessEq));
        }

        public override void Visit(AmlParser.DefLGreater defLGreater)
        {
            defLGreater.rightOperand.Accept(this);
            defLGreater.leftOperand.Accept(this);
            result.Add(new LogicalOp(LogicalOp.Op.Greater));
        }

        public override void Visit(AmlParser.DefLGreaterEqual defLGreaterEqual)
        {
            defLGreaterEqual.rightOperand.Accept(this);
            defLGreaterEqual.leftOperand.Accept(this);
            result.Add(new LogicalOp(LogicalOp.Op.GreaterEq));
        }

        public override void Visit(AmlParser.DefLAnd defLAnd)
        {
            defLAnd.rightOperand.Accept(this);
            defLAnd.leftOperand.Accept(this);
            result.Add(new LogicalOp(LogicalOp.Op.And));
        }

        public override void Visit(AmlParser.DefLOr defLOr)
        {
            defLOr.rightOperand.Accept(this);
            defLOr.leftOperand.Accept(this);
            result.Add(new LogicalOp(LogicalOp.Op.Or));
        }

        public override void Visit(AmlParser.DefLNot defLNot)
        {
            defLNot.operand.Accept(this);
            result.Add(new LogicalOp(LogicalOp.Op.Not));
        }

        public override void Visit(AmlParser.DefAnd defAnd)
        {
            VisitBinaryOperator(defAnd.leftOperand, defAnd.rightOperand, defAnd.target,
                                new BitOp(BitOp.Op.And));
        }

        public override void Visit(AmlParser.DefOr defOr)
        {
            VisitBinaryOperator(defOr.leftOperand, defOr.rightOperand, defOr.target,
                                new BitOp(BitOp.Op.Or));
        }

        public override void Visit(AmlParser.DefNAnd defNAnd)
        {
            VisitBinaryOperator(defNAnd.leftOperand, defNAnd.rightOperand, defNAnd.target,
                                new BitOp(BitOp.Op.NAnd));
        }

        public override void Visit(AmlParser.DefNOr defNOr)
        {
            VisitBinaryOperator(defNOr.leftOperand, defNOr.rightOperand, defNOr.target,
                                new BitOp(BitOp.Op.NOr));
        }

        public override void Visit(AmlParser.DefNot defNot)
        {
            VisitUnaryOperator(defNot.operand, defNot.target,
                               new BitOp(BitOp.Op.Not));
        }

        public override void Visit(AmlParser.DefXOr defXOr)
        {
            VisitBinaryOperator(defXOr.leftOperand, defXOr.rightOperand, defXOr.target,
                                new BitOp(BitOp.Op.XOr));
        }

        private void VisitBinaryOperator(AmlParserNode left, AmlParserNode right,
                                         AmlParser.Target target, StackIRNode stackNode)
        {
            if (IsNullTarget(target)) {
                right.Accept(this);
                left.Accept(this);
                result.Add(stackNode);
            }
            else {
                target.Accept(this);
                right.Accept(this);
                left.Accept(this);
                result.Add(stackNode);
                result.Add(new Store());
            }
        }

        private void VisitUnaryOperator(AmlParserNode operand,
                                        AmlParser.Target target, StackIRNode stackNode)
        {
            if (IsNullTarget(target)) {
                operand.Accept(this);
                result.Add(stackNode);
            }
            else {
                target.Accept(this);
                operand.Accept(this);
                result.Add(stackNode);
                result.Add(new Store());
            }
        }

        public bool IsNullTarget(AmlParser.Target target)
        {
            SuperName superName = target.superName;
            if (superName.Tag != SuperName.TagValue.SimpleName) {
                return false;
            }
            SimpleName simpleName = superName.GetAsSimpleName();
            if (simpleName.Tag != SimpleName.TagValue.NameString) {
                return false;
            }
            NodePath nodePath = simpleName.GetAsNameString().nodePath;
            return (!nodePath.IsAbsolute &&
                    nodePath.NumParentPrefixes == 0 &&
                    nodePath.NameSegments.Length == 0);
        }

        public override void Visit(AmlParser.DefReturn defReturn)
        {
            defReturn.argObject.Accept(this);
            result.Add(new Return());
        }

        public override void Visit(AmlParser.DefWhile defWhile)
        {
            // We create a sequence like this:
            // jump to pushPredicate
            // loopback:
            // (body instructions)
            // pushPredicate:
            // (push predicate)
            // jump if not zero to loopback

            Jump jumpToPushPredicate = new Jump();
            JumpIfNonZero jumpLoopBack = new JumpIfNonZero();

            result.Add(jumpToPushPredicate);
            jumpLoopBack.ThenTarget = result.Count;
            VisitSequence(defWhile.termList);
            jumpToPushPredicate.Target = result.Count;
            defWhile.predicate.Accept(this);
            result.Add(jumpLoopBack);
        }

        public override void Visit(AmlParser.DefNotify defNotify)
        {
            defNotify.notifyValue.Accept(this);
            defNotify.notifyObject.Accept(this);
            result.Add(new Notify());
        }

        public override void Visit(AmlParser.DefSleep defSleep)
        {
            defSleep.msecTime.Accept(this);
            result.Add(new Sleep());
        }

        public override void Visit(AmlParser.DefCondRefOf defCondRefOf)
        {
            // We create a sequence like this:
            // jump if node path exists to existsBranch
            // (push 0)
            // jump to end
            // existsBranch:
            // (execute RefOf instruction and store to target)
            // (push 1)
            // end:

            // The only SuperName that can fail to exist is a node path
            if (defCondRefOf.superName.Tag == SuperName.TagValue.SimpleName &&
                defCondRefOf.superName.GetAsSimpleName().Tag == SimpleName.TagValue.NameString) {

                JumpIfNodePathExists existsJump = new JumpIfNodePathExists(
                    defCondRefOf.superName.GetAsSimpleName().GetAsNameString().nodePath);
                result.Add(existsJump);
                result.Add(new PushConst(new Integer(0)));
                Jump jumpOverExistsBranch = new Jump();
                result.Add(jumpOverExistsBranch);
                existsJump.ThenTarget = result.Count;

                defCondRefOf.target.Accept(this);
                defCondRefOf.superName.Accept(this);
                result.Add(new RefOf());
                result.Add(new Store());
                result.Add(new Discard());
                result.Add(new PushConst(new Integer(1)));

                jumpOverExistsBranch.Target = result.Count;
            }
            else {
                defCondRefOf.target.Accept(this);
                defCondRefOf.superName.Accept(this);
                result.Add(new RefOf());
                result.Add(new Store());
                result.Add(new Discard());
                result.Add(new PushConst(new Integer(1)));
            }
        }

        public override void Visit(AmlParser.DefOpRegion defOpRegion)
        {
            defOpRegion.regionLen.Accept(this);
            defOpRegion.regionOffset.Accept(this);
            result.Add(new StackIR.OperationRegion((RegionSpace)defOpRegion.regionSpace.byteData,
                                                   defOpRegion.nameString.nodePath));
        }
    
        public override void Visit(AmlParser.DefField defField)
        {
            result.Add(new Field(defField.fieldFlags, defField.nameString.nodePath, defField.fieldList));
        }

        public override void Visit(AmlParser.DefAcquire defAcquire)
        {
            defAcquire.mutexObject.Accept(this);
            result.Add(new Acquire(defAcquire.timeOut.wordData));
        }

        public override void Visit(AmlParser.DefRelease defRelease)
        {
            defRelease.mutexObject.Accept(this);
            result.Add(new Release());
        }

        public override void Visit(AmlParser.DefToBuffer defToBuffer)
        {
            defToBuffer.target.Accept(this);
            defToBuffer.operand.Accept(this);
            result.Add(new ToBuffer());
        }

        public override void Visit(AmlParser.DefToInteger defToInteger)
        {
            defToInteger.target.Accept(this);
            defToInteger.operand.Accept(this);
            result.Add(new ToInteger());
        }

        public override void Visit(AmlParser.DefToString defToString)
        {
            defToString.target.Accept(this);
            defToString.lengthArg.Accept(this);
            defToString.termArg.Accept(this);
            result.Add(new ToString());
        }

        public override void Visit(AmlParser.DefNoop defNoop)
        {
            // Do nothing
        }

        public override void Visit(AmlParser.DefLoad defLoad)
        {
            defLoad.ddbHandleObject.Accept(this);
            result.Add(new Load(defLoad.nameString.nodePath));
        }

        public override void Visit(AmlParser.DefStall defStall)
        {
            defStall.usecTime.Accept(this);
            result.Add(new Stall());
        }
    }
}
