public override AstNode Visit(BinaryOperation node)
 {
     // Visit recursively.
     node.GetLeftExpression().Accept(this);
     node.GetRightExpression().Accept(this);
     return node;
 }
Exemple #2
0
        public override AstNode Visit(BinaryOperation node)
        {
            // Process the left expression.
            Expression left = node.GetLeftExpression();
            left = (Expression)left.Accept(this);
            node.SetLeftExpression(left);

            // Process the right expression.
            Expression right = node.GetRightExpression();
            right = (Expression)right.Accept(this);
            node.SetRightExpression(node);

            // Get the node type.
            IChelaType nodeType = node.GetNodeType();
            if(!nodeType.IsConstant())
                return node;

            // Perform left constant coercion.
            ConstantValue leftConstant = (ConstantValue)left.GetNodeValue();
            IChelaType leftType = left.GetNodeType();
            IChelaType leftCoercion = node.GetCoercionType();
            if(leftType != leftCoercion)
                leftConstant = leftConstant.Cast(leftCoercion);

            // Perform right constant coercion.
            ConstantValue rightConstant = (ConstantValue)right.GetNodeValue();
            IChelaType rightType = right.GetNodeType();
            IChelaType rightCoercion = node.GetSecondCoercion();
            if(rightType != rightCoercion)
                rightConstant = rightConstant.Cast(rightCoercion);

            // Perform the constant operation.
            ConstantValue res = null;
            switch(node.GetOperation())
            {
            case BinaryOperation.OpAdd:
                res = leftConstant + rightConstant;
                break;
            case BinaryOperation.OpSub:
                res = leftConstant - rightConstant;
                break;
            case BinaryOperation.OpMul:
                res = leftConstant * rightConstant;
                break;
            case BinaryOperation.OpDiv:
                res = leftConstant / rightConstant;
                break;
            case BinaryOperation.OpMod:
                res = leftConstant % rightConstant;
                break;
            case BinaryOperation.OpEQ:
                res = ConstantValue.Equals(leftConstant, rightConstant);
                break;
            case BinaryOperation.OpNEQ:
                res = ConstantValue.NotEquals(leftConstant, rightConstant);
                break;
            case BinaryOperation.OpLT:
                res = leftConstant < rightConstant;
                break;
            case BinaryOperation.OpLEQ:
                res = leftConstant <= rightConstant;
                break;
            case BinaryOperation.OpGT:
                res = leftConstant > rightConstant;
                break;
            case BinaryOperation.OpGEQ:
                res = leftConstant >= rightConstant;
                break;
            case BinaryOperation.OpBitAnd:
                res = leftConstant & rightConstant;
                break;
            case BinaryOperation.OpBitOr:
                res = leftConstant | rightConstant;
                break;
            case BinaryOperation.OpBitXor:
                res = leftConstant ^ rightConstant;
                break;
            case BinaryOperation.OpBitLeft:
                res = ConstantValue.BitLeft(leftConstant, rightConstant);
                break;
            case BinaryOperation.OpBitRight:
                res = ConstantValue.BitRight(leftConstant, rightConstant);
                break;
            case BinaryOperation.OpLAnd:
                res = new ConstantValue(leftConstant.GetBoolValue() && rightConstant.GetBoolValue());
                break;
            case BinaryOperation.OpLOr:
                res = new ConstantValue(leftConstant.GetBoolValue() || rightConstant.GetBoolValue());
                break;
            default:
                throw new System.NotImplementedException();
            }

            // Return the result node.
            return res.ToAstNode(node.GetPosition());
        }
