[vm/compiler] Support UnboxedDouble representation in unoptimized code
In the unoptimized code unboxed double inputs are now unboxed in the instruction prologue, and unboxed double output is boxed in the instruction epilogue. Double values are still always boxed on the expression stack in the unoptimized mode. Support for unboxed doubles in unoptimized code allows us to have one implementation of double and dart:math built-in functions, shared across optimized and unoptimized modes. TEST=ci Issue: https://github.com/dart-lang/sdk/issues/46650 Change-Id: Ic1e42b72ee80cb2c12019b7abab8111240b05efd Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211302 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
9dceb362ef
commit
25d0ae2304
@@ -1040,6 +1040,12 @@ class Assembler : public AssemblerBase {
|
||||
Register base,
|
||||
int32_t offset,
|
||||
Condition cond = AL);
|
||||
void LoadUnboxedDouble(FpuRegister dst, Register base, int32_t offset) {
|
||||
LoadDFromOffset(EvenDRegisterOf(dst), base, offset);
|
||||
}
|
||||
void StoreUnboxedDouble(FpuRegister src, Register base, int32_t offset) {
|
||||
StoreDToOffset(EvenDRegisterOf(src), base, offset);
|
||||
}
|
||||
|
||||
void LoadMultipleDFromOffset(DRegister first,
|
||||
intptr_t count,
|
||||
|
||||
@@ -1808,6 +1808,13 @@ class Assembler : public AssemblerBase {
|
||||
StoreQToOffset(src, base, offset - kHeapObjectTag);
|
||||
}
|
||||
|
||||
void LoadUnboxedDouble(FpuRegister dst, Register base, int32_t offset) {
|
||||
LoadDFromOffset(dst, base, offset);
|
||||
}
|
||||
void StoreUnboxedDouble(FpuRegister src, Register base, int32_t offset) {
|
||||
StoreDToOffset(src, base, offset);
|
||||
}
|
||||
|
||||
void LoadCompressed(Register dest, const Address& slot);
|
||||
void LoadCompressedFromOffset(Register dest, Register base, int32_t offset);
|
||||
void LoadCompressedSmi(Register dest, const Address& slot);
|
||||
|
||||
@@ -654,6 +654,12 @@ class Assembler : public AssemblerBase {
|
||||
void StoreMemoryValue(Register src, Register base, int32_t offset) {
|
||||
movl(Address(base, offset), src);
|
||||
}
|
||||
void LoadUnboxedDouble(FpuRegister dst, Register base, int32_t offset) {
|
||||
movsd(dst, Address(base, offset));
|
||||
}
|
||||
void StoreUnboxedDouble(FpuRegister src, Register base, int32_t offset) {
|
||||
movsd(Address(base, offset), src);
|
||||
}
|
||||
void LoadAcquire(Register dst, Register address, int32_t offset = 0) {
|
||||
// On intel loads have load-acquire behavior (i.e. loads are not re-ordered
|
||||
// with other loads).
|
||||
|
||||
@@ -1027,6 +1027,12 @@ class Assembler : public AssemblerBase {
|
||||
void StoreMemoryValue(Register src, Register base, int32_t offset) {
|
||||
movq(Address(base, offset), src);
|
||||
}
|
||||
void LoadUnboxedDouble(FpuRegister dst, Register base, int32_t offset) {
|
||||
movsd(dst, Address(base, offset));
|
||||
}
|
||||
void StoreUnboxedDouble(FpuRegister src, Register base, int32_t offset) {
|
||||
movsd(Address(base, offset), src);
|
||||
}
|
||||
|
||||
#if defined(USING_THREAD_SANITIZER)
|
||||
void TsanLoadAcquire(Address addr);
|
||||
|
||||
@@ -564,7 +564,7 @@ void FlowGraphCompiler::EmitSourceLine(Instruction* instr) {
|
||||
|
||||
static bool IsPusher(Instruction* instr) {
|
||||
if (auto def = instr->AsDefinition()) {
|
||||
return def->HasTemp();
|
||||
return def->HasTemp() && (instr->representation() == kTagged);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1566,6 +1566,18 @@ static Register AllocateFreeRegister(bool* blocked_registers) {
|
||||
return kNoRegister;
|
||||
}
|
||||
|
||||
// Allocate a FPU register that is not explictly blocked.
|
||||
static FpuRegister AllocateFreeFpuRegister(bool* blocked_registers) {
|
||||
for (intptr_t regno = 0; regno < kNumberOfFpuRegisters; regno++) {
|
||||
if (!blocked_registers[regno]) {
|
||||
blocked_registers[regno] = true;
|
||||
return static_cast<FpuRegister>(regno);
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
return kNoFpuRegister;
|
||||
}
|
||||
|
||||
void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
ASSERT(!is_optimizing());
|
||||
instr->InitializeLocationSummary(zone(), false); // Not optimizing.
|
||||
@@ -1573,13 +1585,15 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
LocationSummary* locs = instr->locs();
|
||||
|
||||
bool blocked_registers[kNumberOfCpuRegisters];
|
||||
bool blocked_fpu_registers[kNumberOfFpuRegisters];
|
||||
|
||||
// Connect input with peephole output for some special cases. All other
|
||||
// cases are handled by simply allocating registers and generating code.
|
||||
if (top_of_stack_ != nullptr) {
|
||||
const intptr_t p = locs->input_count() - 1;
|
||||
Location peephole = top_of_stack_->locs()->out(0);
|
||||
if (locs->in(p).IsUnallocated() || locs->in(p).IsConstant()) {
|
||||
if ((instr->RequiredInputRepresentation(p) == kTagged) &&
|
||||
(locs->in(p).IsUnallocated() || locs->in(p).IsConstant())) {
|
||||
// If input is unallocated, match with an output register, if set. Also,
|
||||
// if input is a direct constant, but the peephole output is a register,
|
||||
// use that register to avoid wasting the already generated code.
|
||||
@@ -1594,6 +1608,9 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
|
||||
blocked_registers[i] = (kDartAvailableCpuRegs & (1 << i)) == 0;
|
||||
}
|
||||
for (intptr_t i = 0; i < kNumberOfFpuRegisters; i++) {
|
||||
blocked_fpu_registers[i] = false;
|
||||
}
|
||||
|
||||
// Mark all fixed input, temp and output registers as used.
|
||||
for (intptr_t i = 0; i < locs->input_count(); i++) {
|
||||
@@ -1602,6 +1619,10 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
// Check that a register is not specified twice in the summary.
|
||||
ASSERT(!blocked_registers[loc.reg()]);
|
||||
blocked_registers[loc.reg()] = true;
|
||||
} else if (loc.IsFpuRegister()) {
|
||||
// Check that a register is not specified twice in the summary.
|
||||
ASSERT(!blocked_fpu_registers[loc.fpu_reg()]);
|
||||
blocked_fpu_registers[loc.fpu_reg()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1611,6 +1632,10 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
// Check that a register is not specified twice in the summary.
|
||||
ASSERT(!blocked_registers[loc.reg()]);
|
||||
blocked_registers[loc.reg()] = true;
|
||||
} else if (loc.IsFpuRegister()) {
|
||||
// Check that a register is not specified twice in the summary.
|
||||
ASSERT(!blocked_fpu_registers[loc.fpu_reg()]);
|
||||
blocked_fpu_registers[loc.fpu_reg()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1618,23 +1643,50 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
// Fixed output registers are allowed to overlap with
|
||||
// temps and inputs.
|
||||
blocked_registers[locs->out(0).reg()] = true;
|
||||
} else if (locs->out(0).IsFpuRegister()) {
|
||||
// Fixed output registers are allowed to overlap with
|
||||
// temps and inputs.
|
||||
blocked_fpu_registers[locs->out(0).fpu_reg()] = true;
|
||||
}
|
||||
|
||||
// Allocate all unallocated input locations.
|
||||
const bool should_pop = !instr->IsPushArgument();
|
||||
Register fpu_unboxing_temp = kNoRegister;
|
||||
for (intptr_t i = locs->input_count() - 1; i >= 0; i--) {
|
||||
Location loc = locs->in(i);
|
||||
Register reg = kNoRegister;
|
||||
FpuRegister fpu_reg = kNoFpuRegister;
|
||||
if (loc.IsRegister()) {
|
||||
reg = loc.reg();
|
||||
} else if (loc.IsFpuRegister()) {
|
||||
fpu_reg = loc.fpu_reg();
|
||||
} else if (loc.IsUnallocated()) {
|
||||
ASSERT((loc.policy() == Location::kRequiresRegister) ||
|
||||
(loc.policy() == Location::kWritableRegister) ||
|
||||
(loc.policy() == Location::kPrefersRegister) ||
|
||||
(loc.policy() == Location::kAny));
|
||||
reg = AllocateFreeRegister(blocked_registers);
|
||||
locs->set_in(i, Location::RegisterLocation(reg));
|
||||
switch (loc.policy()) {
|
||||
case Location::kRequiresRegister:
|
||||
case Location::kWritableRegister:
|
||||
case Location::kPrefersRegister:
|
||||
case Location::kAny:
|
||||
reg = AllocateFreeRegister(blocked_registers);
|
||||
locs->set_in(i, Location::RegisterLocation(reg));
|
||||
break;
|
||||
case Location::kRequiresFpuRegister:
|
||||
fpu_reg = AllocateFreeFpuRegister(blocked_fpu_registers);
|
||||
locs->set_in(i, Location::FpuRegisterLocation(fpu_reg));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
if (fpu_reg != kNoFpuRegister) {
|
||||
ASSERT(reg == kNoRegister);
|
||||
// Allocate temporary CPU register for unboxing, but only once.
|
||||
if (fpu_unboxing_temp == kNoRegister) {
|
||||
fpu_unboxing_temp = AllocateFreeRegister(blocked_registers);
|
||||
}
|
||||
reg = fpu_unboxing_temp;
|
||||
}
|
||||
|
||||
ASSERT(reg != kNoRegister || loc.IsConstant());
|
||||
|
||||
// Inputs are consumed from the simulated frame (or a peephole push/pop).
|
||||
@@ -1644,7 +1696,8 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
if (!loc.IsConstant()) {
|
||||
// Moves top of stack location of the peephole into the required
|
||||
// input. None of the required moves needs a temp register allocator.
|
||||
EmitMove(locs->in(i), top_of_stack_->locs()->out(0), nullptr);
|
||||
EmitMove(Location::RegisterLocation(reg),
|
||||
top_of_stack_->locs()->out(0), nullptr);
|
||||
}
|
||||
top_of_stack_ = nullptr; // consumed!
|
||||
} else if (loc.IsConstant()) {
|
||||
@@ -1652,6 +1705,24 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
} else {
|
||||
assembler()->PopRegister(reg);
|
||||
}
|
||||
if (!loc.IsConstant()) {
|
||||
switch (instr->RequiredInputRepresentation(i)) {
|
||||
case kUnboxedDouble:
|
||||
ASSERT(fpu_reg != kNoFpuRegister);
|
||||
ASSERT(instr->SpeculativeModeOfInput(i) ==
|
||||
Instruction::kNotSpeculative);
|
||||
assembler()->LoadUnboxedDouble(
|
||||
fpu_reg, reg,
|
||||
compiler::target::Double::value_offset() - kHeapObjectTag);
|
||||
break;
|
||||
default:
|
||||
// No automatic unboxing for other representations.
|
||||
ASSERT(fpu_reg == kNoFpuRegister);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ASSERT(fpu_reg == kNoFpuRegister);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1659,9 +1730,20 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
for (intptr_t i = 0; i < locs->temp_count(); i++) {
|
||||
Location loc = locs->temp(i);
|
||||
if (loc.IsUnallocated()) {
|
||||
ASSERT(loc.policy() == Location::kRequiresRegister);
|
||||
loc = Location::RegisterLocation(AllocateFreeRegister(blocked_registers));
|
||||
locs->set_temp(i, loc);
|
||||
switch (loc.policy()) {
|
||||
case Location::kRequiresRegister:
|
||||
loc = Location::RegisterLocation(
|
||||
AllocateFreeRegister(blocked_registers));
|
||||
locs->set_temp(i, loc);
|
||||
break;
|
||||
case Location::kRequiresFpuRegister:
|
||||
loc = Location::FpuRegisterLocation(
|
||||
AllocateFreeFpuRegister(blocked_fpu_registers));
|
||||
locs->set_temp(i, loc);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1679,6 +1761,9 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
|
||||
result_location = locs->in(0);
|
||||
break;
|
||||
case Location::kRequiresFpuRegister:
|
||||
result_location = Location::FpuRegisterLocation(
|
||||
AllocateFreeFpuRegister(blocked_fpu_registers));
|
||||
break;
|
||||
case Location::kRequiresStackSlot:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@@ -3103,6 +3188,10 @@ void FlowGraphCompiler::FrameStateUpdateWith(Instruction* instr) {
|
||||
void FlowGraphCompiler::FrameStatePush(Definition* defn) {
|
||||
Representation rep = defn->representation();
|
||||
ASSERT(!is_optimizing());
|
||||
if ((rep == kUnboxedDouble) && defn->locs()->out(0).IsFpuRegister()) {
|
||||
// Output value is boxed in the instruction epilogue.
|
||||
rep = kTagged;
|
||||
}
|
||||
ASSERT((rep == kTagged) || (rep == kUntagged) ||
|
||||
RepresentationUtils::IsUnboxedInteger(rep));
|
||||
ASSERT(rep != kUntagged || flow_graph_.IsIrregexpFunction());
|
||||
|
||||
@@ -241,7 +241,25 @@ void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) {
|
||||
}
|
||||
Definition* defn = instr->AsDefinition();
|
||||
if ((defn != NULL) && defn->HasTemp()) {
|
||||
__ Push(defn->locs()->out(0).reg());
|
||||
const Location value = defn->locs()->out(0);
|
||||
if (value.IsRegister()) {
|
||||
__ PushRegister(value.reg());
|
||||
} else if (value.IsFpuRegister()) {
|
||||
ASSERT(instr->representation() == kUnboxedDouble);
|
||||
// In unoptimized code at instruction epilogue the only
|
||||
// live register is an output register.
|
||||
instr->locs()->live_registers()->Clear();
|
||||
if (value.fpu_reg() != BoxDoubleStubABI::kValueReg) {
|
||||
__ vmovd(EvenDRegisterOf(BoxDoubleStubABI::kValueReg),
|
||||
EvenDRegisterOf(value.fpu_reg()));
|
||||
}
|
||||
GenerateNonLazyDeoptableStubCall(
|
||||
InstructionSource(), // No token position.
|
||||
StubCode::BoxDouble(), UntaggedPcDescriptors::kOther, instr->locs());
|
||||
__ PushRegister(BoxDoubleStubABI::kResultReg);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -232,7 +232,24 @@ void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) {
|
||||
}
|
||||
Definition* defn = instr->AsDefinition();
|
||||
if ((defn != NULL) && defn->HasTemp()) {
|
||||
__ Push(defn->locs()->out(0).reg());
|
||||
const Location value = defn->locs()->out(0);
|
||||
if (value.IsRegister()) {
|
||||
__ PushRegister(value.reg());
|
||||
} else if (value.IsFpuRegister()) {
|
||||
ASSERT(instr->representation() == kUnboxedDouble);
|
||||
// In unoptimized code at instruction epilogue the only
|
||||
// live register is an output register.
|
||||
instr->locs()->live_registers()->Clear();
|
||||
if (value.fpu_reg() != BoxDoubleStubABI::kValueReg) {
|
||||
__ fmovdd(BoxDoubleStubABI::kValueReg, value.fpu_reg());
|
||||
}
|
||||
GenerateNonLazyDeoptableStubCall(
|
||||
InstructionSource(), // No token position.
|
||||
StubCode::BoxDouble(), UntaggedPcDescriptors::kOther, instr->locs());
|
||||
__ PushRegister(BoxDoubleStubABI::kResultReg);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -363,7 +363,19 @@ void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) {
|
||||
if ((defn != NULL) && defn->HasTemp()) {
|
||||
Location value = defn->locs()->out(0);
|
||||
if (value.IsRegister()) {
|
||||
__ pushl(value.reg());
|
||||
__ PushRegister(value.reg());
|
||||
} else if (value.IsFpuRegister()) {
|
||||
ASSERT(instr->representation() == kUnboxedDouble);
|
||||
// In unoptimized code at instruction epilogue the only
|
||||
// live register is an output register.
|
||||
instr->locs()->live_registers()->Clear();
|
||||
if (value.fpu_reg() != BoxDoubleStubABI::kValueReg) {
|
||||
__ movaps(BoxDoubleStubABI::kValueReg, value.fpu_reg());
|
||||
}
|
||||
GenerateNonLazyDeoptableStubCall(
|
||||
InstructionSource(), // No token position.
|
||||
StubCode::BoxDouble(), UntaggedPcDescriptors::kOther, instr->locs());
|
||||
__ PushRegister(BoxDoubleStubABI::kResultReg);
|
||||
} else if (value.IsConstant()) {
|
||||
__ PushObject(value.constant());
|
||||
} else {
|
||||
|
||||
@@ -235,7 +235,19 @@ void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) {
|
||||
if ((defn != NULL) && defn->HasTemp()) {
|
||||
Location value = defn->locs()->out(0);
|
||||
if (value.IsRegister()) {
|
||||
__ pushq(value.reg());
|
||||
__ PushRegister(value.reg());
|
||||
} else if (value.IsFpuRegister()) {
|
||||
ASSERT(instr->representation() == kUnboxedDouble);
|
||||
// In unoptimized code at instruction epilogue the only
|
||||
// live register is an output register.
|
||||
instr->locs()->live_registers()->Clear();
|
||||
if (value.fpu_reg() != BoxDoubleStubABI::kValueReg) {
|
||||
__ movaps(BoxDoubleStubABI::kValueReg, value.fpu_reg());
|
||||
}
|
||||
GenerateNonLazyDeoptableStubCall(
|
||||
InstructionSource(), // No token position.
|
||||
StubCode::BoxDouble(), UntaggedPcDescriptors::kOther, instr->locs());
|
||||
__ PushRegister(BoxDoubleStubABI::kResultReg);
|
||||
} else if (value.IsConstant()) {
|
||||
__ PushObject(value.constant());
|
||||
} else {
|
||||
|
||||
@@ -978,7 +978,7 @@ Representation StoreInstanceFieldInstr::RequiredInputRepresentation(
|
||||
// The instance is always tagged.
|
||||
return kTagged;
|
||||
}
|
||||
if (IsUnboxedDartFieldStore()) {
|
||||
if (IsUnboxedDartFieldStore() && CompilerState::Current().is_optimizing()) {
|
||||
return FlowGraph::UnboxedFieldRepresentationOf(slot().field());
|
||||
}
|
||||
return slot().representation();
|
||||
|
||||
@@ -543,6 +543,8 @@ class SmallSet {
|
||||
|
||||
bool IsEmpty() const { return data_ == 0; }
|
||||
|
||||
void Clear() { data_ = 0; }
|
||||
|
||||
intptr_t data() const { return data_; }
|
||||
|
||||
private:
|
||||
@@ -705,6 +707,12 @@ class RegisterSet : public ValueObject {
|
||||
intptr_t cpu_registers() const { return cpu_registers_.data(); }
|
||||
intptr_t fpu_registers() const { return fpu_registers_.data(); }
|
||||
|
||||
void Clear() {
|
||||
cpu_registers_.Clear();
|
||||
fpu_registers_.Clear();
|
||||
untagged_cpu_registers_.Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
SmallSet<Register> cpu_registers_;
|
||||
SmallSet<Register> untagged_cpu_registers_;
|
||||
|
||||
@@ -1105,6 +1105,7 @@ class Thread : public AllStatic {
|
||||
static word stack_limit_offset();
|
||||
static word saved_stack_limit_offset();
|
||||
static word unboxed_int64_runtime_arg_offset();
|
||||
static word unboxed_double_runtime_arg_offset();
|
||||
|
||||
static word callback_code_offset();
|
||||
static word callback_stack_return_offset();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -275,6 +275,7 @@
|
||||
FIELD(Thread, top_offset) \
|
||||
FIELD(Thread, top_resource_offset) \
|
||||
FIELD(Thread, unboxed_int64_runtime_arg_offset) \
|
||||
FIELD(Thread, unboxed_double_runtime_arg_offset) \
|
||||
FIELD(Thread, vm_tag_offset) \
|
||||
FIELD(Thread, write_barrier_code_offset) \
|
||||
FIELD(Thread, write_barrier_entry_point_offset) \
|
||||
|
||||
@@ -1022,6 +1022,34 @@ EMIT_BOX_ALLOCATION(Int32x4)
|
||||
|
||||
#undef EMIT_BOX_ALLOCATION
|
||||
|
||||
void StubCodeCompiler::GenerateBoxDoubleStub(Assembler* assembler) {
|
||||
#if defined(TARGET_ARCH_ARM)
|
||||
if (!TargetCPUFeatures::vfp_supported()) {
|
||||
__ Breakpoint();
|
||||
return;
|
||||
}
|
||||
#endif // defined(TARGET_ARCH_ARM)
|
||||
Label call_runtime;
|
||||
if (!FLAG_use_slow_path && FLAG_inline_alloc) {
|
||||
__ TryAllocate(compiler::DoubleClass(), &call_runtime,
|
||||
compiler::Assembler::kFarJump, BoxDoubleStubABI::kResultReg,
|
||||
BoxDoubleStubABI::kTempReg);
|
||||
__ StoreUnboxedDouble(
|
||||
BoxDoubleStubABI::kValueReg, BoxDoubleStubABI::kResultReg,
|
||||
compiler::target::Double::value_offset() - kHeapObjectTag);
|
||||
__ Ret();
|
||||
}
|
||||
__ Bind(&call_runtime);
|
||||
__ EnterStubFrame();
|
||||
__ PushObject(NullObject()); /* Make room for result. */
|
||||
__ StoreUnboxedDouble(BoxDoubleStubABI::kValueReg, THR,
|
||||
Thread::unboxed_double_runtime_arg_offset());
|
||||
__ CallRuntime(kBoxDoubleRuntimeEntry, 0);
|
||||
__ PopRegister(BoxDoubleStubABI::kResultReg);
|
||||
__ LeaveStubFrame();
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -487,6 +487,13 @@ struct AllocateTypedDataArrayABI {
|
||||
static const Register kLengthReg = R4;
|
||||
};
|
||||
|
||||
// ABI for BoxDoubleStub.
|
||||
struct BoxDoubleStubABI {
|
||||
static const FpuRegister kValueReg = Q0;
|
||||
static const Register kTempReg = R1;
|
||||
static const Register kResultReg = R0;
|
||||
};
|
||||
|
||||
// ABI for DispatchTableNullErrorStub and consequently for all dispatch
|
||||
// table calls (though normal functions will not expect or use this
|
||||
// register). This ABI is added to distinguish memory corruption errors from
|
||||
|
||||
@@ -327,6 +327,13 @@ struct AllocateTypedDataArrayABI {
|
||||
static const Register kLengthReg = R4;
|
||||
};
|
||||
|
||||
// ABI for BoxDoubleStub.
|
||||
struct BoxDoubleStubABI {
|
||||
static const FpuRegister kValueReg = V0;
|
||||
static const Register kTempReg = R1;
|
||||
static const Register kResultReg = R0;
|
||||
};
|
||||
|
||||
// ABI for DispatchTableNullErrorStub and consequently for all dispatch
|
||||
// table calls (though normal functions will not expect or use this
|
||||
// register). This ABI is added to distinguish memory corruption errors from
|
||||
|
||||
@@ -226,6 +226,13 @@ struct AllocateTypedDataArrayABI {
|
||||
static const Register kLengthReg = kResultReg;
|
||||
};
|
||||
|
||||
// ABI for BoxDoubleStub.
|
||||
struct BoxDoubleStubABI {
|
||||
static const FpuRegister kValueReg = XMM0;
|
||||
static const Register kTempReg = EBX;
|
||||
static const Register kResultReg = EAX;
|
||||
};
|
||||
|
||||
// ABI for DispatchTableNullErrorStub and consequently for all dispatch
|
||||
// table calls (though normal functions will not expect or use this
|
||||
// register). This ABI is added to distinguish memory corruption errors from
|
||||
|
||||
@@ -299,6 +299,13 @@ struct AllocateTypedDataArrayABI {
|
||||
static const Register kLengthReg = kResultReg;
|
||||
};
|
||||
|
||||
// ABI for BoxDoubleStub.
|
||||
struct BoxDoubleStubABI {
|
||||
static const FpuRegister kValueReg = XMM0;
|
||||
static const Register kTempReg = RBX;
|
||||
static const Register kResultReg = RAX;
|
||||
};
|
||||
|
||||
// ABI for DispatchTableNullErrorStub and consequently for all dispatch
|
||||
// table calls (though normal functions will not expect or use this
|
||||
// register). This ABI is added to distinguish memory corruption errors from
|
||||
|
||||
@@ -344,6 +344,11 @@ DEFINE_RUNTIME_ENTRY_NO_LAZY_DEOPT(AllocateDouble, 0) {
|
||||
arguments.SetReturn(Object::Handle(zone, Double::New(0.0)));
|
||||
}
|
||||
|
||||
DEFINE_RUNTIME_ENTRY_NO_LAZY_DEOPT(BoxDouble, 0) {
|
||||
const double val = thread->unboxed_double_runtime_arg();
|
||||
arguments.SetReturn(Object::Handle(zone, Double::New(val)));
|
||||
}
|
||||
|
||||
DEFINE_RUNTIME_ENTRY_NO_LAZY_DEOPT(AllocateMint, 0) {
|
||||
if (FLAG_shared_slow_path_triggers_gc) {
|
||||
isolate->group()->heap()->CollectAllGarbage();
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace dart {
|
||||
V(AllocateClosure) \
|
||||
V(AllocateContext) \
|
||||
V(AllocateObject) \
|
||||
V(BoxDouble) \
|
||||
V(BreakpointRuntimeHandler) \
|
||||
V(SingleStepHandler) \
|
||||
V(CloneContext) \
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace dart {
|
||||
V(AllocateObjectParameterized) \
|
||||
V(AllocateObjectSlow) \
|
||||
V(AllocateUnhandledException) \
|
||||
V(BoxDouble) \
|
||||
V(CloneContext) \
|
||||
V(CallToRuntime) \
|
||||
V(LazyCompile) \
|
||||
|
||||
@@ -73,6 +73,7 @@ Thread::Thread(bool is_vm_isolate)
|
||||
marking_stack_block_(NULL),
|
||||
vm_tag_(0),
|
||||
unboxed_int64_runtime_arg_(0),
|
||||
unboxed_double_runtime_arg_(0.0),
|
||||
active_exception_(Object::null()),
|
||||
active_stacktrace_(Object::null()),
|
||||
global_object_pool_(ObjectPool::null()),
|
||||
|
||||
+12
-2
@@ -689,6 +689,15 @@ class Thread : public ThreadState {
|
||||
static intptr_t unboxed_int64_runtime_arg_offset() {
|
||||
return OFFSET_OF(Thread, unboxed_int64_runtime_arg_);
|
||||
}
|
||||
double unboxed_double_runtime_arg() const {
|
||||
return unboxed_double_runtime_arg_;
|
||||
}
|
||||
void set_unboxed_double_runtime_arg(double value) {
|
||||
unboxed_double_runtime_arg_ = value;
|
||||
}
|
||||
static intptr_t unboxed_double_runtime_arg_offset() {
|
||||
return OFFSET_OF(Thread, unboxed_double_runtime_arg_);
|
||||
}
|
||||
|
||||
GrowableObjectArrayPtr pending_functions();
|
||||
void clear_pending_functions();
|
||||
@@ -1050,11 +1059,12 @@ class Thread : public ThreadState {
|
||||
MarkingStackBlock* marking_stack_block_;
|
||||
MarkingStackBlock* deferred_marking_stack_block_;
|
||||
uword volatile vm_tag_;
|
||||
// Memory location dedicated for passing unboxed int64 values from
|
||||
// generated code to runtime.
|
||||
// Memory locations dedicated for passing unboxed int64 and double
|
||||
// values from generated code to runtime.
|
||||
// TODO(dartbug.com/33549): Clean this up when unboxed values
|
||||
// could be passed as arguments.
|
||||
ALIGN8 int64_t unboxed_int64_runtime_arg_;
|
||||
ALIGN8 double unboxed_double_runtime_arg_;
|
||||
|
||||
// State that is cached in the TLS for fast access in generated code.
|
||||
#define DECLARE_MEMBERS(type_name, member_name, expr, default_init_value) \
|
||||
|
||||
Reference in New Issue
Block a user