Allow uint32 value on optimized frames if they are consumed by safe operations.

Safe operations are those that either do not observe unsignedness or have special support for uint32 values:

- all binary bitwise operations: they perform ToInt32 on inputs;
- >> and << shifts: they perform ToInt32 on left hand side and ToUint32 on right hand side;
- >>> shift: it performs ToUint32 on both inputs;
- stores to integer external arrays (not pixel, float or double ones): these stores are "bitwise";
- HChange: special support added for conversions of uint32 values to double and tagged values;
- HSimulate: special support added for deoptimization with uint32 values in registers and stack slots;
- HPhi: phis that have only safe uses and only uint32 operands are uint32 themselves.

BUG=v8:2097
TEST=test/mjsunit/compiler/uint32.js

Review URL: https://chromiumcodereview.appspot.com/10778029

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12367 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
vegorov@chromium.org 2012-08-22 15:44:17 +00:00
parent fcaab50e05
commit f476d4d431
28 changed files with 1080 additions and 117 deletions

View File

@ -713,15 +713,20 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op,
right = UseRegisterAtStart(right_value);
}
// Shift operations can only deoptimize if we do a logical shift
// by 0 and the result cannot be truncated to int32.
bool may_deopt = (op == Token::SHR && constant_value == 0);
bool does_deopt = false;
if (may_deopt) {
for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
does_deopt = true;
break;
if (FLAG_opt_safe_uint32_operations) {
does_deopt = !instr->CheckFlag(HInstruction::kUint32);
} else {
// Shift operations can only deoptimize if we do a logical shift
// by 0 and the result cannot be truncated to int32.
bool may_deopt = (op == Token::SHR && constant_value == 0);
if (may_deopt) {
for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
does_deopt = true;
break;
}
}
}
}
@ -880,7 +885,9 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
} else {
op = UseAny(value);
}
result->AddValue(op, value->representation());
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
if (hydrogen_env->frame_type() == JS_FUNCTION) {
@ -1649,7 +1656,10 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
if (to.IsTagged()) {
HValue* val = instr->value();
LOperand* value = UseRegisterAtStart(val);
if (val->HasRange() && val->range()->IsInSmiRange()) {
if (val->CheckFlag(HInstruction::kUint32)) {
LNumberTagU* result = new(zone()) LNumberTagU(value);
return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
} else if (val->HasRange() && val->range()->IsInSmiRange()) {
return DefineAsRegister(new(zone()) LSmiTag(value));
} else {
LNumberTagI* result = new(zone()) LNumberTagI(value);
@ -1657,8 +1667,13 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
}
} else {
ASSERT(to.IsDouble());
LOperand* value = Use(instr->value());
return DefineAsRegister(new(zone()) LInteger32ToDouble(value));
if (instr->value()->CheckFlag(HInstruction::kUint32)) {
return DefineAsRegister(
new(zone()) LUint32ToDouble(UseRegister(instr->value())));
} else {
return DefineAsRegister(
new(zone()) LInteger32ToDouble(Use(instr->value())));
}
}
}
UNREACHABLE();

View File

@ -108,6 +108,7 @@ class LCodeGen;
V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(Uint32ToDouble) \
V(InvokeFunction) \
V(IsConstructCallAndBranch) \
V(IsNilAndBranch) \
@ -137,6 +138,7 @@ class LCodeGen;
V(MulI) \
V(NumberTagD) \
V(NumberTagI) \
V(NumberTagU) \
V(NumberUntagD) \
V(ObjectLiteral) \
V(OsrEntry) \
@ -1605,6 +1607,16 @@ class LInteger32ToDouble: public LTemplateInstruction<1, 1, 0> {
};
class LUint32ToDouble: public LTemplateInstruction<1, 1, 0> {
public:
explicit LUint32ToDouble(LOperand* value) {
inputs_[0] = value;
}
DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double")
};
class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
public:
explicit LNumberTagI(LOperand* value) {
@ -1615,6 +1627,16 @@ class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
};
class LNumberTagU: public LTemplateInstruction<1, 1, 0> {
public:
explicit LNumberTagU(LOperand* value) {
inputs_[0] = value;
}
DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u")
};
class LNumberTagD: public LTemplateInstruction<1, 1, 2> {
public:
LNumberTagD(LOperand* value, LOperand* temp1, LOperand* temp2) {

View File

@ -512,7 +512,8 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
translation->MarkDuplicate();
AddToTranslation(translation,
environment->spilled_registers()[value->index()],
environment->HasTaggedValueAt(i));
environment->HasTaggedValueAt(i),
environment->HasUint32ValueAt(i));
} else if (
value->IsDoubleRegister() &&
environment->spilled_double_registers()[value->index()] != NULL) {
@ -520,18 +521,23 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
AddToTranslation(
translation,
environment->spilled_double_registers()[value->index()],
false,
false);
}
}
AddToTranslation(translation, value, environment->HasTaggedValueAt(i));
AddToTranslation(translation,
value,
environment->HasTaggedValueAt(i),
environment->HasUint32ValueAt(i));
}
}
void LCodeGen::AddToTranslation(Translation* translation,
LOperand* op,
bool is_tagged) {
bool is_tagged,
bool is_uint32) {
if (op == NULL) {
// TODO(twuerthinger): Introduce marker operands to indicate that this value
// is not present and must be reconstructed from the deoptimizer. Currently
@ -540,6 +546,8 @@ void LCodeGen::AddToTranslation(Translation* translation,
} else if (op->IsStackSlot()) {
if (is_tagged) {
translation->StoreStackSlot(op->index());
} else if (is_uint32) {
translation->StoreUint32StackSlot(op->index());
} else {
translation->StoreInt32StackSlot(op->index());
}
@ -553,6 +561,8 @@ void LCodeGen::AddToTranslation(Translation* translation,
Register reg = ToRegister(op);
if (is_tagged) {
translation->StoreRegister(reg);
} else if (is_uint32) {
translation->StoreUint32Register(reg);
} else {
translation->StoreInt32Register(reg);
}
@ -3022,11 +3032,10 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
break;
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
__ ldr(result, mem_operand);
__ cmp(result, Operand(0x80000000));
// TODO(danno): we could be more clever here, perhaps having a special
// version of the stub that detects if the overflow case actually
// happens, and generate code that returns a double rather than int.
DeoptimizeIf(cs, instr->environment());
if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) {
__ cmp(result, Operand(0x80000000));
DeoptimizeIf(cs, instr->environment());
}
break;
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
@ -4285,12 +4294,24 @@ void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
}
void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) {
LOperand* input = instr->InputAt(0);
LOperand* output = instr->result();
SwVfpRegister flt_scratch = double_scratch0().low();
__ vmov(flt_scratch, ToRegister(input));
__ vcvt_f64_u32(ToDoubleRegister(output), flt_scratch);
}
void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
class DeferredNumberTagI: public LDeferredCode {
public:
DeferredNumberTagI(LCodeGen* codegen, LNumberTagI* instr)
: LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() { codegen()->DoDeferredNumberTagI(instr_); }
virtual void Generate() {
codegen()->DoDeferredNumberTagI(instr_, SIGNED_INT32);
}
virtual LInstruction* instr() { return instr_; }
private:
LNumberTagI* instr_;
@ -4306,7 +4327,33 @@ void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
}
void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
void LCodeGen::DoNumberTagU(LNumberTagU* instr) {
class DeferredNumberTagU: public LDeferredCode {
public:
DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr)
: LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() {
codegen()->DoDeferredNumberTagI(instr_, UNSIGNED_INT32);
}
virtual LInstruction* instr() { return instr_; }
private:
LNumberTagU* instr_;
};
LOperand* input = instr->InputAt(0);
ASSERT(input->IsRegister() && input->Equals(instr->result()));
Register reg = ToRegister(input);
DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr);
__ cmp(reg, Operand(Smi::kMaxValue));
__ b(hi, deferred->entry());
__ SmiTag(reg, reg);
__ bind(deferred->exit());
}
void LCodeGen::DoDeferredNumberTagI(LInstruction* instr,
IntegerSignedness signedness) {
Label slow;
Register src = ToRegister(instr->InputAt(0));
Register dst = ToRegister(instr->result());
@ -4316,16 +4363,22 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
// Preserve the value of all registers.
PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
// There was overflow, so bits 30 and 31 of the original integer
// disagree. Try to allocate a heap number in new space and store
// the value in there. If that fails, call the runtime system.
Label done;
if (dst.is(src)) {
__ SmiUntag(src, dst);
__ eor(src, src, Operand(0x80000000));
if (signedness == SIGNED_INT32) {
// There was overflow, so bits 30 and 31 of the original integer
// disagree. Try to allocate a heap number in new space and store
// the value in there. If that fails, call the runtime system.
if (dst.is(src)) {
__ SmiUntag(src, dst);
__ eor(src, src, Operand(0x80000000));
}
__ vmov(flt_scratch, src);
__ vcvt_f64_s32(dbl_scratch, flt_scratch);
} else {
__ vmov(flt_scratch, src);
__ vcvt_f64_u32(dbl_scratch, flt_scratch);
}
__ vmov(flt_scratch, src);
__ vcvt_f64_s32(dbl_scratch, flt_scratch);
if (FLAG_inline_new) {
__ LoadRoot(r6, Heap::kHeapNumberMapRootIndex);
__ AllocateHeapNumber(r5, r3, r4, r6, &slow);

View File

@ -114,7 +114,10 @@ class LCodeGen BASE_EMBEDDED {
void DoDeferredBinaryOpStub(LTemplateInstruction<1, 2, T>* instr,
Token::Value op);
void DoDeferredNumberTagD(LNumberTagD* instr);
void DoDeferredNumberTagI(LNumberTagI* instr);
enum IntegerSignedness { SIGNED_INT32, UNSIGNED_INT32 };
void DoDeferredNumberTagI(LInstruction* instr, IntegerSignedness signedness);
void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LStackCheck* instr);
@ -252,7 +255,8 @@ class LCodeGen BASE_EMBEDDED {
void AddToTranslation(Translation* translation,
LOperand* op,
bool is_tagged);
bool is_tagged,
bool is_uint32);
void PopulateDeoptimizationData(Handle<Code> code);
int DefineDeoptimizationLiteral(Handle<Object> literal);

View File

@ -2366,7 +2366,7 @@ void Simulator::DecodeType01(Instruction* instr) {
// Format(instr, "cmn'cond 'rn, 'imm");
alu_out = rn_val + shifter_operand;
SetNZFlags(alu_out);
SetCFlag(!CarryFrom(rn_val, shifter_operand));
SetCFlag(CarryFrom(rn_val, shifter_operand));
SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true));
} else {
// Other instructions matching this pattern are handled in the

View File

@ -595,9 +595,11 @@ void Deoptimizer::DoComputeOutputFrames() {
case Translation::BEGIN:
case Translation::REGISTER:
case Translation::INT32_REGISTER:
case Translation::UINT32_REGISTER:
case Translation::DOUBLE_REGISTER:
case Translation::STACK_SLOT:
case Translation::INT32_STACK_SLOT:
case Translation::UINT32_STACK_SLOT:
case Translation::DOUBLE_STACK_SLOT:
case Translation::LITERAL:
case Translation::ARGUMENTS_OBJECT:
@ -769,6 +771,34 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
return;
}
case Translation::UINT32_REGISTER: {
int input_reg = iterator->Next();
uintptr_t value = static_cast<uintptr_t>(input_->GetRegister(input_reg));
bool is_smi = (value <= static_cast<uintptr_t>(Smi::kMaxValue));
if (FLAG_trace_deopt) {
PrintF(
" 0x%08" V8PRIxPTR ": [top + %d] <- %" V8PRIuPTR
" ; uint %s (%s)\n",
output_[frame_index]->GetTop() + output_offset,
output_offset,
value,
converter.NameOfCPURegister(input_reg),
is_smi ? "smi" : "heap number");
}
if (is_smi) {
intptr_t tagged_value =
reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
} else {
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
static_cast<double>(static_cast<uint32_t>(value)));
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
}
return;
}
case Translation::DOUBLE_REGISTER: {
int input_reg = iterator->Next();
double value = input_->GetDoubleRegister(input_reg);
@ -834,6 +864,36 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
return;
}
case Translation::UINT32_STACK_SLOT: {
int input_slot_index = iterator->Next();
unsigned input_offset =
input_->GetOffsetFromSlotIndex(input_slot_index);
uintptr_t value =
static_cast<uintptr_t>(input_->GetFrameSlot(input_offset));
bool is_smi = (value <= static_cast<uintptr_t>(Smi::kMaxValue));
if (FLAG_trace_deopt) {
PrintF(" 0x%08" V8PRIxPTR ": ",
output_[frame_index]->GetTop() + output_offset);
PrintF("[top + %d] <- %" V8PRIuPTR " ; [sp + %d] (uint32 %s)\n",
output_offset,
value,
input_offset,
is_smi ? "smi" : "heap number");
}
if (is_smi) {
intptr_t tagged_value =
reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
} else {
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
static_cast<double>(static_cast<uint32_t>(value)));
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
}
return;
}
case Translation::DOUBLE_STACK_SLOT: {
int input_slot_index = iterator->Next();
unsigned input_offset =
@ -886,6 +946,56 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
}
static bool ObjectToInt32(Object* obj, int32_t* value) {
if (obj->IsSmi()) {
*value = Smi::cast(obj)->value();
return true;
}
if (obj->IsHeapNumber()) {
double num = HeapNumber::cast(obj)->value();
if (FastD2I(FastI2D(num)) != num) {
if (FLAG_trace_osr) {
PrintF("**** %g could not be converted to int32 ****\n",
HeapNumber::cast(obj)->value());
}
return false;
}
*value = FastD2I(num);
return true;
}
return false;
}
static bool ObjectToUint32(Object* obj, uint32_t* value) {
if (obj->IsSmi()) {
if (Smi::cast(obj)->value() < 0) return false;
*value = static_cast<uint32_t>(Smi::cast(obj)->value());
return true;
}
if (obj->IsHeapNumber()) {
double num = HeapNumber::cast(obj)->value();
if ((num < 0) || (FastD2UI(FastUI2D(num)) != num)) {
if (FLAG_trace_osr) {
PrintF("**** %g could not be converted to uint32 ****\n",
HeapNumber::cast(obj)->value());
}
return false;
}
*value = FastD2UI(num);
return true;
}
return false;
}
bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
int* input_offset) {
disasm::NameConverter converter;
@ -926,22 +1036,10 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
}
case Translation::INT32_REGISTER: {
// Abort OSR if we don't have a number.
if (!input_object->IsNumber()) return false;
int32_t int32_value = 0;
if (!ObjectToInt32(input_object, &int32_value)) return false;
int output_reg = iterator->Next();
int int32_value = input_object->IsSmi()
? Smi::cast(input_object)->value()
: FastD2I(input_object->Number());
// Abort the translation if the conversion lost information.
if (!input_object->IsSmi() &&
FastI2D(int32_value) != input_object->Number()) {
if (FLAG_trace_osr) {
PrintF("**** %g could not be converted to int32 ****\n",
input_object->Number());
}
return false;
}
if (FLAG_trace_osr) {
PrintF(" %s <- %d (int32) ; [sp + %d]\n",
converter.NameOfCPURegister(output_reg),
@ -952,6 +1050,21 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
break;
}
case Translation::UINT32_REGISTER: {
uint32_t uint32_value = 0;
if (!ObjectToUint32(input_object, &uint32_value)) return false;
int output_reg = iterator->Next();
if (FLAG_trace_osr) {
PrintF(" %s <- %u (uint32) ; [sp + %d]\n",
converter.NameOfCPURegister(output_reg),
uint32_value,
*input_offset);
}
output->SetRegister(output_reg, static_cast<int32_t>(uint32_value));
}
case Translation::DOUBLE_REGISTER: {
// Abort OSR if we don't have a number.
if (!input_object->IsNumber()) return false;
@ -985,24 +1098,12 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
}
case Translation::INT32_STACK_SLOT: {
// Abort OSR if we don't have a number.
if (!input_object->IsNumber()) return false;
int32_t int32_value = 0;
if (!ObjectToInt32(input_object, &int32_value)) return false;
int output_index = iterator->Next();
unsigned output_offset =
output->GetOffsetFromSlotIndex(output_index);
int int32_value = input_object->IsSmi()
? Smi::cast(input_object)->value()
: DoubleToInt32(input_object->Number());
// Abort the translation if the conversion lost information.
if (!input_object->IsSmi() &&
FastI2D(int32_value) != input_object->Number()) {
if (FLAG_trace_osr) {
PrintF("**** %g could not be converted to int32 ****\n",
input_object->Number());
}
return false;
}
if (FLAG_trace_osr) {
PrintF(" [sp + %d] <- %d (int32) ; [sp + %d]\n",
output_offset,
@ -1013,6 +1114,23 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
break;
}
case Translation::UINT32_STACK_SLOT: {
uint32_t uint32_value = 0;
if (!ObjectToUint32(input_object, &uint32_value)) return false;
int output_index = iterator->Next();
unsigned output_offset =
output->GetOffsetFromSlotIndex(output_index);
if (FLAG_trace_osr) {
PrintF(" [sp + %d] <- %u (uint32) ; [sp + %d]\n",
output_offset,
uint32_value,
*input_offset);
}
output->SetFrameSlot(output_offset, static_cast<int32_t>(uint32_value));
break;
}
case Translation::DOUBLE_STACK_SLOT: {
static const int kLowerOffset = 0 * kPointerSize;
static const int kUpperOffset = 1 * kPointerSize;
@ -1399,6 +1517,12 @@ void Translation::StoreInt32Register(Register reg) {
}
void Translation::StoreUint32Register(Register reg) {
buffer_->Add(UINT32_REGISTER, zone());
buffer_->Add(reg.code(), zone());
}
void Translation::StoreDoubleRegister(DoubleRegister reg) {
buffer_->Add(DOUBLE_REGISTER, zone());
buffer_->Add(DoubleRegister::ToAllocationIndex(reg), zone());
@ -1417,6 +1541,12 @@ void Translation::StoreInt32StackSlot(int index) {
}
void Translation::StoreUint32StackSlot(int index) {
buffer_->Add(UINT32_STACK_SLOT, zone());
buffer_->Add(index, zone());
}
void Translation::StoreDoubleStackSlot(int index) {
buffer_->Add(DOUBLE_STACK_SLOT, zone());
buffer_->Add(index, zone());
@ -1447,9 +1577,11 @@ int Translation::NumberOfOperandsFor(Opcode opcode) {
case SETTER_STUB_FRAME:
case REGISTER:
case INT32_REGISTER:
case UINT32_REGISTER:
case DOUBLE_REGISTER:
case STACK_SLOT:
case INT32_STACK_SLOT:
case UINT32_STACK_SLOT:
case DOUBLE_STACK_SLOT:
case LITERAL:
return 1;
@ -1483,12 +1615,16 @@ const char* Translation::StringFor(Opcode opcode) {
return "REGISTER";
case INT32_REGISTER:
return "INT32_REGISTER";
case UINT32_REGISTER:
return "UINT32_REGISTER";
case DOUBLE_REGISTER:
return "DOUBLE_REGISTER";
case STACK_SLOT:
return "STACK_SLOT";
case INT32_STACK_SLOT:
return "INT32_STACK_SLOT";
case UINT32_STACK_SLOT:
return "UINT32_STACK_SLOT";
case DOUBLE_STACK_SLOT:
return "DOUBLE_STACK_SLOT";
case LITERAL:
@ -1545,6 +1681,7 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
case Translation::REGISTER:
case Translation::INT32_REGISTER:
case Translation::UINT32_REGISTER:
case Translation::DOUBLE_REGISTER:
case Translation::DUPLICATE:
// We are at safepoint which corresponds to call. All registers are
@ -1564,6 +1701,12 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
return SlotRef(slot_addr, SlotRef::INT32);
}
case Translation::UINT32_STACK_SLOT: {
int slot_index = iterator->Next();
Address slot_addr = SlotAddress(frame, slot_index);
return SlotRef(slot_addr, SlotRef::UINT32);
}
case Translation::DOUBLE_STACK_SLOT: {
int slot_index = iterator->Next();
Address slot_addr = SlotAddress(frame, slot_index);

View File

@ -565,9 +565,11 @@ class Translation BASE_EMBEDDED {
ARGUMENTS_ADAPTOR_FRAME,
REGISTER,
INT32_REGISTER,
UINT32_REGISTER,
DOUBLE_REGISTER,
STACK_SLOT,
INT32_STACK_SLOT,
UINT32_STACK_SLOT,
DOUBLE_STACK_SLOT,
LITERAL,
ARGUMENTS_OBJECT,
@ -596,9 +598,11 @@ class Translation BASE_EMBEDDED {
void BeginSetterStubFrame(int literal_id);
void StoreRegister(Register reg);
void StoreInt32Register(Register reg);
void StoreUint32Register(Register reg);
void StoreDoubleRegister(DoubleRegister reg);
void StoreStackSlot(int index);
void StoreInt32StackSlot(int index);
void StoreUint32StackSlot(int index);
void StoreDoubleStackSlot(int index);
void StoreLiteral(int literal_id);
void StoreArgumentsObject();
@ -648,6 +652,7 @@ class SlotRef BASE_EMBEDDED {
UNKNOWN,
TAGGED,
INT32,
UINT32,
DOUBLE,
LITERAL
};
@ -675,6 +680,16 @@ class SlotRef BASE_EMBEDDED {
}
}
case UINT32: {
uint32_t value = Memory::uint32_at(addr_);
if (value <= static_cast<uint32_t>(Smi::kMaxValue)) {
return Handle<Object>(Smi::FromInt(static_cast<int>(value)));
} else {
return Isolate::Current()->factory()->NewNumber(
static_cast<double>(value));
}
}
case DOUBLE: {
double value = Memory::double_at(addr_);
return Isolate::Current()->factory()->NewNumber(value);

View File

@ -218,6 +218,9 @@ DEFINE_int(loop_weight, 1, "loop weight for representation inference")
DEFINE_bool(optimize_for_in, true,
"optimize functions containing for-in loops")
DEFINE_bool(opt_safe_uint32_operations, true,
"allow uint32 values on optimize frames if they are used only in"
"safe operations")
DEFINE_bool(parallel_recompilation, false,
"optimizing hot functions asynchronously on a separate thread")

View File

@ -203,6 +203,7 @@ typedef byte* Address;
#define V8PRIxPTR V8_PTR_PREFIX "x"
#define V8PRIdPTR V8_PTR_PREFIX "d"
#define V8PRIuPTR V8_PTR_PREFIX "u"
// Fix for Mac OS X defining uintptr_t as "unsigned long":
#if defined(__APPLE__) && defined(__MACH__)

View File

@ -875,12 +875,14 @@ HValue* HBitwise::Canonicalize() {
int32_t nop_constant = (op() == Token::BIT_AND) ? -1 : 0;
if (left()->IsConstant() &&
HConstant::cast(left())->HasInteger32Value() &&
HConstant::cast(left())->Integer32Value() == nop_constant) {
HConstant::cast(left())->Integer32Value() == nop_constant &&
!right()->CheckFlag(kUint32)) {
return right();
}
if (right()->IsConstant() &&
HConstant::cast(right())->HasInteger32Value() &&
HConstant::cast(right())->Integer32Value() == nop_constant) {
HConstant::cast(right())->Integer32Value() == nop_constant &&
!left()->CheckFlag(kUint32)) {
return left();
}
return this;
@ -892,7 +894,9 @@ HValue* HBitNot::Canonicalize() {
if (value()->IsBitNot()) {
HValue* result = HBitNot::cast(value())->value();
ASSERT(result->representation().IsInteger32());
return result;
if (!result->CheckFlag(kUint32)) {
return result;
}
}
return this;
}

View File

@ -562,7 +562,14 @@ class HValue: public ZoneObject {
kIsArguments,
kTruncatingToInt32,
kIsDead,
kLastFlag = kIsDead
// Instructions that are allowed to produce full range unsigned integer
// values are marked with kUint32 flag. If arithmetic shift or a load from
// EXTERNAL_UNSIGNED_INT_ELEMENTS array is not marked with this flag
// it will deoptimize if result does not fit into signed integer range.
// HGraph::ComputeSafeUint32Operations is responsible for setting this
// flag.
kUint32,
kLastFlag = kUint32
};
STATIC_ASSERT(kLastFlag < kBitsPerInt);
@ -2556,6 +2563,10 @@ class HConstant: public HTemplateInstruction<0> {
bool ToBoolean();
bool IsUint32() {
return HasInteger32Value() && (Integer32Value() >= 0);
}
virtual intptr_t Hashcode() {
ASSERT_ALLOCATION_DISABLED;
intptr_t hash;

View File

@ -694,6 +694,7 @@ HGraph::HGraph(CompilationInfo* info)
blocks_(8, info->zone()),
values_(16, info->zone()),
phi_list_(NULL),
uint32_instructions_(NULL),
info_(info),
zone_(info->zone()),
is_recursive_(false) {
@ -2723,6 +2724,226 @@ void HGraph::MarkDeoptimizeOnUndefined() {
}
// Discover instructions that can be marked with kUint32 flag allowing
// them to produce full range uint32 values.
class Uint32Analysis BASE_EMBEDDED {
public:
explicit Uint32Analysis(Zone* zone) : zone_(zone), phis_(4, zone) { }
void Analyze(HInstruction* current);
void UnmarkUnsafePhis();
private:
bool IsSafeUint32Use(HValue* val, HValue* use);
bool Uint32UsesAreSafe(HValue* uint32val);
bool CheckPhiOperands(HPhi* phi);
void UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist);
Zone* zone_;
ZoneList<HPhi*> phis_;
};
bool Uint32Analysis::IsSafeUint32Use(HValue* val, HValue* use) {
// Operations that operatate on bits are safe.
if (use->IsBitwise() ||
use->IsShl() ||
use->IsSar() ||
use->IsShr() ||
use->IsBitNot()) {
return true;
} else if (use->IsChange() || use->IsSimulate()) {
// Conversions and deoptimization have special support for unt32.
return true;
} else if (use->IsStoreKeyedSpecializedArrayElement()) {
// Storing a value into an external integer array is a bit level operation.
HStoreKeyedSpecializedArrayElement* store =
HStoreKeyedSpecializedArrayElement::cast(use);
if (store->value() == val) {
// Clamping or a conversion to double should have beed inserted.
ASSERT(store->elements_kind() != EXTERNAL_PIXEL_ELEMENTS);
ASSERT(store->elements_kind() != EXTERNAL_FLOAT_ELEMENTS);
ASSERT(store->elements_kind() != EXTERNAL_DOUBLE_ELEMENTS);
return true;
}
}
return false;
}
// Iterate over all uses and verify that they are uint32 safe: either don't
// distinguish between int32 and uint32 due to their bitwise nature or
// have special support for uint32 values.
// Encountered phis are optimisitically treated as safe uint32 uses,
// marked with kUint32 flag and collected in the phis_ list. A separate
// path will be performed later by UnmarkUnsafePhis to clear kUint32 from
// phis that are not actually uint32-safe (it requries fix point iteration).
bool Uint32Analysis::Uint32UsesAreSafe(HValue* uint32val) {
bool collect_phi_uses = false;
for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) {
HValue* use = it.value();
if (use->IsPhi()) {
if (!use->CheckFlag(HInstruction::kUint32)) {
// There is a phi use of this value from a phis that is not yet
// collected in phis_ array. Separate pass is required.
collect_phi_uses = true;
}
// Optimistically treat phis as uint32 safe.
continue;
}
if (!IsSafeUint32Use(uint32val, use)) {
return false;
}
}
if (collect_phi_uses) {
for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) {
HValue* use = it.value();
// There is a phi use of this value from a phis that is not yet
// collected in phis_ array. Separate pass is required.
if (use->IsPhi() && !use->CheckFlag(HInstruction::kUint32)) {
use->SetFlag(HInstruction::kUint32);
phis_.Add(HPhi::cast(use), zone_);
}
}
}
return true;
}
// Analyze instruction and mark it with kUint32 if all its uses are uint32
// safe.
void Uint32Analysis::Analyze(HInstruction* current) {
if (Uint32UsesAreSafe(current)) current->SetFlag(HInstruction::kUint32);
}
// Check if all operands to the given phi are marked with kUint32 flag.
bool Uint32Analysis::CheckPhiOperands(HPhi* phi) {
if (!phi->CheckFlag(HInstruction::kUint32)) {
// This phi is not uint32 safe. No need to check operands.
return false;
}
for (int j = 0; j < phi->OperandCount(); j++) {
HValue* operand = phi->OperandAt(j);
if (!operand->CheckFlag(HInstruction::kUint32)) {
// Lazyly mark constants that fit into uint32 range with kUint32 flag.
if (operand->IsConstant() &&
HConstant::cast(operand)->IsUint32()) {
operand->SetFlag(HInstruction::kUint32);
continue;
}
// This phi is not safe, some operands are not uint32 values.
return false;
}
}
return true;
}
// Remove kUint32 flag from the phi itself and its operands. If any operand
// was a phi marked with kUint32 place it into a worklist for
// transitive clearing of kUint32 flag.
void Uint32Analysis::UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist) {
phi->ClearFlag(HInstruction::kUint32);
for (int j = 0; j < phi->OperandCount(); j++) {
HValue* operand = phi->OperandAt(j);
if (operand->CheckFlag(HInstruction::kUint32)) {
operand->ClearFlag(HInstruction::kUint32);
if (operand->IsPhi()) {
worklist->Add(HPhi::cast(operand), zone_);
}
}
}
}
void Uint32Analysis::UnmarkUnsafePhis() {
// No phis were collected. Nothing to do.
if (phis_.length() == 0) return;
// Worklist used to transitively clear kUint32 from phis that
// are used as arguments to other phis.
ZoneList<HPhi*> worklist(phis_.length(), zone_);
// Phi can be used as a uint32 value if and only if
// all its operands are uint32 values and all its
// uses are uint32 safe.
// Iterate over collected phis and unmark those that
// are unsafe. When unmarking phi unmark its operands
// and add it to the worklist if it is a phi as well.
// Phis that are still marked as safe are shifted down
// so that all safe phis form a prefix of the phis_ array.
int phi_count = 0;
for (int i = 0; i < phis_.length(); i++) {
HPhi* phi = phis_[i];
if (CheckPhiOperands(phi) && Uint32UsesAreSafe(phi)) {
phis_[phi_count++] = phi;
} else {
UnmarkPhi(phi, &worklist);
}
}
// Now phis array contains only those phis that have safe
// non-phi uses. Start transitively clearing kUint32 flag
// from phi operands of discovered non-safe phies until
// only safe phies are left.
while (!worklist.is_empty()) {
while (!worklist.is_empty()) {
HPhi* phi = worklist.RemoveLast();
UnmarkPhi(phi, &worklist);
}
// Check if any operands to safe phies were unmarked
// turning a safe phi into unsafe. The same value
// can flow into several phis.
int new_phi_count = 0;
for (int i = 0; i < phi_count; i++) {
HPhi* phi = phis_[i];
if (CheckPhiOperands(phi)) {
phis_[new_phi_count++] = phi;
} else {
UnmarkPhi(phi, &worklist);
}
}
phi_count = new_phi_count;
}
}
void HGraph::ComputeSafeUint32Operations() {
if (!FLAG_opt_safe_uint32_operations || uint32_instructions_ == NULL) {
return;
}
Uint32Analysis analysis(zone());
for (int i = 0; i < uint32_instructions_->length(); ++i) {
HInstruction* current = uint32_instructions_->at(i);
if (current->IsLinked()) analysis.Analyze(current);
}
// Some phis might have been optimistically marked with kUint32 flag.
// Remove this flag from those phis that are unsafe and propagate
// this information transitively potentially clearing kUint32 flag
// from some non-phi operations that are used as operands to unsafe phis.
analysis.UnmarkUnsafePhis();
}
void HGraph::ComputeMinusZeroChecks() {
BitVector visited(GetMaximumValueID(), zone());
for (int i = 0; i < blocks_.length(); ++i) {
@ -3131,6 +3352,12 @@ bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
InsertRepresentationChanges();
InitializeInferredTypes();
// Must be performed before canonicalization to ensure that Canonicalize
// will not remove semantically meaningful ToInt32 operations e.g. BIT_OR with
// zero.
ComputeSafeUint32Operations();
Canonicalize();
// Perform common subexpression elimination and loop-invariant code motion.
@ -5851,8 +6078,14 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
external_elements, checked_key, val, elements_kind);
} else {
ASSERT(val == NULL);
return new(zone()) HLoadKeyedSpecializedArrayElement(
external_elements, checked_key, dependency, elements_kind);
HLoadKeyedSpecializedArrayElement* load =
new(zone()) HLoadKeyedSpecializedArrayElement(
external_elements, checked_key, dependency, elements_kind);
if (FLAG_opt_safe_uint32_operations &&
elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) {
graph()->RecordUint32Instruction(load);
}
return load;
}
}
@ -8034,6 +8267,9 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
break;
case Token::SHR:
instr = HShr::NewHShr(zone(), context, left, right);
if (FLAG_opt_safe_uint32_operations && instr->IsShr()) {
graph()->RecordUint32Instruction(instr);
}
break;
case Token::SHL:
instr = HShl::NewHShl(zone(), context, left, right);

View File

@ -258,6 +258,7 @@ class HGraph: public ZoneObject {
void InsertRepresentationChanges();
void MarkDeoptimizeOnUndefined();
void ComputeMinusZeroChecks();
void ComputeSafeUint32Operations();
bool ProcessArgumentsObject();
void EliminateRedundantPhis();
void EliminateUnreachablePhis();
@ -343,6 +344,13 @@ class HGraph: public ZoneObject {
return is_recursive_;
}
void RecordUint32Instruction(HInstruction* instr) {
if (uint32_instructions_ == NULL) {
uint32_instructions_ = new(zone()) ZoneList<HInstruction*>(4, zone());
}
uint32_instructions_->Add(instr, zone());
}
private:
HConstant* GetConstant(SetOncePointer<HConstant>* pointer,
Handle<Object> value);
@ -370,6 +378,7 @@ class HGraph: public ZoneObject {
ZoneList<HBasicBlock*> blocks_;
ZoneList<HValue*> values_;
ZoneList<HPhi*>* phi_list_;
ZoneList<HInstruction*>* uint32_instructions_;
SetOncePointer<HConstant> undefined_constant_;
SetOncePointer<HConstant> constant_1_;
SetOncePointer<HConstant> constant_minus1_;

View File

@ -451,7 +451,8 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
translation->MarkDuplicate();
AddToTranslation(translation,
environment->spilled_registers()[value->index()],
environment->HasTaggedValueAt(i));
environment->HasTaggedValueAt(i),
environment->HasUint32ValueAt(i));
} else if (
value->IsDoubleRegister() &&
environment->spilled_double_registers()[value->index()] != NULL) {
@ -459,18 +460,23 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
AddToTranslation(
translation,
environment->spilled_double_registers()[value->index()],
false,
false);
}
}
AddToTranslation(translation, value, environment->HasTaggedValueAt(i));
AddToTranslation(translation,
value,
environment->HasTaggedValueAt(i),
environment->HasUint32ValueAt(i));
}
}
void LCodeGen::AddToTranslation(Translation* translation,
LOperand* op,
bool is_tagged) {
bool is_tagged,
bool is_uint32) {
if (op == NULL) {
// TODO(twuerthinger): Introduce marker operands to indicate that this value
// is not present and must be reconstructed from the deoptimizer. Currently
@ -479,6 +485,8 @@ void LCodeGen::AddToTranslation(Translation* translation,
} else if (op->IsStackSlot()) {
if (is_tagged) {
translation->StoreStackSlot(op->index());
} else if (is_uint32) {
translation->StoreUint32StackSlot(op->index());
} else {
translation->StoreInt32StackSlot(op->index());
}
@ -492,6 +500,8 @@ void LCodeGen::AddToTranslation(Translation* translation,
Register reg = ToRegister(op);
if (is_tagged) {
translation->StoreRegister(reg);
} else if (is_uint32) {
translation->StoreUint32Register(reg);
} else {
translation->StoreInt32Register(reg);
}
@ -2837,11 +2847,10 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
break;
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
__ mov(result, operand);
__ test(result, Operand(result));
// TODO(danno): we could be more clever here, perhaps having a special
// version of the stub that detects if the overflow case actually
// happens, and generate code that returns a double rather than int.
DeoptimizeIf(negative, instr->environment());
if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) {
__ test(result, Operand(result));
DeoptimizeIf(negative, instr->environment());
}
break;
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
@ -4081,12 +4090,25 @@ void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
}
void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) {
LOperand* input = instr->InputAt(0);
LOperand* output = instr->result();
LOperand* temp = instr->TempAt(0);
__ LoadUint32(ToDoubleRegister(output),
ToRegister(input),
ToDoubleRegister(temp));
}
void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
class DeferredNumberTagI: public LDeferredCode {
public:
DeferredNumberTagI(LCodeGen* codegen, LNumberTagI* instr)
: LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() { codegen()->DoDeferredNumberTagI(instr_); }
virtual void Generate() {
codegen()->DoDeferredNumberTagI(instr_, SIGNED_INT32);
}
virtual LInstruction* instr() { return instr_; }
private:
LNumberTagI* instr_;
@ -4103,7 +4125,33 @@ void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
}
void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
void LCodeGen::DoNumberTagU(LNumberTagU* instr) {
class DeferredNumberTagU: public LDeferredCode {
public:
DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr)
: LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() {
codegen()->DoDeferredNumberTagI(instr_, UNSIGNED_INT32);
}
virtual LInstruction* instr() { return instr_; }
private:
LNumberTagU* instr_;
};
LOperand* input = instr->InputAt(0);
ASSERT(input->IsRegister() && input->Equals(instr->result()));
Register reg = ToRegister(input);
DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr);
__ cmp(reg, Immediate(Smi::kMaxValue));
__ j(above, deferred->entry());
__ SmiTag(reg);
__ bind(deferred->exit());
}
void LCodeGen::DoDeferredNumberTagI(LInstruction* instr,
IntegerSignedness signedness) {
Label slow;
Register reg = ToRegister(instr->InputAt(0));
Register tmp = reg.is(eax) ? ecx : eax;
@ -4111,13 +4159,19 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
// Preserve the value of all registers.
PushSafepointRegistersScope scope(this);
// There was overflow, so bits 30 and 31 of the original integer
// disagree. Try to allocate a heap number in new space and store
// the value in there. If that fails, call the runtime system.
Label done;
__ SmiUntag(reg);
__ xor_(reg, 0x80000000);
__ cvtsi2sd(xmm0, Operand(reg));
if (signedness == SIGNED_INT32) {
// There was overflow, so bits 30 and 31 of the original integer
// disagree. Try to allocate a heap number in new space and store
// the value in there. If that fails, call the runtime system.
__ SmiUntag(reg);
__ xor_(reg, 0x80000000);
__ cvtsi2sd(xmm0, Operand(reg));
} else {
__ LoadUint32(xmm0, reg, xmm1);
}
if (FLAG_inline_new) {
__ AllocateHeapNumber(reg, tmp, no_reg, &slow);
__ jmp(&done, Label::kNear);

View File

@ -105,7 +105,10 @@ class LCodeGen BASE_EMBEDDED {
// Deferred code support.
void DoDeferredNumberTagD(LNumberTagD* instr);
void DoDeferredNumberTagI(LNumberTagI* instr);
enum IntegerSignedness { SIGNED_INT32, UNSIGNED_INT32 };
void DoDeferredNumberTagI(LInstruction* instr, IntegerSignedness signedness);
void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LStackCheck* instr);
@ -233,7 +236,8 @@ class LCodeGen BASE_EMBEDDED {
void AddToTranslation(Translation* translation,
LOperand* op,
bool is_tagged);
bool is_tagged,
bool is_uint32);
void PopulateDeoptimizationData(Handle<Code> code);
int DefineDeoptimizationLiteral(Handle<Object> literal);

View File

@ -738,15 +738,20 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op,
right = UseFixed(right_value, ecx);
}
// Shift operations can only deoptimize if we do a logical shift by 0 and
// the result cannot be truncated to int32.
bool may_deopt = (op == Token::SHR && constant_value == 0);
bool does_deopt = false;
if (may_deopt) {
for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
does_deopt = true;
break;
if (FLAG_opt_safe_uint32_operations) {
does_deopt = !instr->CheckFlag(HInstruction::kUint32);
} else {
// Shift operations can only deoptimize if we do a logical shift by 0 and
// the result cannot be truncated to int32.
bool may_deopt = (op == Token::SHR && constant_value == 0 &&
!instr->CheckFlag(HInstruction::kUint32));
if (may_deopt) {
for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
does_deopt = true;
break;
}
}
}
}
@ -906,7 +911,9 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
} else {
op = UseAny(value);
}
result->AddValue(op, value->representation());
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
if (hydrogen_env->frame_type() == JS_FUNCTION) {
@ -1698,14 +1705,24 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
LOperand* value = UseRegister(val);
if (val->HasRange() && val->range()->IsInSmiRange()) {
return DefineSameAsFirst(new(zone()) LSmiTag(value));
} else if (val->CheckFlag(HInstruction::kUint32)) {
LOperand* temp = FixedTemp(xmm1);
LNumberTagU* result = new(zone()) LNumberTagU(value, temp);
return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
} else {
LNumberTagI* result = new(zone()) LNumberTagI(value);
return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
}
} else {
ASSERT(to.IsDouble());
return DefineAsRegister(
new(zone()) LInteger32ToDouble(Use(instr->value())));
if (instr->value()->CheckFlag(HInstruction::kUint32)) {
LOperand* temp = FixedTemp(xmm1);
return DefineAsRegister(
new(zone()) LUint32ToDouble(UseRegister(instr->value()), temp));
} else {
return DefineAsRegister(
new(zone()) LInteger32ToDouble(Use(instr->value())));
}
}
}
UNREACHABLE();

View File

@ -102,6 +102,7 @@ class LCodeGen;
V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(Uint32ToDouble) \
V(InvokeFunction) \
V(IsConstructCallAndBranch) \
V(IsNilAndBranch) \
@ -132,6 +133,7 @@ class LCodeGen;
V(MulI) \
V(NumberTagD) \
V(NumberTagI) \
V(NumberTagU) \
V(NumberUntagD) \
V(ObjectLiteral) \
V(OsrEntry) \
@ -1671,6 +1673,17 @@ class LInteger32ToDouble: public LTemplateInstruction<1, 1, 0> {
};
class LUint32ToDouble: public LTemplateInstruction<1, 1, 1> {
public:
explicit LUint32ToDouble(LOperand* value, LOperand* temp) {
inputs_[0] = value;
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double")
};
class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
public:
explicit LNumberTagI(LOperand* value) {
@ -1681,6 +1694,17 @@ class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
};
class LNumberTagU: public LTemplateInstruction<1, 1, 1> {
public:
explicit LNumberTagU(LOperand* value, LOperand* temp) {
inputs_[0] = value;
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u")
};
class LNumberTagD: public LTemplateInstruction<1, 1, 1> {
public:
LNumberTagD(LOperand* value, LOperand* temp) {

View File

@ -152,6 +152,24 @@ void MacroAssembler::ClampUint8(Register reg) {
}
static double kUint32Bias =
static_cast<double>(static_cast<uint32_t>(0xFFFFFFFF)) + 1;
void MacroAssembler::LoadUint32(XMMRegister dst,
Register src,
XMMRegister scratch) {
Label done;
cmp(src, Immediate(0));
movdbl(scratch,
Operand(reinterpret_cast<int32_t>(&kUint32Bias), RelocInfo::NONE));
cvtsi2sd(dst, src);
j(not_sign, &done, Label::kNear);
addsd(dst, scratch);
bind(&done);
}
void MacroAssembler::RecordWriteArray(Register object,
Register value,
Register index,

View File

@ -467,6 +467,8 @@ class MacroAssembler: public Assembler {
j(not_carry, is_smi);
}
void LoadUint32(XMMRegister dst, Register src, XMMRegister scratch);
// Jump the register contains a smi.
inline void JumpIfSmi(Register value,
Label* smi_label,

View File

@ -471,6 +471,7 @@ class LEnvironment: public ZoneObject {
pc_offset_(-1),
values_(value_count, zone),
is_tagged_(value_count, zone),
is_uint32_(value_count, zone),
spilled_registers_(NULL),
spilled_double_registers_(NULL),
outer_(outer),
@ -491,17 +492,28 @@ class LEnvironment: public ZoneObject {
const ZoneList<LOperand*>* values() const { return &values_; }
LEnvironment* outer() const { return outer_; }
void AddValue(LOperand* operand, Representation representation) {
void AddValue(LOperand* operand,
Representation representation,
bool is_uint32) {
values_.Add(operand, zone());
if (representation.IsTagged()) {
ASSERT(!is_uint32);
is_tagged_.Add(values_.length() - 1);
}
if (is_uint32) {
is_uint32_.Add(values_.length() - 1);
}
}
bool HasTaggedValueAt(int index) const {
return is_tagged_.Contains(index);
}
bool HasUint32ValueAt(int index) const {
return is_uint32_.Contains(index);
}
void Register(int deoptimization_index,
int translation_index,
int pc_offset) {
@ -535,6 +547,7 @@ class LEnvironment: public ZoneObject {
int pc_offset_;
ZoneList<LOperand*> values_;
BitVector is_tagged_;
BitVector is_uint32_;
// Allocation index indexed arrays of spill slot operands for registers
// that are also in spill slots at an OSR entry. NULL for environments

View File

@ -8340,6 +8340,14 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
break;
}
case Translation::UINT32_REGISTER: {
int reg_code = iterator.Next();
PrintF(out,
"{input=%s (unsigned)}",
converter.NameOfCPURegister(reg_code));
break;
}
case Translation::DOUBLE_REGISTER: {
int reg_code = iterator.Next();
PrintF(out, "{input=%s}",
@ -8359,6 +8367,12 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
break;
}
case Translation::UINT32_STACK_SLOT: {
int input_slot_index = iterator.Next();
PrintF(out, "{input=%d (unsigned)}", input_slot_index);
break;
}
case Translation::DOUBLE_STACK_SLOT: {
int input_slot_index = iterator.Next();
PrintF(out, "{input=%d}", input_slot_index);

View File

@ -399,7 +399,8 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
translation->MarkDuplicate();
AddToTranslation(translation,
environment->spilled_registers()[value->index()],
environment->HasTaggedValueAt(i));
environment->HasTaggedValueAt(i),
environment->HasUint32ValueAt(i));
} else if (
value->IsDoubleRegister() &&
environment->spilled_double_registers()[value->index()] != NULL) {
@ -407,18 +408,23 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
AddToTranslation(
translation,
environment->spilled_double_registers()[value->index()],
false,
false);
}
}
AddToTranslation(translation, value, environment->HasTaggedValueAt(i));
AddToTranslation(translation,
value,
environment->HasTaggedValueAt(i),
environment->HasUint32ValueAt(i));
}
}
void LCodeGen::AddToTranslation(Translation* translation,
LOperand* op,
bool is_tagged) {
bool is_tagged,
bool is_uint32) {
if (op == NULL) {
// TODO(twuerthinger): Introduce marker operands to indicate that this value
// is not present and must be reconstructed from the deoptimizer. Currently
@ -427,6 +433,8 @@ void LCodeGen::AddToTranslation(Translation* translation,
} else if (op->IsStackSlot()) {
if (is_tagged) {
translation->StoreStackSlot(op->index());
} else if (is_uint32) {
translation->StoreUint32StackSlot(op->index());
} else {
translation->StoreInt32StackSlot(op->index());
}
@ -440,6 +448,8 @@ void LCodeGen::AddToTranslation(Translation* translation,
Register reg = ToRegister(op);
if (is_tagged) {
translation->StoreRegister(reg);
} else if (is_uint32) {
translation->StoreUint32Register(reg);
} else {
translation->StoreInt32Register(reg);
}
@ -2724,11 +2734,10 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
break;
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
__ movl(result, operand);
__ testl(result, result);
// TODO(danno): we could be more clever here, perhaps having a special
// version of the stub that detects if the overflow case actually
// happens, and generate code that returns a double rather than int.
DeoptimizeIf(negative, instr->environment());
if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) {
__ testl(result, result);
DeoptimizeIf(negative, instr->environment());
}
break;
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
@ -4012,6 +4021,17 @@ void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
}
void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) {
LOperand* input = instr->InputAt(0);
LOperand* output = instr->result();
LOperand* temp = instr->TempAt(0);
__ LoadUint32(ToDoubleRegister(output),
ToRegister(input),
ToDoubleRegister(temp));
}
void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
LOperand* input = instr->InputAt(0);
ASSERT(input->IsRegister() && input->Equals(instr->result()));
@ -4021,6 +4041,66 @@ void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
}
void LCodeGen::DoNumberTagU(LNumberTagU* instr) {
class DeferredNumberTagU: public LDeferredCode {
public:
DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr)
: LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() {
codegen()->DoDeferredNumberTagU(instr_);
}
virtual LInstruction* instr() { return instr_; }
private:
LNumberTagU* instr_;
};
LOperand* input = instr->InputAt(0);
ASSERT(input->IsRegister() && input->Equals(instr->result()));
Register reg = ToRegister(input);
DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr);
__ cmpl(reg, Immediate(Smi::kMaxValue));
__ j(above, deferred->entry());
__ Integer32ToSmi(reg, reg);
__ bind(deferred->exit());
}
void LCodeGen::DoDeferredNumberTagU(LNumberTagU* instr) {
Label slow;
Register reg = ToRegister(instr->InputAt(0));
Register tmp = reg.is(rax) ? rcx : rax;
// Preserve the value of all registers.
PushSafepointRegistersScope scope(this);
Label done;
__ LoadUint32(xmm0, reg, xmm1);
if (FLAG_inline_new) {
__ AllocateHeapNumber(reg, tmp, &slow);
__ jmp(&done, Label::kNear);
}
// Slow case: Call the runtime system to do the number allocation.
__ bind(&slow);
// Put a valid pointer value in the stack slot where the result
// register is stored, as this register is in the pointer map, but contains an
// integer value.
__ StoreToSafepointRegisterSlot(reg, Immediate(0));
CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
if (!reg.is(rax)) __ movq(reg, rax);
// Done. Put the value in xmm0 into the value of the allocated heap
// number.
__ bind(&done);
__ movsd(FieldOperand(reg, HeapNumber::kValueOffset), xmm0);
__ StoreToSafepointRegisterSlot(reg, reg);
}
void LCodeGen::DoNumberTagD(LNumberTagD* instr) {
class DeferredNumberTagD: public LDeferredCode {
public:

View File

@ -98,6 +98,7 @@ class LCodeGen BASE_EMBEDDED {
// Deferred code support.
void DoDeferredNumberTagD(LNumberTagD* instr);
void DoDeferredNumberTagU(LNumberTagU* instr);
void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LStackCheck* instr);
@ -223,7 +224,8 @@ class LCodeGen BASE_EMBEDDED {
void AddToTranslation(Translation* translation,
LOperand* op,
bool is_tagged);
bool is_tagged,
bool is_uint32);
void PopulateDeoptimizationData(Handle<Code> code);
int DefineDeoptimizationLiteral(Handle<Object> literal);

View File

@ -720,13 +720,17 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op,
// Shift operations can only deoptimize if we do a logical shift by 0 and
// the result cannot be truncated to int32.
bool may_deopt = (op == Token::SHR && constant_value == 0);
bool does_deopt = false;
if (may_deopt) {
for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
does_deopt = true;
break;
if (FLAG_opt_safe_uint32_operations) {
does_deopt = !instr->CheckFlag(HInstruction::kUint32);
} else {
bool may_deopt = (op == Token::SHR && constant_value == 0);
if (may_deopt) {
for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
does_deopt = true;
break;
}
}
}
}
@ -885,7 +889,9 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
} else {
op = UseAny(value);
}
result->AddValue(op, value->representation());
result->AddValue(op,
value->representation(),
value->CheckFlag(HInstruction::kUint32));
}
if (hydrogen_env->frame_type() == JS_FUNCTION) {
@ -1623,16 +1629,26 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
if (to.IsTagged()) {
HValue* val = instr->value();
LOperand* value = UseRegister(val);
if (val->HasRange() && val->range()->IsInSmiRange()) {
if (val->CheckFlag(HInstruction::kUint32)) {
LOperand* temp = FixedTemp(xmm1);
LNumberTagU* result = new(zone()) LNumberTagU(value, temp);
return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
} else if (val->HasRange() && val->range()->IsInSmiRange()) {
return DefineSameAsFirst(new(zone()) LSmiTag(value));
} else {
LNumberTagI* result = new(zone()) LNumberTagI(value);
return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
}
} else {
ASSERT(to.IsDouble());
LOperand* value = Use(instr->value());
return DefineAsRegister(new(zone()) LInteger32ToDouble(value));
if (instr->value()->CheckFlag(HInstruction::kUint32)) {
LOperand* temp = FixedTemp(xmm1);
return DefineAsRegister(
new(zone()) LUint32ToDouble(UseRegister(instr->value()), temp));
} else {
ASSERT(to.IsDouble());
LOperand* value = Use(instr->value());
return DefineAsRegister(new(zone()) LInteger32ToDouble(value));
}
}
}
UNREACHABLE();

View File

@ -108,6 +108,7 @@ class LCodeGen;
V(InstanceOfKnownGlobal) \
V(InstructionGap) \
V(Integer32ToDouble) \
V(Uint32ToDouble) \
V(InvokeFunction) \
V(IsConstructCallAndBranch) \
V(IsNilAndBranch) \
@ -137,6 +138,7 @@ class LCodeGen;
V(MulI) \
V(NumberTagD) \
V(NumberTagI) \
V(NumberTagU) \
V(NumberUntagD) \
V(ObjectLiteral) \
V(OsrEntry) \
@ -1581,6 +1583,17 @@ class LInteger32ToDouble: public LTemplateInstruction<1, 1, 0> {
};
class LUint32ToDouble: public LTemplateInstruction<1, 1, 1> {
public:
explicit LUint32ToDouble(LOperand* value, LOperand* temp) {
inputs_[0] = value;
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double")
};
class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
public:
explicit LNumberTagI(LOperand* value) {
@ -1591,6 +1604,17 @@ class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
};
class LNumberTagU: public LTemplateInstruction<1, 1, 1> {
public:
explicit LNumberTagU(LOperand* value, LOperand* temp) {
inputs_[0] = value;
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u")
};
class LNumberTagD: public LTemplateInstruction<1, 1, 1> {
public:
explicit LNumberTagD(LOperand* value, LOperand* temp) {

View File

@ -2500,6 +2500,12 @@ MacroAssembler::kSafepointPushRegisterIndices[Register::kNumRegisters] = {
};
void MacroAssembler::StoreToSafepointRegisterSlot(Register dst,
const Immediate& imm) {
movq(SafepointRegisterSlot(dst), imm);
}
void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) {
movq(SafepointRegisterSlot(dst), src);
}
@ -2854,6 +2860,26 @@ void MacroAssembler::ClampDoubleToUint8(XMMRegister input_reg,
}
static double kUint32Bias =
static_cast<double>(static_cast<uint32_t>(0xFFFFFFFF)) + 1;
void MacroAssembler::LoadUint32(XMMRegister dst,
Register src,
XMMRegister scratch) {
Label done;
cmpl(src, Immediate(0));
movq(kScratchRegister,
reinterpret_cast<int64_t>(&kUint32Bias),
RelocInfo::NONE);
movsd(scratch, Operand(kScratchRegister, 0));
cvtlsi2sd(dst, src);
j(not_sign, &done, Label::kNear);
addsd(dst, scratch);
bind(&done);
}
void MacroAssembler::LoadInstanceDescriptors(Register map,
Register descriptors) {
Register temp = descriptors;

View File

@ -317,6 +317,7 @@ class MacroAssembler: public Assembler {
void PopSafepointRegisters() { Popad(); }
// Store the value in register src in the safepoint register stack
// slot for register dst.
void StoreToSafepointRegisterSlot(Register dst, const Immediate& imm);
void StoreToSafepointRegisterSlot(Register dst, Register src);
void LoadFromSafepointRegisterSlot(Register dst, Register src);
@ -944,6 +945,8 @@ class MacroAssembler: public Assembler {
Register result_reg,
Register temp_reg);
void LoadUint32(XMMRegister dst, Register src, XMMRegister scratch);
void LoadInstanceDescriptors(Register map, Register descriptors);
// Abort execution if argument is not a number. Used in debug code.

View File

@ -0,0 +1,150 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax
// Test uint32 handing in optimized frames.
var K1 = 0x7fffffff;
var K2 = 0xffffffff;
var uint32_array = new Uint32Array(2);
uint32_array[0] = K1;
uint32_array[1] = K2;
function ChangeI2T(arr, i) {
return uint32_array[i];
}
assertEquals(K1, ChangeI2T(uint32_array, 0));
assertEquals(K2, ChangeI2T(uint32_array, 1));
%OptimizeFunctionOnNextCall(ChangeI2T);
assertEquals(K1, ChangeI2T(uint32_array, 0));
assertEquals(K2, ChangeI2T(uint32_array, 1));
function SideEffect() {
with ({}) { } // not inlinable
}
function Deopt(obj, arr, i) {
var x = arr[i];
SideEffect(); // x will be used by HSimulate.
obj.x;
return x;
}
assertEquals(K1, Deopt({x: 0}, uint32_array, 0));
assertEquals(K2, Deopt({x: 0}, uint32_array, 1));
%OptimizeFunctionOnNextCall(Deopt);
assertEquals(K2, Deopt({}, uint32_array, 1));
function ChangeI2D(arr) {
// This addition will have a double type feedback so ChangeI2D will
// be generated for its operands.
return arr[0] + arr[1];
}
assertEquals(K1 + K2, ChangeI2D(uint32_array));
assertEquals(K1 + K2, ChangeI2D(uint32_array));
%OptimizeFunctionOnNextCall(ChangeI2D);
assertEquals(K1 + K2, ChangeI2D(uint32_array));
function ShrShr(val) {
return (val >>> 0) >>> 1;
}
assertEquals(K1, ShrShr(K2 | 0));
assertEquals(K1, ShrShr(K2 | 0));
%OptimizeFunctionOnNextCall(ShrShr);
assertEquals(K1, ShrShr(K2 | 0));
function SarShr(val) {
return val >> (-2 >>> 0);
}
var K3 = 0x80000000;
assertEquals(-2, SarShr(K3 | 0));
assertEquals(-2, SarShr(K3 | 0));
%OptimizeFunctionOnNextCall(SarShr);
assertEquals(-2, SarShr(K3 | 0));
function Uint32Phi(a, b, c) {
var i = a ? (b >>> 0) : (c >>> 0);
return (i | 0);
}
var K4 = 0x80000001;
assertEquals(K3 | 0, Uint32Phi(true, K3, K4));
assertEquals(K4 | 0, Uint32Phi(false, K3, K4));
assertEquals(K3 | 0, Uint32Phi(true, K3, K4));
assertEquals(K4 | 0, Uint32Phi(false, K3, K4));
%OptimizeFunctionOnNextCall(Uint32Phi);
assertEquals(K3 | 0, Uint32Phi(true, K3, K4));
assertEquals(K4 | 0, Uint32Phi(false, K3, K4));
function NonUint32Phi(a, b, c) {
var i = a ? (b >>> 0) : c;
return (i | 0);
}
assertEquals(K3 | 0, NonUint32Phi(true, K3, K4));
assertEquals(K4 | 0, NonUint32Phi(false, K3, K4));
assertEquals(K3 | 0, NonUint32Phi(true, K3, K4));
assertEquals(K4 | 0, NonUint32Phi(false, K3, K4));
%OptimizeFunctionOnNextCall(NonUint32Phi);
assertEquals(K3 | 0, NonUint32Phi(true, K3, K4));
assertEquals(K4 | 0, NonUint32Phi(false, K3, K4));
function PhiOfPhi(x) {
var a = (x >>> 0);
for (var i = 0; i < 2; i++) {
for (var j = 0; j < 2; j++) {
a = (a >>> 0);
}
}
return (a | 0);
}
assertEquals(1, PhiOfPhi(1));
assertEquals(1, PhiOfPhi(1));
%OptimizeFunctionOnNextCall(PhiOfPhi);
assertEquals(K3 | 0, PhiOfPhi(K3));
function PhiOfPhiUnsafe(x) {
var a = x >>> 0;
for (var i = 0; i < 2; i++) {
for (var j = 0; j < 2; j++) {
a = (a >>> 0);
}
}
return a + a;
}
assertEquals(2, PhiOfPhiUnsafe(1));
assertEquals(2, PhiOfPhiUnsafe(1));
%OptimizeFunctionOnNextCall(PhiOfPhiUnsafe);
assertEquals(2 * K3, PhiOfPhiUnsafe(K3));