Exemple #3
0
        public override AstNode Visit(BinaryOperation node)
        {
            // Begin the node.
            builder.BeginNode(node);

            // Get the expressions.
            Expression left = node.GetLeftExpression();
            Expression right = node.GetRightExpression();

            // Get the types.
            IChelaType leftType = left.GetNodeType();
            IChelaType rightType = right.GetNodeType();
            IChelaType coercionType = node.GetCoercionType();
            IChelaType secondCoercion = node.GetSecondCoercion();
            IChelaType operationType = node.GetOperationType();
            IChelaType destType = node.GetNodeType();

            // Special pipeline for operator overloading.
            Function overload = node.GetOverload();
            if(overload != null)
            {
                FunctionType overloadType = overload.GetFunctionType();
                coercionType = overloadType.GetArgument(0);
                secondCoercion = overloadType.GetArgument(1);

                // Send first the left operand.
                left.Accept(this);
                if(leftType != coercionType)
                    Cast(node, left.GetNodeValue(), leftType, coercionType);

                // Now send the right operand.
                right.Accept(this);
                if(rightType != secondCoercion)
                    Cast(node, right.GetNodeValue(), rightType, secondCoercion);

                // Perform the call.
                builder.CreateCall(overload, 2);

                // Return the node.
                return builder.EndNode();
            }

            // Send the left operand.
            left.Accept(this);
            if(leftType != coercionType)
                Cast(node, left.GetNodeValue(), leftType, coercionType);

            // Short circuit operations has special evaluation orders.
            if(node.GetOperation() == BinaryOperation.OpLAnd ||
               node.GetOperation() == BinaryOperation.OpLOr)
            {
                BasicBlock continueBlock = CreateBasicBlock();
                continueBlock.SetName("shc");

                BasicBlock stopBlock = CreateBasicBlock();
                stopBlock.SetName("shs");

                BasicBlock mergeBlock = CreateBasicBlock();
                mergeBlock.SetName("shm");

                // Perform left branch.
                if(node.GetOperation() == BinaryOperation.OpLAnd)
                    builder.CreateBr(continueBlock, stopBlock);
                else
                    builder.CreateBr(stopBlock, continueBlock);

                // Build stop.
                builder.SetBlock(stopBlock);
                builder.CreateLoadBool(node.GetOperation() == BinaryOperation.OpLOr);
                builder.CreateJmp(mergeBlock);

                // Build continue block.
                builder.SetBlock(continueBlock);

                // Send the right operand verbatim.
                right.Accept(this);
                if(rightType != secondCoercion)
                    Cast(node, right.GetNodeValue(), rightType, secondCoercion);
                builder.CreateJmp(mergeBlock);

                // Continue with the control flow.
                builder.SetBlock(mergeBlock);
                return builder.EndNode();
            }

            // Send the right operand.
            right.Accept(this);
            if(rightType != secondCoercion)
                Cast(node, right.GetNodeValue(), rightType, secondCoercion);

            switch(node.GetOperation())
            {
            case BinaryOperation.OpAdd:
                builder.CreateAdd();
                break;
            case BinaryOperation.OpSub:
                builder.CreateSub();
                break;
            case BinaryOperation.OpMul:
                if(node.IsMatrixMul())
                    builder.CreateMatMul();
                else
                    builder.CreateMul();
                break;
            case BinaryOperation.OpDiv:
                builder.CreateDiv();
                break;
            case BinaryOperation.OpMod:
                builder.CreateMod();
                break;
            case BinaryOperation.OpBitAnd:
                builder.CreateAnd();
                break;
            case BinaryOperation.OpBitOr:
                builder.CreateOr();
                break;
            case BinaryOperation.OpBitXor:
                builder.CreateXor();
                break;
            case BinaryOperation.OpBitLeft:
                builder.CreateShLeft();
                break;
            case BinaryOperation.OpBitRight:
                builder.CreateShRight();
                break;
            case BinaryOperation.OpLT:
                builder.CreateCmpLT();
                break;
            case BinaryOperation.OpGT:
                builder.CreateCmpGT();
                break;
            case BinaryOperation.OpEQ:
                builder.CreateCmpEQ();
                break;
            case BinaryOperation.OpNEQ:
                builder.CreateCmpNE();
                break;
            case BinaryOperation.OpLEQ:
                builder.CreateCmpLE();
                break;
            case BinaryOperation.OpGEQ:
                builder.CreateCmpGE();
                break;
            case BinaryOperation.OpLAnd:
            case BinaryOperation.OpLOr:
                // Shouldn't reach here.
                break;
            }

            // Cast the result.
            if(operationType != destType)
                Cast(node, null, operationType, destType);

            return builder.EndNode();
        }
