dart-sdk/runtime/lib/integers.cc
dgrove@google.com 4c0f559d23 Initial checkin.
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15 260f80e4-7a28-3924-810f-c04153c831b5
2011-10-05 05:20:07 +00:00

587 lines
18 KiB
C++

// Copyright (c) 2011, 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/bootstrap_natives.h"
#include "vm/bigint_operations.h"
#include "vm/dart_entry.h"
#include "vm/native_entry.h"
#include "vm/object.h"
namespace dart {
DEFINE_FLAG(bool, trace_intrinsified_natives, false,
"Report if any of the intrinsified natives are called");
// Smi natives.
// Return the most compact presentation of an integer.
static RawInteger* AsInteger(const Integer& value) {
if (value.IsSmi()) return value.raw();
if (value.IsMint()) {
Mint& mint = Mint::Handle();
mint ^= value.raw();
if (Smi::IsValid64(mint.value())) {
return Smi::New(mint.value());
} else {
return value.raw();
}
}
ASSERT(value.IsBigint());
Bigint& big_value = Bigint::Handle();
big_value ^= value.raw();
if (BigintOperations::FitsIntoSmi(big_value)) {
return BigintOperations::ToSmi(big_value);
} else if (BigintOperations::FitsIntoInt64(big_value)) {
return Mint::New(BigintOperations::ToInt64(big_value));
} else {
return big_value.raw();
}
}
// Returns value in form of a RawBigint.
static RawBigint* AsBigint(const Integer& value) {
ASSERT(!value.IsNull());
if (value.IsSmi()) {
Smi& smi = Smi::Handle();
smi ^= value.raw();
return BigintOperations::NewFromSmi(smi);
} else if (value.IsMint()) {
Mint& mint = Mint::Handle();
mint ^= value.raw();
return BigintOperations::NewFromInt64(mint.value());
} else {
ASSERT(value.IsBigint());
Bigint& big = Bigint::Handle();
big ^= value.raw();
ASSERT(!BigintOperations::FitsIntoSmi(big));
return big.raw();
}
}
static bool Are64bitOperands(const Integer& op1, const Integer& op2) {
return !op1.IsBigint() && !op2.IsBigint();
}
static RawInteger* IntegerBitOperation(Token::Kind kind,
const Integer& op1_int,
const Integer& op2_int) {
if (op1_int.IsSmi() && op2_int.IsSmi()) {
Smi& op1 = Smi::Handle();
Smi& op2 = Smi::Handle();
op1 ^= op1_int.raw();
op2 ^= op2_int.raw();
intptr_t result = 0;
switch (kind) {
case Token::kBIT_AND:
result = op1.Value() & op2.Value();
break;
case Token::kBIT_OR:
result = op1.Value() | op2.Value();
break;
case Token::kBIT_XOR:
result = op1.Value() ^ op2.Value();
break;
default:
UNIMPLEMENTED();
}
ASSERT(Smi::IsValid(result));
return Smi::New(result);
} else if (Are64bitOperands(op1_int, op2_int)) {
int64_t a = op1_int.AsInt64Value();
int64_t b = op2_int.AsInt64Value();
switch (kind) {
case Token::kBIT_AND:
return Integer::New(a & b);
case Token::kBIT_OR:
return Integer::New(a | b);
case Token::kBIT_XOR:
return Integer::New(a ^ b);
default:
UNIMPLEMENTED();
}
} else if (op1_int.IsSmi()) {
return IntegerBitOperation(kind, op2_int, op1_int);
} else if (op2_int.IsSmi()) {
Bigint& op1 = Bigint::Handle(AsBigint(op1_int));
Smi& op2 = Smi::Handle();
op2 ^= op2_int.raw();
switch (kind) {
case Token::kBIT_AND:
return BigintOperations::BitAndWithSmi(op1, op2);
case Token::kBIT_OR:
return BigintOperations::BitOrWithSmi(op1, op2);
case Token::kBIT_XOR:
return BigintOperations::BitXorWithSmi(op1, op2);
default:
UNIMPLEMENTED();
}
} else {
Bigint& op1 = Bigint::Handle(AsBigint(op1_int));
Bigint& op2 = Bigint::Handle(AsBigint(op2_int));
switch (kind) {
case Token::kBIT_AND:
return BigintOperations::BitAnd(op1, op2);
case Token::kBIT_OR:
return BigintOperations::BitOr(op1, op2);
case Token::kBIT_XOR:
return BigintOperations::BitXor(op1, op2);
default:
UNIMPLEMENTED();
}
}
return Integer::null();
}
// Returns false if integer is in wrong representation, e.g., as is a Bigint
// when it could have been a Smi.
static bool CheckInteger(const Integer& i) {
if (i.IsBigint()) {
Bigint& bigint = Bigint::Handle();
bigint ^= i.raw();
return !BigintOperations::FitsIntoSmi(bigint) &&
!BigintOperations::FitsIntoInt64(bigint);
}
if (i.IsMint()) {
Mint& mint = Mint::Handle();
mint ^= i.raw();
return !Smi::IsValid64(mint.value());
}
return true;
}
DEFINE_NATIVE_ENTRY(Integer_bitAndFromInteger, 2) {
const Integer& right = Integer::CheckedHandle(arguments->At(0));
const Integer& left = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(right));
ASSERT(CheckInteger(left));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Integer_bitAndFromInteger %s & %s\n",
right.ToCString(), left.ToCString());
}
Integer& result = Integer::Handle(
IntegerBitOperation(Token::kBIT_AND, left, right));
arguments->SetReturn(Integer::Handle(AsInteger(result)));
}
DEFINE_NATIVE_ENTRY(Integer_bitOrFromInteger, 2) {
const Integer& right = Integer::CheckedHandle(arguments->At(0));
const Integer& left = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(right));
ASSERT(CheckInteger(left));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Integer_bitOrFromInteger %s | %s\n",
left.ToCString(), right.ToCString());
}
Integer& result = Integer::Handle(
IntegerBitOperation(Token::kBIT_OR, left, right));
arguments->SetReturn(Integer::Handle(AsInteger(result)));
}
DEFINE_NATIVE_ENTRY(Integer_bitXorFromInteger, 2) {
const Integer& right = Integer::CheckedHandle(arguments->At(0));
const Integer& left = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(right));
ASSERT(CheckInteger(left));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Integer_bitXorFromInteger %s ^ %s\n",
left.ToCString(), right.ToCString());
}
Integer& result = Integer::Handle(
IntegerBitOperation(Token::kBIT_XOR, left, right));
arguments->SetReturn(Integer::Handle(AsInteger(result)));
}
// The result is invalid if it is outside the range
// Smi::kMaxValue..Smi::kMinValue.
static int64_t BinaryOpWithTwoSmis(Token::Kind operation,
const Smi& left,
const Smi& right) {
switch (operation) {
case Token::kADD:
return left.Value() + right.Value();
case Token::kSUB:
return left.Value() - right.Value();
case Token::kMUL: {
#if defined(TARGET_ARCH_X64)
// Overflow check for 64-bit platforms unimplemented.
UNIMPLEMENTED();
return 0;
#else
int64_t result_64 =
static_cast<int64_t>(left.Value()) *
static_cast<int64_t>(right.Value());
return result_64;
#endif
}
case Token::kTRUNCDIV:
return left.Value() / right.Value();
case Token::kMOD: {
intptr_t remainder = left.Value() % right.Value();
if (remainder < 0) {
if (right.Value() < 0) {
return remainder - right.Value();
} else {
return remainder + right.Value();
}
} else {
return remainder;
}
}
default:
UNIMPLEMENTED();
return 0;
}
}
static RawBigint* BinaryOpWithTwoBigints(Token::Kind operation,
const Bigint& left,
const Bigint& right) {
switch (operation) {
case Token::kADD:
return BigintOperations::Add(left, right);
case Token::kSUB:
return BigintOperations::Subtract(left, right);
case Token::kMUL:
return BigintOperations::Multiply(left, right);
case Token::kTRUNCDIV:
return BigintOperations::Divide(left, right);
case Token::kMOD:
return BigintOperations::Modulo(left, right);
default:
UNIMPLEMENTED();
return Bigint::null();
}
}
// TODO(srdjan): Implement correct overflow checking before allowing 64 bit
// operands.
static bool AreBoth64bitOperands(const Integer& op1, const Integer& op2) {
return false;
}
static RawInteger* IntegerBinopHelper(Token::Kind operation,
const Integer& left_int,
const Integer& right_int) {
if (left_int.IsSmi() && right_int.IsSmi()) {
Smi& left_smi = Smi::Handle();
Smi& right_smi = Smi::Handle();
left_smi ^= left_int.raw();
right_smi ^= right_int.raw();
int64_t result = BinaryOpWithTwoSmis(operation, left_smi, right_smi);
if (Smi::IsValid64(result)) {
return Smi::New(result);
} else {
// Overflow to Mint.
return Mint::New(result);
}
} else if (AreBoth64bitOperands(left_int, right_int)) {
// TODO(srdjan): Test for overflow of result instead of operand
// types.
const int64_t a = left_int.AsInt64Value();
const int64_t b = right_int.AsInt64Value();
switch (operation) {
case Token::kADD:
return Integer::New(a + b);
case Token::kSUB:
return Integer::New(a - b);
case Token::kMUL:
return Integer::New(a * b);
case Token::kTRUNCDIV:
return Integer::New(a / b);
case Token::kMOD: {
int64_t remainder = a % b;
int64_t c = 0;
if (remainder < 0) {
if (b < 0) {
c = remainder - b;
} else {
c = remainder + b;
}
} else {
c = remainder;
}
return Integer::New(c);
}
default:
UNIMPLEMENTED();
}
}
const Bigint& left_big = Bigint::Handle(AsBigint(left_int));
const Bigint& right_big = Bigint::Handle(AsBigint(right_int));
const Bigint& result =
Bigint::Handle(BinaryOpWithTwoBigints(operation, left_big, right_big));
return Integer::Handle(AsInteger(result)).raw();
}
DEFINE_NATIVE_ENTRY(Integer_addFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->At(0));
const Integer& left_int = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(left_int));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Integer_addFromInteger %s + %s\n",
left_int.ToCString(), right_int.ToCString());
}
const Integer& result =
Integer::Handle(IntegerBinopHelper(Token::kADD, left_int, right_int));
arguments->SetReturn(result);
}
DEFINE_NATIVE_ENTRY(Integer_subFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->At(0));
const Integer& left_int = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(left_int));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Integer_subFromInteger %s - %s\n",
left_int.ToCString(), right_int.ToCString());
}
const Integer& result =
Integer::Handle(IntegerBinopHelper(Token::kSUB, left_int, right_int));
arguments->SetReturn(result);
}
DEFINE_NATIVE_ENTRY(Integer_mulFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->At(0));
const Integer& left_int = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(left_int));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Integer_mulFromInteger %s * %s\n",
left_int.ToCString(), right_int.ToCString());
}
const Integer& result =
Integer::Handle(IntegerBinopHelper(Token::kMUL, left_int, right_int));
arguments->SetReturn(result);
}
DEFINE_NATIVE_ENTRY(Integer_truncDivFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->At(0));
const Integer& left_int = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(left_int));
ASSERT(!right_int.IsZero());
const Integer& result = Integer::Handle(
IntegerBinopHelper(Token::kTRUNCDIV, left_int, right_int));
arguments->SetReturn(result);
}
DEFINE_NATIVE_ENTRY(Integer_moduloFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->At(0));
const Integer& left_int = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(right_int));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Integer_moduloFromInteger %s mod %s\n",
left_int.ToCString(), right_int.ToCString());
}
if (right_int.IsZero()) {
// Should have been caught before calling into runtime.
UNIMPLEMENTED();
}
const Integer& result =
Integer::Handle(IntegerBinopHelper(Token::kMOD, left_int, right_int));
arguments->SetReturn(result);
}
DEFINE_NATIVE_ENTRY(Integer_greaterThanFromInteger, 2) {
const Integer& right = Integer::CheckedHandle(arguments->At(0));
const Integer& left = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(right));
ASSERT(CheckInteger(left));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Integer_greaterThanFromInteger %s > %s\n",
left.ToCString(), right.ToCString());
}
const Bool& result = Bool::Handle(Bool::Get(left.CompareWith(right) == 1));
arguments->SetReturn(result);
}
DEFINE_NATIVE_ENTRY(Integer_equalToInteger, 2) {
const Integer& left = Integer::CheckedHandle(arguments->At(0));
const Integer& right = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(left));
ASSERT(CheckInteger(right));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Integer_equalToInteger %s == %s\n",
left.ToCString(), right.ToCString());
}
const Bool& result = Bool::Handle(Bool::Get(left.CompareWith(right) == 0));
arguments->SetReturn(result);
}
static int HighestBit(int64_t v) {
uint64_t t = static_cast<uint64_t>((v > 0) ? v : -v);
int count = 0;
while ((t >>= 1) != 0) {
count++;
}
return count;
}
// TODO(srdjan): Clarify handling of negative right operand in a shift op.
static RawInteger* SmiShiftOperation(Token::Kind kind,
const Smi& left,
const Smi& right) {
ASSERT(right.Value() >= 0);
intptr_t result = 0;
switch (kind) {
case Token::kSHL:
if ((left.Value() == 0) || (right.Value() == 0)) {
return left.raw();
}
{ // Check for overflow.
int cnt = HighestBit(left.Value());
if ((cnt + right.Value()) >= Smi::kBits) {
if ((cnt + right.Value()) >= Mint::kBits) {
return BigintOperations::ShiftLeft(
Bigint::Handle(AsBigint(left)), right.Value());
} else {
int64_t left_64 = left.Value();
return Integer::New(left_64 << right.Value());
}
}
}
result = left.Value() << right.Value();
break;
case Token::kSAR: {
int shift_amount = (right.Value() > 0x1F) ? 0x1F : right.Value();
result = left.Value() >> shift_amount;
break;
}
default:
UNIMPLEMENTED();
}
ASSERT(Smi::IsValid(result));
return Smi::New(result);
}
static RawInteger* ShiftOperationHelper(Token::Kind kind,
const Integer& value,
const Smi& amount) {
if (amount.Value() < 0) {
// TODO(srdjan): Throw exception maybe.
UNIMPLEMENTED();
}
if (value.IsSmi()) {
Smi& smi_value = Smi::Handle();
smi_value ^= value.raw();
return SmiShiftOperation(kind, smi_value, amount);
}
Bigint& big_value = Bigint::Handle();
if (value.IsMint()) {
const int64_t mint_value = value.AsInt64Value();
const int count = HighestBit(mint_value);
if ((count + amount.Value()) < Mint::kBits) {
switch (kind) {
case Token::kSHL:
return Integer::New(mint_value << amount.Value());
case Token::kSAR:
return Integer::New(mint_value >> amount.Value());
default:
UNIMPLEMENTED();
}
} else {
// Overflow in shift, use Bigints
big_value = BigintOperations::NewFromInt64(mint_value);
}
} else {
ASSERT(value.IsBigint());
big_value ^= value.raw();
}
switch (kind) {
case Token::kSHL:
return BigintOperations::ShiftLeft(big_value, amount.Value());
case Token::kSAR:
return BigintOperations::ShiftRight(big_value, amount.Value());
default:
UNIMPLEMENTED();
}
return Integer::null();
}
DEFINE_NATIVE_ENTRY(Smi_sarFromInt, 2) {
const Smi& amount = Smi::CheckedHandle(arguments->At(0));
const Integer& value = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(amount));
ASSERT(CheckInteger(value));
Integer& result = Integer::Handle(
ShiftOperationHelper(Token::kSAR, value, amount));
arguments->SetReturn(Integer::Handle(AsInteger(result)));
}
DEFINE_NATIVE_ENTRY(Smi_shlFromInt, 2) {
const Smi& amount = Smi::CheckedHandle(arguments->At(0));
const Integer& value = Integer::CheckedHandle(arguments->At(1));
ASSERT(CheckInteger(amount));
ASSERT(CheckInteger(value));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Smi_shlFromInt: %s << %s\n",
value.ToCString(), amount.ToCString());
}
Integer& result = Integer::Handle(
ShiftOperationHelper(Token::kSHL, value, amount));
arguments->SetReturn(Integer::Handle(AsInteger(result)));
}
DEFINE_NATIVE_ENTRY(Smi_bitNegate, 1) {
const Smi& operand = Smi::CheckedHandle(arguments->At(0));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Smi_bitNegate: %s\n", operand.ToCString());
}
intptr_t result = ~operand.Value();
ASSERT(Smi::IsValid(result));
arguments->SetReturn(Smi::Handle(Smi::New(result)));
}
// Mint natives.
DEFINE_NATIVE_ENTRY(Mint_bitNegate, 1) {
const Mint& operand = Mint::CheckedHandle(arguments->At(0));
ASSERT(CheckInteger(operand));
if (FLAG_trace_intrinsified_natives) {
OS::Print("Mint_bitNegate: %s\n", operand.ToCString());
}
int64_t result = ~operand.value();
arguments->SetReturn(Integer::Handle(Integer::New(result)));
}
// Bigint natives.
DEFINE_NATIVE_ENTRY(Bigint_bitNegate, 1) {
const Bigint& value = Bigint::CheckedHandle(arguments->At(0));
const Bigint& result = Bigint::Handle(BigintOperations::BitNot(value));
ASSERT(CheckInteger(value));
ASSERT(CheckInteger(result));
arguments->SetReturn(Integer::Handle(AsInteger(result)));
}
} // namespace dart