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:
parent
fcaab50e05
commit
f476d4d431
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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")
|
||||
|
@ -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__)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
240
src/hydrogen.cc
240
src/hydrogen.cc
@ -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);
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
150
test/mjsunit/compiler/uint32.js
Normal file
150
test/mjsunit/compiler/uint32.js
Normal 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));
|
Loading…
Reference in New Issue
Block a user