Exemplo n.º 1
0
        private void GenerateBlockItem(BlockItemNode blockItem)
        {
            if (blockItem is DeclareNode declareNode)
            {
                // Variable declaration
                if (CurrentVariableMap.DeclaredVariable(declareNode.Name))
                {
                    throw new GeneratorException("Duplicate variable declaration!", blockItem);
                }

                // Execute the expression or use default initializer
                if (declareNode.Expression != null)
                {
                    GenerateExpression(declareNode.Expression);
                }
                // Save initial value on stack
                writer.WriteLine("push    %eax");

                // Keep track of where it is
                CurrentVariableMap.AddInt(declareNode.Name);
            }
            else if (blockItem is StatementNode statement)
            {
                GenerateStatement(statement);
            }
            else
            {
                throw new NotImplementedException();
            }
        }
Exemplo n.º 2
0
        private void GenerateFunction(FunctionNode function)
        {
            // No need to generate non-definitions
            if (!function.IsDefinition)
            {
                return;
            }

            currentFunction = function;
            labelCounter    = 0;

            localVariableMaps.Push(new LocalVariableMap());
            // Add local variables to the map
            int i = 0;

            foreach (var var in function.Parameters)
            {
                CurrentVariableMap.AddParamInt(var, i++);
            }

            // Function label
            writer.WriteLine(".globl {0}", GetFunctionLabel(function.Name));
            writer.WriteLine(GetFunctionLabel(function.Name) + ":");

            // Function prologue, epiloge is included in return statement generation
            writer.WriteLine("push    %ebp");           // Store ebp on the stack
            writer.WriteLine("movl    %esp, %ebp");     // Use the current esp as our ebp
                                                        // TODO: Add more prologue/epilogue depending on calling conventions?

            // Process all block items
            foreach (var item in function.BodyItems)
            {
                GenerateBlockItem(item);
            }

            currentFunction = null;
        }
