public override AstNode Visit(BinaryOperation node) { // Visit recursively. node.GetLeftExpression().Accept(this); node.GetRightExpression().Accept(this); return node; }
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()); }
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(); }
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; }