blob: 6bd7c3b8ddd401f2e13a4ae3eab316fc19b74356 [file] [log] [blame] [edit]
// 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