Exemplo n.º 3
0
        /// <summary>
        /// Generates an expression for which the result gets stored in EAX
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="writer"></param>
        private void GenerateExpression(ExpressionNode expression)
        {
            if (expression is ConstantNode constantNode)
            {
                writer.WriteLine("movl    ${0}, %eax", constantNode.Value);
            }
            else if (expression is UnaryNode unaryNode)
            {
                switch (unaryNode.Op)
                {
                case UnaryNode.Operation.Complement:
                    GenerateExpression(unaryNode.Expression);
                    writer.WriteLine("not     %eax");
                    break;

                case UnaryNode.Operation.Negate:
                    GenerateExpression(unaryNode.Expression);
                    writer.WriteLine("neg     %eax");
                    break;

                case UnaryNode.Operation.LogicNegate:
                    GenerateExpression(unaryNode.Expression);
                    writer.WriteLine("cmpl    $0, %eax");       // Set ZF if eax = 0
                    writer.WriteLine("movl    $0, %eax");       // Zero eax
                    writer.WriteLine("sete    %al");            // Set al (lowest byte of eax) to 1 IF ZF is set
                    break;

                case UnaryNode.Operation.Address:
                    // Store the absolute variable address in eax
                    var name = ((VariableNode)unaryNode.Expression).Name;
                    // TODO: Function addresses
                    if (!CurrentVariableMap.TryGetOffset(name, out int offset))
                    {
                        throw new GeneratorException("Reference to undeclared variable", unaryNode);
                    }

                    // Store the value of ebp + offset in eax
                    writer.WriteLine("movl    %ebp, %eax");             // Get EBP in EAX
                    writer.WriteLine("addl    ${0}, %eax", offset);     // Add offset to EAX, getting the absolute variable address

                    break;

                case UnaryNode.Operation.Indirection:
                    // Interpret eax as an address and get the value in memory at that address
                    GenerateExpression(unaryNode.Expression);
                    writer.WriteLine("movl    (%eax), %eax");
                    break;

                default:
                    throw new NotImplementedException();
                }
            }
            else if (expression is BinaryNode binaryNode)
            {
                switch (binaryNode.Op)
                {
                case BinaryNode.Operation.Add:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("addl    %ecx, %eax");         // eax = ebx + eax
                    break;

                case BinaryNode.Operation.Subtract:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("movl    %eax, %ecx");         // Move result of right hand side into EBX
                    writer.WriteLine("pop     %eax");               // Pop left hand side result off the stack into EAX
                    writer.WriteLine("subl    %ecx, %eax");         // eax = eax - ebx
                    break;

                case BinaryNode.Operation.Multiply:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("imul    %ecx, %eax");         // eax = ebx * eax
                    break;

                case BinaryNode.Operation.Divide:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("movl    %eax, %ecx");         // Move result of right hand side into EBX
                    writer.WriteLine("pop     %eax");               // Pop left hand side result off the stack into EAX
                    writer.WriteLine("movl    $0, %edx");           // Clear EDX
                    writer.WriteLine("idiv    %ecx");               // eax = (edx:eax) / ebx, note that remainder is in edx
                    break;

                case BinaryNode.Operation.Modulo:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("movl    %eax, %ecx");         // Move result of right hand side into EBX
                    writer.WriteLine("pop     %eax");               // Pop left hand side result off the stack into EAX
                    writer.WriteLine("movl    $0, %edx");           // Clear EDX
                    writer.WriteLine("idiv    %ecx");               // eax = (edx:eax) / ebx, the remainder is in edx
                    writer.WriteLine("movl    %edx, %eax");         // Move the remainder to EAX
                    break;

                case BinaryNode.Operation.Equal:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("cmpl    %ecx, %eax");         // Set ZF if eax = ebx
                    writer.WriteLine("movl    $0, %eax");           // Zero eax
                    writer.WriteLine("sete    %al");                // Set al (lowest byte of eax) to 1 IF ZF is set (e.g. ebx == eax)
                    break;

                case BinaryNode.Operation.NotEqual:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("cmpl    %ecx, %eax");         // Set ZF if eax = ebx
                    writer.WriteLine("movl    $0, %eax");           // Zero eax
                    writer.WriteLine("setne   %al");                // Set al (lowest byte of eax) to 1 IF ZF is NOT SET (e.g. ebx != eax)
                    break;

                case BinaryNode.Operation.LessThan:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("cmpl    %eax, %ecx");         // Do LHS - RHS: If LHS < RHS sign is not set
                    writer.WriteLine("movl    $0, %eax");           // Zero eax
                    writer.WriteLine("setl    %al");                // Set al (lowest byte of eax) to 1 IF SF != OF
                    break;

                case BinaryNode.Operation.LessThanOrEqual:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("cmpl    %eax, %ecx");         // Do LHS - RHS: If LHS < RHS sign is not set
                    writer.WriteLine("movl    $0, %eax");           // Zero eax
                    writer.WriteLine("setle   %al");                // Set al (lowest byte of eax) to 1 IF  SF != OF OR ZF = 1
                    break;

                case BinaryNode.Operation.GreaterThan:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("cmpl    %eax, %ecx");         // Do LHS - RHS: If LHS > RHS sign is set
                    writer.WriteLine("movl    $0, %eax");           // Zero eax
                    writer.WriteLine("setg    %al");                // Set al (lowest byte of eax) to 1 IF SF = 1
                    break;

                case BinaryNode.Operation.GreaterThanOrEqual:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("cmpl    %eax, %ecx");         // Do LHS - RHS: If LHS > RHS sign is set
                    writer.WriteLine("movl    $0, %eax");           // Zero eax
                    writer.WriteLine("setge   %al");                // Set al (lowest byte of eax) to 1 IF SF = 1 OR ZF = 1
                    break;

                case BinaryNode.Operation.LogicOr:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("orl     %ecx, %eax");         // Or eax with ebx, !(eax | ebx) == ZF
                    writer.WriteLine("movl    $0, %eax");           // Zero eax
                    writer.WriteLine("setne   %al");                // Set al (lowest byte of eax) to 1 IF ZF == 0
                    break;

                case BinaryNode.Operation.LogicAnd:
                    GenerateExpression(binaryNode.FirstTerm);       // Store the result of the left hand side in EAX
                    writer.WriteLine("push    %eax");               // Push left hand side result on stack
                    GenerateExpression(binaryNode.SecondTerm);      // Store the result of the right hand side in EAX
                    writer.WriteLine("pop     %ecx");               // Pop left hand side result off the stack into EBX
                    writer.WriteLine("cmpl    $0, %ecx");           // Set ZF iff ebx == 0
                    writer.WriteLine("setne   %cl");                // Set bl = 1 iff ebx != 0
                    writer.WriteLine("cmpl    $0, %eax");           // Set ZF iff eax == 0
                    writer.WriteLine("movl    $0, %eax");           // Clear eax
                    writer.WriteLine("setne   %al");                // Set al = 1 iff eax != 0
                    writer.WriteLine("andb    %cl, %al");           // And bl & al, al = 1 iif bl = 1 and al = 1
                    break;

                default:
                    throw new NotImplementedException();
                }
            }
            else if (expression is AssignmentNode assignmentNode)
            {
                // Variable assignment
                if (CurrentVariableMap.TryGetOffset(assignmentNode.Name, out int variableOffset))
                {
                    GenerateExpression(assignmentNode.Expression);
                    writer.WriteLine("movl    %eax, {0}(%ebp)", variableOffset);    // Store eax in memory at ebp + variableOffset
                }
                else
                {
                    throw new GeneratorException("Reference to undeclared variable", assignmentNode);
                }
            }
            else if (expression is VariableNode variableNode)
            {
                // Reference to a variable
                if (CurrentVariableMap.TryGetOffset(variableNode.Name, out int variableOffset))
                {
                    writer.WriteLine("movl    {0}(%ebp), %eax", variableOffset);    // eax = mem(ebp + variableOffset)
                }
                else
                {
                    throw new GeneratorException("Reference to undeclared variable", variableNode);
                }
            }
            else if (expression is ConditionalNode conditional)
            {
                // Conditional expression a ? b : c
                var startOfElseLabel = GetUniqueLabel();
                var endOfElseLabel   = GetUniqueLabel();

                // Condition
                GenerateExpression(conditional.Condition);
                // Branch, jump to else label if false
                writer.WriteLine("cmpl    $0, %eax");                   // ZF = (0 == eax)
                writer.WriteLine("je      {0}", startOfElseLabel);      // Jump to else if ZF == 0
                GenerateExpression(conditional.TrueExpression);         // Put in the true expression
                writer.WriteLine("jmp     {0}", endOfElseLabel);        // Jump to end
                writer.WriteLine("{0}:", startOfElseLabel);             // Put in start of else label
                GenerateExpression(conditional.FalseExpression);        // Else statement
                writer.WriteLine("{0}:", endOfElseLabel);               // Put in end of else label last
            }
            else if (expression is CallNode call)
            {
                // Function call a(params)
                var funcLabel = GetFunctionLabel(call.Name);
                // Push params on stack in reverse order
                foreach (var expr in call.Parameters.Reverse())
                {
                    GenerateExpression(expr);                                      // Generate expression for the param
                    writer.WriteLine("push    %eax");                              // Push param on the stack
                }
                writer.WriteLine("call    {0}", funcLabel);                        // Call the function
                writer.WriteLine("addl    ${0}, %esp", call.Parameters.Count * 4); // Restore stack pointer after returning
            }
            else if (expression is PostfixNode postfix)
            {
                // a-- or a++
                // Increment or decrement a but return the value of a before this happened
                GenerateExpression(postfix.Variable);                       // Get the value of a
                writer.WriteLine("push    %eax");                           // Save a on stack
                switch (postfix.Op)
                {
                case PostfixNode.Operation.Increment:
                    writer.WriteLine("addl    $1, %eax");                   // Add 1 to a
                    break;

                case PostfixNode.Operation.Decrement:
                    writer.WriteLine("subl    $1, %eax");                   // Subtract 1 from a
                    break;

                default:
                    throw new NotImplementedException();
                }
                writer.WriteLine("movl    %eax, {0}(%ebp)",
                                 CurrentVariableMap.GetOffset(postfix.Variable.Name)); // Store a back in memory
                writer.WriteLine("pop     %eax");                                      // Return old value of a in EAX
            }
            else
            {
                throw new NotImplementedException();
            }
        }