Exemple #4
0
        public override AstNode Visit(BinaryOperation node)
        {
            // Get the expressions.
            Expression left = node.GetLeftExpression();
            Expression right = node.GetRightExpression();

            // Make sure they are linked, used by operator overloading.
            if(left.GetNext() != null || right.GetNext() != null)
                Error(node, "binary operation arguments linked.");
            left.SetNext(right);

            // Visit the expressions.
            left.Accept(this);
            right.Accept(this);

            // Check the types.
            IChelaType leftType = left.GetNodeType();
            IChelaType rightType = right.GetNodeType();
            IChelaType nullRef = ChelaType.GetNullType();

            // Dereference and de-const types.
            bool leftConstant = leftType.IsConstant();
            leftType = DeConstType(leftType); // Required for string constant.
            if(leftType != nullRef && leftType.IsReference())
            {
                leftType = DeReferenceType(leftType);
                leftConstant = leftConstant || leftType.IsConstant();
                leftType = DeConstType(leftType);
                leftType = DeReferenceType(leftType);
            }

            leftConstant = leftConstant || leftType.IsConstant();
            leftType = DeConstType(leftType);

            // De-reference and de-const the right type.
            bool rightConstant = rightType.IsConstant();
            rightType = DeConstType(rightType);
            if(rightType != nullRef && rightType.IsReference())
            {
                rightType = DeReferenceType(rightType);
                rightConstant = rightConstant || rightType.IsConstant();
                rightType = DeConstType(rightType);
                rightType = DeReferenceType(rightType);
            }
            rightType = DeConstType(rightType);

            // Is this a constant.
            bool isConstant = leftConstant && rightConstant;

            // Copy the deref and deconst types.
            IChelaType actualLeftType = leftType;
            IChelaType actualRightType = rightType;

            // Check for operator overloading.
            string opName = BinaryOperation.GetOpName(node.GetOperation());
            Function overload = null;
            if((leftType.IsStructure() || leftType.IsClass()) &&
               opName != null)
            {
                // Find the operation function.
                Structure building = (Structure)leftType;
                FunctionGroup opGroup = (FunctionGroup)building.FindMember(opName);
                if(opGroup != null)
                {
                    // Pick the function from the group.
                    overload = PickFunction(node, opGroup, true, null, left, true);
                }
            }

            // Now check at the right type.
            if((rightType.IsStructure() || rightType.IsClass()) &&
               opName != null && overload == null)
            {
                // Find the operation function.
                Structure building = (Structure)rightType;
                FunctionGroup opGroup = (FunctionGroup)building.FindMember(opName);
                if(opGroup  != null)
                {
                    // Pick the function from the group.
                    overload = PickFunction(node, opGroup, true, null, left, true);
                }
            }

            // Use the found overload.
            if(overload != null)
            {
                // Set the operator overload.
                FunctionType overloadType = overload.GetFunctionType();
                node.SetNodeType(overloadType.GetReturnType());
                node.SetOverload(overload);
                return node;
            }

            // Check for pointer arithmetic.
            int op = node.GetOperation();
            if((leftType.IsPointer() || rightType.IsPointer()) &&
               (op == BinaryOperation.OpAdd || op == BinaryOperation.OpSub))
            {
                // Only addition and subtraction are accepted.
                if(leftType.IsPointer() && rightType.IsPointer())
                {
                    if(op == BinaryOperation.OpAdd)
                        Error(node, "cannot add two pointers.");

                    // Set the coercion type and pointer arithmetic flags.
                    node.SetNodeType(ChelaType.GetLongType());
                    node.SetCoercionType(leftType);
                    node.SetSecondCoercion(rightType);
                    node.SetPointerArithmetic(true);
                }
                else
                {
                    // Check type compatibility.
                    if(op == BinaryOperation.OpSub && !leftType.IsPointer())
                        Error(node, "cannot perform integer - pointer subtraction.");
                    else if((leftType.IsPointer() && !rightType.IsInteger()) ||
                            (!leftType.IsInteger() && rightType.IsPointer()))
                        Error(node, "only pointers and integers can be used in pointer arithmetic.");

                    // Set the coercion type and pointer arithmetic flags.
                    node.SetNodeType(leftType.IsPointer() ? leftType : rightType);
                    node.SetCoercionType(leftType);
                    node.SetSecondCoercion(rightType);
                    node.SetPointerArithmetic(true);
                }

                // There aren't intermediate operations.
                node.SetOperationType(node.GetNodeType());

                return node;
            }
            else
            {
                // Read the types again.
                leftType = left.GetNodeType();
                rightType = right.GetNodeType();
            }

            // Perform coercion.
            IChelaType destType = Coerce(node, leftType, rightType, right.GetNodeValue());
            if(destType == null)
                destType = Coerce(node, rightType, leftType, left.GetNodeValue());

            // Set the node coercion type.
            node.SetCoercionType(destType);
            node.SetSecondCoercion(destType);

            // Checks for matrix multiplication.
            if(BinaryOperation.OpMul == op)
            {
                // Use the actual types.
                leftType = actualLeftType;
                rightType = actualRightType;

                if(leftType.IsMatrix() && rightType.IsMatrix())
                {
                    // Cast the matrix.
                    MatrixType leftMatrix = (MatrixType)leftType;
                    MatrixType rightMatrix = (MatrixType)rightType;

                    // The number of first matrix columns must be equal
                    // to the number of the second matrix rows.
                    if(leftMatrix.GetNumColumns() != rightMatrix.GetNumRows())
                        Error(node, "not matching number of matrix column and rows");

                    // Coerce the primitive types.
                    IChelaType primCoercion = Coerce(leftMatrix.GetPrimitiveType(), rightMatrix.GetPrimitiveType());
                    if(primCoercion == null)
                        Error(node, "cannot multiply a {0} by a{1}", leftMatrix.GetDisplayName(), rightMatrix.GetDisplayName());
                    node.SetCoercionType(MatrixType.Create(primCoercion, leftMatrix.GetNumRows(), leftMatrix.GetNumColumns()));
                    node.SetSecondCoercion(MatrixType.Create(primCoercion, rightMatrix.GetNumRows(), rightMatrix.GetNumColumns()));

                    // Create the destination type.
                    destType = MatrixType.Create(primCoercion, leftMatrix.GetNumRows(), rightMatrix.GetNumColumns());
                    node.SetMatrixMul(true);
                }
                else if(leftType.IsMatrix() && rightType.IsVector())
                {
                    // Cast the matrix and the vector.
                    MatrixType leftMatrix = (MatrixType)leftType;
                    VectorType rightVector = (VectorType)rightType;

                    // Check the dimensions.
                    if(leftMatrix.GetNumColumns() != rightVector.GetNumComponents())
                        Error(node, "not matching number of matrix column and rows");

                    // Coerce the primitive types.
                    IChelaType primCoercion = Coerce(leftMatrix.GetPrimitiveType(), rightVector.GetPrimitiveType());
                    if(primCoercion == null)
                        Error(node, "cannot multiply a {0} by a {1}", leftMatrix.GetDisplayName(), rightVector.GetDisplayName());
                    node.SetCoercionType(MatrixType.Create(primCoercion, leftMatrix.GetNumRows(), leftMatrix.GetNumColumns()));
                    node.SetSecondCoercion(VectorType.Create(primCoercion, rightVector.GetNumComponents()));

                    // Create the destination type.
                    destType = VectorType.Create(primCoercion, leftMatrix.GetNumRows());
                    node.SetMatrixMul(true);
                }
                else if(rightType.IsMatrix() && leftType.IsVector())
                {
                    // Cast the matrix and the vector.
                    VectorType leftVector = (VectorType)leftType;
                    MatrixType rightMatrix = (MatrixType)rightType;

                    // Check the dimensions.
                    if(leftVector.GetNumComponents() != rightMatrix.GetNumRows())
                        Error(node, "not matching number of matrix column and rows");

                    // Coerce the primitive types.
                    IChelaType primCoercion = Coerce(leftVector.GetPrimitiveType(), rightMatrix.GetPrimitiveType());
                    if(primCoercion == null)
                        Error(node, "cannot multiply a {0} by a {1}", leftVector.GetDisplayName(), rightMatrix.GetDisplayName());
                    node.SetCoercionType(VectorType.Create(primCoercion, leftVector.GetNumComponents()));
                    node.SetSecondCoercion(MatrixType.Create(primCoercion, rightMatrix.GetNumRows(), rightMatrix.GetNumColumns()));

                    // Create the destination type.
                    destType = VectorType.Create(primCoercion, rightMatrix.GetNumColumns());
                    node.SetMatrixMul(true);
                }
            }

            // Check for special operations
            if(destType == null)
            {
                // Use the actual types.
                leftType = actualLeftType;
                rightType = actualRightType;

                // Check for external operations.
                if(leftType.IsVector() && (op == BinaryOperation.OpDiv
                   || op == BinaryOperation.OpMul))
                {
                    // Select the types.
                    VectorType vectorType = (VectorType)leftType;
                    IChelaType primitiveType = rightType;

                    // Coerce the primitive type.
                    IChelaType secondCoercion = vectorType.GetPrimitiveType();
                    if(primitiveType != secondCoercion &&
                       Coerce(secondCoercion, primitiveType) != secondCoercion)
                        Error(node, "failed to perform scalar operation.");

                    // Store the new coercion types.
                    destType = vectorType;
                    node.SetCoercionType(vectorType);
                    node.SetSecondCoercion(secondCoercion);
                }
                else if(rightType.IsVector() && op == BinaryOperation.OpMul)
                {
                    // Select the types.
                    IChelaType primitiveType = leftType;
                    VectorType vectorType = (VectorType)rightType;

                    // Coerce the primitive type.
                    IChelaType firstCoercion = vectorType.GetPrimitiveType();
                    if(primitiveType != firstCoercion &&
                       Coerce(firstCoercion, primitiveType) != firstCoercion)
                        Error(node, "failed to perform scalar operation.");

                    // Store the new coercion types.
                    destType = vectorType;
                    node.SetCoercionType(firstCoercion);
                    node.SetSecondCoercion(vectorType);
                }
                else
                    Error(node, "failed to perform binary operation, invalid operands of type {0} and {1}.",
                        leftType.GetDisplayName(), rightType.GetDisplayName());
            }

            // De-const the type.
            destType = DeConstType(destType);

            // Dest type must be integer, float or boolean.
            IChelaType operationType = destType;
            if(!destType.IsNumber() && !destType.IsPointer() &&
                !BinaryOperation.IsEquality(op))
            {
                // Try to operate with the unboxed types.
                // Only structures are box holders.
                if(!destType.IsStructure())
                    Error(node, "cannot perform binary operations with type {0}.", destType.GetDisplayName());

                // Use the structure associated type, or use his enum type.
                Structure building = (Structure)destType;
                IChelaType assocType = currentModule.GetAssociatedClassPrimitive(building);
                if(assocType == null)
                {
                    // The structure can be an enumeration.
                    Class enumClass = currentModule.GetEnumClass();
                    if(!building.IsDerivedFrom(enumClass))
                        Error(node, "cannot perform operation {0} with object of types {1}.",
                            BinaryOperation.GetOpErrorName(op),
                            building.GetDisplayName());
                }

                // Unbox the structure.
                FieldVariable valueField = building.FindMember("__value") as FieldVariable;
                if(valueField == null)
                    valueField = building.FindMember("m_value") as FieldVariable;
                if(valueField == null)
                    Error(node, "structure {0} is not boxing a primitive.", building.GetDisplayName());
                operationType = valueField.GetVariableType();
                node.SetCoercionType(operationType);
                node.SetSecondCoercion(operationType);
            }

            // Use integers of at least 4 bytes.
            if(!operationType.IsPlaceHolderType() && operationType.IsInteger() &&
                operationType.GetSize() < 4 && BinaryOperation.IsArithmetic(op))
            {
                destType = ChelaType.GetIntType();
                operationType = destType;
                IChelaType coercionType = destType;
                if(isConstant)
                    coercionType = ConstantType.Create(destType);
                node.SetCoercionType(coercionType);
                node.SetSecondCoercion(coercionType);
            }

            // Perform validation
            switch(op)
            {
            case BinaryOperation.OpAdd:
            case BinaryOperation.OpMul:
            case BinaryOperation.OpDiv:
            case BinaryOperation.OpMod:
                if(operationType == ChelaType.GetBoolType())
                    Error(node, "Arithmetic operations aren't available with boolean values.");
                break;

            case BinaryOperation.OpSub:
                if(operationType == ChelaType.GetBoolType())
                    Error(node, "Arithmetic operations aren't available with boolean values.");

                // Use signed integer when subtracting.
                if(operationType.IsInteger() && operationType.IsUnsigned())
                {
                    // Use a new dest type and coercion type.
                    IntegerType intType = (IntegerType)operationType;
                    operationType = intType.GetSignedVersion();

                    IChelaType newCoercion = destType;
                    if(isConstant)
                        newCoercion = ConstantType.Create(intType);

                    // Override the result types.
                    node.SetCoercionType(newCoercion);
                    node.SetSecondCoercion(newCoercion);
                    destType = operationType;
                }
                break;
            case BinaryOperation.OpBitAnd:
            case BinaryOperation.OpBitOr:
            case BinaryOperation.OpBitXor:
            case BinaryOperation.OpBitLeft:
            case BinaryOperation.OpBitRight:
                if(operationType.IsFloatingPoint() || operationType == ChelaType.GetBoolType())
                    Error(node, "Bitwise operations aren't available with floating point and boolean values.");
                break;
            case BinaryOperation.OpLT:
            case BinaryOperation.OpGT:
            case BinaryOperation.OpEQ:
            case BinaryOperation.OpNEQ:
            case BinaryOperation.OpLEQ:
            case BinaryOperation.OpGEQ:
                // Boolean result.
                operationType = ChelaType.GetBoolType();
                destType = operationType;
                break;
            case BinaryOperation.OpLAnd:
            case BinaryOperation.OpLOr:
                // Left and right must be boolean.
                if(operationType != ChelaType.GetBoolType())
                    Error(node, "Expected boolean arguments.");
                break;
            default:
                Error(node, "Compiler bug, unknown binary operation.");
                break;
            }

            // Set the destination type.
            if(isConstant)
            {
                node.SetOperationType(ConstantType.Create(operationType));
                node.SetNodeType(ConstantType.Create(destType));
            }
            else
            {
                node.SetOperationType(operationType);
                node.SetNodeType(destType);
            }

            return node;
        }