// 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(); } 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(); } 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(Constant(C)) because it gets // canonicalized to UnboxedConstant(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(double_constant.value()); return (static_cast(*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