|  | // 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.AsInt64Value() == 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.AsInt64Value() >= 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.IsSmi()) { | 
|  | return Integer::New(~Smi::Cast(value).Value(), Heap::kOld); | 
|  | } else if (value.IsMint()) { | 
|  | return Integer::New(~Mint::Cast(value).value(), Heap::kOld); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | return Integer::null(); | 
|  | } | 
|  |  | 
|  | static IntegerPtr BitLengthEvaluateRaw(const Integer& value, Zone* zone) { | 
|  | if (value.IsSmi()) { | 
|  | return Integer::New(Utils::BitLength(Smi::Cast(value).Value()), Heap::kOld); | 
|  | } else if (value.IsMint()) { | 
|  | return Integer::New(Utils::BitLength(Mint::Cast(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.AsTruncatedInt64Value(), 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::EvaluateDoubleOp(const double left, | 
|  | const double right, | 
|  | Token::Kind token_kind) { | 
|  | 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 left / 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.IsSmi()) { | 
|  | *result = Smi::Cast(constant).Value(); | 
|  | return true; | 
|  | } else if (constant.IsMint()) { | 
|  | *result = Mint::Cast(constant).value(); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | }  // namespace dart |