| // Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | #include "vm/compiler/backend/evaluator.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | static IntegerPtr BinaryIntegerEvaluateRaw(const Integer& left, | 
 |                                            const Integer& right, | 
 |                                            Token::Kind token_kind) { | 
 |   switch (token_kind) { | 
 |     case Token::kTRUNCDIV: | 
 |       FALL_THROUGH; | 
 |     case Token::kMOD: | 
 |       // Check right value for zero. | 
 |       if (right.Value() == 0) { | 
 |         break;  // Will throw. | 
 |       } | 
 |       FALL_THROUGH; | 
 |     case Token::kADD: | 
 |       FALL_THROUGH; | 
 |     case Token::kSUB: | 
 |       FALL_THROUGH; | 
 |     case Token::kMUL: | 
 |       return left.ArithmeticOp(token_kind, right, Heap::kOld); | 
 |     case Token::kSHL: | 
 |       FALL_THROUGH; | 
 |     case Token::kSHR: | 
 |       FALL_THROUGH; | 
 |     case Token::kUSHR: | 
 |       if (right.Value() >= 0) { | 
 |         return left.ShiftOp(token_kind, right, Heap::kOld); | 
 |       } | 
 |       break; | 
 |     case Token::kBIT_AND: | 
 |       FALL_THROUGH; | 
 |     case Token::kBIT_OR: | 
 |       FALL_THROUGH; | 
 |     case Token::kBIT_XOR: | 
 |       return left.BitOp(token_kind, right, Heap::kOld); | 
 |     case Token::kDIV: | 
 |       break; | 
 |     default: | 
 |       UNREACHABLE(); | 
 |   } | 
 |  | 
 |   return Integer::null(); | 
 | } | 
 |  | 
 | static IntegerPtr UnaryIntegerEvaluateRaw(const Integer& value, | 
 |                                           Token::Kind token_kind, | 
 |                                           Zone* zone) { | 
 |   switch (token_kind) { | 
 |     case Token::kNEGATE: | 
 |       return value.ArithmeticOp(Token::kMUL, Smi::Handle(zone, Smi::New(-1)), | 
 |                                 Heap::kOld); | 
 |     case Token::kBIT_NOT: | 
 |       if (value.IsInteger()) { | 
 |         return Integer::New(~value.Value(), Heap::kOld); | 
 |       } | 
 |       break; | 
 |     default: | 
 |       UNREACHABLE(); | 
 |   } | 
 |   return Integer::null(); | 
 | } | 
 |  | 
 | static IntegerPtr BitLengthEvaluateRaw(const Integer& value, Zone* zone) { | 
 |   if (value.IsInteger()) { | 
 |     return Integer::New(Utils::BitLength(value.Value()), Heap::kOld); | 
 |   } | 
 |   return Integer::null(); | 
 | } | 
 |  | 
 | int64_t Evaluator::TruncateTo(int64_t v, Representation r) { | 
 |   switch (r) { | 
 |     case kTagged: { | 
 |       const intptr_t kTruncateBits = | 
 |           kBitsPerInt64 - (compiler::target::kSmiBits + 1 /*sign bit*/); | 
 |       return Utils::ShiftLeftWithTruncation(v, kTruncateBits) >> kTruncateBits; | 
 |     } | 
 |     case kUnboxedInt32: | 
 |       return Utils::ShiftLeftWithTruncation(v, kBitsPerInt32) >> kBitsPerInt32; | 
 |     case kUnboxedUint32: | 
 |       return v & kMaxUint32; | 
 |     case kUnboxedInt64: | 
 |       return v; | 
 |     default: | 
 |       UNREACHABLE(); | 
 |   } | 
 | } | 
 |  | 
 | IntegerPtr Evaluator::BinaryIntegerEvaluate(const Object& left, | 
 |                                             const Object& right, | 
 |                                             Token::Kind token_kind, | 
 |                                             bool is_truncating, | 
 |                                             Representation representation, | 
 |                                             Thread* thread) { | 
 |   if (!left.IsInteger() || !right.IsInteger()) { | 
 |     return Integer::null(); | 
 |   } | 
 |   Zone* zone = thread->zone(); | 
 |   const Integer& left_int = Integer::Cast(left); | 
 |   const Integer& right_int = Integer::Cast(right); | 
 |   Integer& result = Integer::Handle( | 
 |       zone, BinaryIntegerEvaluateRaw(left_int, right_int, token_kind)); | 
 |  | 
 |   if (!result.IsNull()) { | 
 |     if (is_truncating) { | 
 |       const int64_t truncated = TruncateTo(result.Value(), representation); | 
 |       result = Integer::New(truncated, Heap::kOld); | 
 |       ASSERT(FlowGraph::IsConstantRepresentable( | 
 |           result, representation, /*tagged_value_must_be_smi=*/true)); | 
 |     } else if (!FlowGraph::IsConstantRepresentable( | 
 |                    result, representation, /*tagged_value_must_be_smi=*/true)) { | 
 |       // If this operation is not truncating it would deoptimize on overflow. | 
 |       // Check that we match this behavior and don't produce a value that is | 
 |       // larger than something this operation can produce. We could have | 
 |       // specialized instructions that use this value under this assumption. | 
 |       return Integer::null(); | 
 |     } | 
 |     result ^= result.Canonicalize(thread); | 
 |   } | 
 |  | 
 |   return result.ptr(); | 
 | } | 
 |  | 
 | IntegerPtr Evaluator::UnaryIntegerEvaluate(const Object& value, | 
 |                                            Token::Kind token_kind, | 
 |                                            Representation representation, | 
 |                                            Thread* thread) { | 
 |   if (!value.IsInteger()) { | 
 |     return Integer::null(); | 
 |   } | 
 |   Zone* zone = thread->zone(); | 
 |   const Integer& value_int = Integer::Cast(value); | 
 |   Integer& result = Integer::Handle( | 
 |       zone, UnaryIntegerEvaluateRaw(value_int, token_kind, zone)); | 
 |  | 
 |   if (!result.IsNull()) { | 
 |     if (!FlowGraph::IsConstantRepresentable( | 
 |             result, representation, | 
 |             /*tagged_value_must_be_smi=*/true)) { | 
 |       // If this operation is not truncating it would deoptimize on overflow. | 
 |       // Check that we match this behavior and don't produce a value that is | 
 |       // larger than something this operation can produce. We could have | 
 |       // specialized instructions that use this value under this assumption. | 
 |       return Integer::null(); | 
 |     } | 
 |  | 
 |     result ^= result.Canonicalize(thread); | 
 |   } | 
 |  | 
 |   return result.ptr(); | 
 | } | 
 |  | 
 | IntegerPtr Evaluator::BitLengthEvaluate(const Object& value, | 
 |                                         Representation representation, | 
 |                                         Thread* thread) { | 
 |   if (!value.IsInteger()) { | 
 |     return Integer::null(); | 
 |   } | 
 |   Zone* zone = thread->zone(); | 
 |   const Integer& value_int = Integer::Cast(value); | 
 |   Integer& result = | 
 |       Integer::Handle(zone, BitLengthEvaluateRaw(value_int, zone)); | 
 |  | 
 |   if (!result.IsNull()) { | 
 |     if (!FlowGraph::IsConstantRepresentable( | 
 |             result, representation, | 
 |             /*tagged_value_must_be_smi=*/true)) { | 
 |       // If this operation is not truncating it would deoptimize on overflow. | 
 |       // Check that we match this behavior and don't produce a value that is | 
 |       // larger than something this operation can produce. We could have | 
 |       // specialized instructions that use this value under this assumption. | 
 |       return Integer::null(); | 
 |     } | 
 |  | 
 |     result ^= result.Canonicalize(thread); | 
 |   } | 
 |  | 
 |   return result.ptr(); | 
 | } | 
 |  | 
 | double Evaluator::EvaluateUnaryDoubleOp(const double value, | 
 |                                         Token::Kind token_kind, | 
 |                                         Representation representation) { | 
 |   // The different set of operations for float32 and float64 is due to the | 
 |   // different set of operations made available by dart:core.double and | 
 |   // dart:typed_data.Float64x2 versus dart:typed_data.Float32x4. | 
 |   if (representation == kUnboxedDouble) { | 
 |     switch (token_kind) { | 
 |       case Token::kABS: | 
 |         return fabs(value); | 
 |       case Token::kNEGATE: | 
 |         return -value; | 
 |       case Token::kSQRT: | 
 |         return sqrt(value); | 
 |       case Token::kSQUARE: | 
 |         return value * value; | 
 |       case Token::kTRUNCATE: | 
 |         return trunc(value); | 
 |       case Token::kFLOOR: | 
 |         return floor(value); | 
 |       case Token::kCEILING: | 
 |         return ceil(value); | 
 |       default: | 
 |         UNREACHABLE(); | 
 |     } | 
 |   } else { | 
 |     ASSERT(representation == kUnboxedFloat); | 
 |     switch (token_kind) { | 
 |       case Token::kABS: | 
 |         return fabsf(static_cast<float>(value)); | 
 |       case Token::kNEGATE: | 
 |         return -static_cast<float>(value); | 
 |       case Token::kRECIPROCAL: | 
 |         return 1.0f / static_cast<float>(value); | 
 |       case Token::kRECIPROCAL_SQRT: | 
 |         return sqrtf(1.0f / static_cast<float>(value)); | 
 |       case Token::kSQRT: | 
 |         return sqrtf(static_cast<float>(value)); | 
 |       case Token::kSQUARE: | 
 |         return static_cast<float>(value) * static_cast<float>(value); | 
 |       default: | 
 |         UNREACHABLE(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | double Evaluator::EvaluateBinaryDoubleOp(const double left, | 
 |                                          const double right, | 
 |                                          Token::Kind token_kind, | 
 |                                          Representation representation) { | 
 |   if (representation == kUnboxedDouble) { | 
 |     switch (token_kind) { | 
 |       case Token::kADD: | 
 |         return left + right; | 
 |       case Token::kSUB: | 
 |         return left - right; | 
 |       case Token::kMUL: | 
 |         return left * right; | 
 |       case Token::kDIV: | 
 |         return Utils::DivideAllowZero(left, right); | 
 |       case Token::kMIN: | 
 |         return fmin(left, right); | 
 |       case Token::kMAX: | 
 |         return fmax(left, right); | 
 |       default: | 
 |         UNREACHABLE(); | 
 |     } | 
 |   } else { | 
 |     ASSERT(representation == kUnboxedFloat); | 
 |     switch (token_kind) { | 
 |       case Token::kADD: | 
 |         return static_cast<float>(left) + static_cast<float>(right); | 
 |       case Token::kSUB: | 
 |         return static_cast<float>(left) - static_cast<float>(right); | 
 |       case Token::kMUL: | 
 |         return static_cast<float>(left) * static_cast<float>(right); | 
 |       case Token::kDIV: | 
 |         return Utils::DivideAllowZero(static_cast<float>(left), | 
 |                                       static_cast<float>(right)); | 
 |       case Token::kMIN: | 
 |         return fminf(static_cast<float>(left), static_cast<float>(right)); | 
 |       case Token::kMAX: | 
 |         return fmaxf(static_cast<float>(left), static_cast<float>(right)); | 
 |       default: | 
 |         UNREACHABLE(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | bool Evaluator::ToIntegerConstant(Value* value, int64_t* result) { | 
 |   if (!value->BindsToConstant()) { | 
 |     UnboxInstr* unbox = value->definition()->AsUnbox(); | 
 |     if (unbox != nullptr) { | 
 |       switch (unbox->representation()) { | 
 |         case kUnboxedDouble: | 
 |         case kUnboxedInt64: | 
 |           return ToIntegerConstant(unbox->value(), result); | 
 |         case kUnboxedUint32: | 
 |           if (ToIntegerConstant(unbox->value(), result)) { | 
 |             *result = Evaluator::TruncateTo(*result, kUnboxedUint32); | 
 |             return true; | 
 |           } | 
 |           break; | 
 |           // No need to handle Unbox<Int32>(Constant(C)) because it gets | 
 |           // canonicalized to UnboxedConstant<Int32>(C). | 
 |         case kUnboxedInt32: | 
 |         default: | 
 |           break; | 
 |       } | 
 |     } | 
 |     return false; | 
 |   } | 
 |   const Object& constant = value->BoundConstant(); | 
 |   if (constant.IsDouble()) { | 
 |     const Double& double_constant = Double::Cast(constant); | 
 |     *result = Utils::SafeDoubleToInt<int64_t>(double_constant.value()); | 
 |     return (static_cast<double>(*result) == double_constant.value()); | 
 |   } else if (constant.IsInteger()) { | 
 |     *result = Integer::Cast(constant).Value(); | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | }  // namespace dart |