Bug 1925195 - Part 4: Inline Date.prototype.get{Hours,Minutes,Seconds}. r=jandem

The local time components aren't stored in separate slots, but instead in a
single slot which stores the number of seconds since the start of the year.

Differential Revision: https://phabricator.services.mozilla.com/D225927
This commit is contained in:
André Bargull 2024-10-17 12:37:02 +00:00
parent da4450bc1e
commit db611ec954
15 changed files with 442 additions and 3 deletions

View File

@ -111,3 +111,72 @@ function testDateGetFullYearMonthDateDay() {
}
}
testDateGetFullYearMonthDateDay();
function testDateGetHours() {
var timeValues = [
0,
12,
23,
NaN,
];
for (var i = 0; i < 250; ++i) {
var t = timeValues[i & 3];
// Create a new Date object with an uninitialized local time cache.
var d = new Date(2000, 0, 1, t);
// First call to getHours initializes the cache.
assertEq(d.getHours(), t);
// Second call to getHours uses the cached value.
assertEq(d.getHours(), t);
}
}
testDateGetHours();
function testDateGetMinutes() {
var timeValues = [
0,
30,
59,
NaN,
];
for (var i = 0; i < 250; ++i) {
var t = timeValues[i & 3];
// Create a new Date object with an uninitialized local time cache.
var d = new Date(2000, 0, 1, 0, t);
// First call to getMinutes initializes the cache.
assertEq(d.getMinutes(), t);
// Second call to getMinutes uses the cached value.
assertEq(d.getMinutes(), t);
}
}
testDateGetMinutes();
function testDateGetSeconds() {
var timeValues = [
0,
30,
59,
NaN,
];
for (var i = 0; i < 250; ++i) {
var t = timeValues[i & 3];
// Create a new Date object with an uninitialized local time cache.
var d = new Date(2000, 0, 1, 0, 0, t);
// First call to getSeconds initializes the cache.
assertEq(d.getSeconds(), t);
// Second call to getSeconds uses the cached value.
assertEq(d.getSeconds(), t);
}
}
testDateGetSeconds();

View File

@ -10503,6 +10503,24 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDateGet(
case DateComponent::Day:
writer.loadFixedSlotResult(objId, DateObject::offsetOfLocalDaySlot());
break;
case DateComponent::Hours: {
ValOperandId secondsIntoYearValId = writer.loadFixedSlot(
objId, DateObject::offsetOfLocalSecondsIntoYearSlot());
writer.dateHoursFromSecondsIntoYearResult(secondsIntoYearValId);
break;
}
case DateComponent::Minutes: {
ValOperandId secondsIntoYearValId = writer.loadFixedSlot(
objId, DateObject::offsetOfLocalSecondsIntoYearSlot());
writer.dateMinutesFromSecondsIntoYearResult(secondsIntoYearValId);
break;
}
case DateComponent::Seconds: {
ValOperandId secondsIntoYearValId = writer.loadFixedSlot(
objId, DateObject::offsetOfLocalSecondsIntoYearSlot());
writer.dateSecondsFromSecondsIntoYearResult(secondsIntoYearValId);
break;
}
}
writer.returnFromIC();
@ -10520,6 +10538,15 @@ AttachDecision InlinableNativeIRGenerator::tryAttachDateGet(
case DateComponent::Day:
trackAttached("DateGetDay");
break;
case DateComponent::Hours:
trackAttached("DateGetHours");
break;
case DateComponent::Minutes:
trackAttached("DateGetMinutes");
break;
case DateComponent::Seconds:
trackAttached("DateGetSeconds");
break;
}
return AttachDecision::Attach;
}
@ -12239,6 +12266,12 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() {
return tryAttachDateGet(DateComponent::Date);
case InlinableNative::DateGetDay:
return tryAttachDateGet(DateComponent::Day);
case InlinableNative::DateGetHours:
return tryAttachDateGet(DateComponent::Hours);
case InlinableNative::DateGetMinutes:
return tryAttachDateGet(DateComponent::Minutes);
case InlinableNative::DateGetSeconds:
return tryAttachDateGet(DateComponent::Seconds);
// Testing functions.
case InlinableNative::TestBailout:

View File

@ -10820,6 +10820,51 @@ bool CacheIRCompiler::emitDateFillLocalTimeSlots(ObjOperandId dateId) {
return true;
}
bool CacheIRCompiler::emitDateHoursFromSecondsIntoYearResult(
ValOperandId secondsIntoYearId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoOutputRegister output(*this);
ValueOperand secondsIntoYear =
allocator.useValueRegister(masm, secondsIntoYearId);
AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output);
AutoScratchRegister scratch2(allocator, masm);
masm.dateHoursFromSecondsIntoYear(secondsIntoYear, output.valueReg(),
scratch1, scratch2);
return true;
}
bool CacheIRCompiler::emitDateMinutesFromSecondsIntoYearResult(
ValOperandId secondsIntoYearId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoOutputRegister output(*this);
ValueOperand secondsIntoYear =
allocator.useValueRegister(masm, secondsIntoYearId);
AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output);
AutoScratchRegister scratch2(allocator, masm);
masm.dateMinutesFromSecondsIntoYear(secondsIntoYear, output.valueReg(),
scratch1, scratch2);
return true;
}
bool CacheIRCompiler::emitDateSecondsFromSecondsIntoYearResult(
ValOperandId secondsIntoYearId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoOutputRegister output(*this);
ValueOperand secondsIntoYear =
allocator.useValueRegister(masm, secondsIntoYearId);
AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output);
AutoScratchRegister scratch2(allocator, masm);
masm.dateSecondsFromSecondsIntoYear(secondsIntoYear, output.valueReg(),
scratch1, scratch2);
return true;
}
bool CacheIRCompiler::emitArrayFromArgumentsObjectResult(ObjOperandId objId,
uint32_t shapeOffset) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);

View File

@ -642,6 +642,9 @@ class MOZ_RAII InlinableNativeIRGenerator {
Month,
Date,
Day,
Hours,
Minutes,
Seconds,
};
AttachDecision tryAttachArrayPush();

View File

@ -3517,6 +3517,27 @@
args:
date: ObjId
- name: DateHoursFromSecondsIntoYearResult
shared: true
transpile: true
cost_estimate: 2
args:
secondsIntoYear: ValId
- name: DateMinutesFromSecondsIntoYearResult
shared: true
transpile: true
cost_estimate: 2
args:
secondsIntoYear: ValId
- name: DateSecondsFromSecondsIntoYearResult
shared: true
transpile: true
cost_estimate: 2
args:
secondsIntoYear: ValId
- name: ArrayFromArgumentsObjectResult
shared: true
transpile: true

View File

@ -21789,6 +21789,39 @@ void CodeGenerator::visitDateFillLocalTimeSlots(LDateFillLocalTimeSlots* ins) {
masm.dateFillLocalTimeSlots(date, temp, liveVolatileRegs(ins));
}
void CodeGenerator::visitDateHoursFromSecondsIntoYear(
LDateHoursFromSecondsIntoYear* ins) {
auto secondsIntoYear =
ToValue(ins, LDateHoursFromSecondsIntoYear::SecondsIntoYearIndex);
auto output = ToOutValue(ins);
Register temp0 = ToRegister(ins->temp0());
Register temp1 = ToRegister(ins->temp1());
masm.dateHoursFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
}
void CodeGenerator::visitDateMinutesFromSecondsIntoYear(
LDateMinutesFromSecondsIntoYear* ins) {
auto secondsIntoYear =
ToValue(ins, LDateMinutesFromSecondsIntoYear::SecondsIntoYearIndex);
auto output = ToOutValue(ins);
Register temp0 = ToRegister(ins->temp0());
Register temp1 = ToRegister(ins->temp1());
masm.dateMinutesFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
}
void CodeGenerator::visitDateSecondsFromSecondsIntoYear(
LDateSecondsFromSecondsIntoYear* ins) {
auto secondsIntoYear =
ToValue(ins, LDateSecondsFromSecondsIntoYear::SecondsIntoYearIndex);
auto output = ToOutValue(ins);
Register temp0 = ToRegister(ins->temp0());
Register temp1 = ToRegister(ins->temp1());
masm.dateSecondsFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
}
template <size_t NumDefs>
void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) {
wasm::JitCallStackArgVector stackArgs;

View File

@ -301,6 +301,9 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) {
case InlinableNative::DateGetMonth:
case InlinableNative::DateGetDate:
case InlinableNative::DateGetDay:
case InlinableNative::DateGetHours:
case InlinableNative::DateGetMinutes:
case InlinableNative::DateGetSeconds:
case InlinableNative::FunctionBind:
case InlinableNative::MapGet:
case InlinableNative::MapHas:

View File

@ -77,6 +77,9 @@
_(DateGetMonth) \
_(DateGetDate) \
_(DateGetDay) \
_(DateGetHours) \
_(DateGetMinutes) \
_(DateGetSeconds) \
\
_(FunctionBind) \
\

View File

@ -7715,6 +7715,27 @@ void LIRGenerator::visitDateFillLocalTimeSlots(MDateFillLocalTimeSlots* ins) {
assignSafepoint(lir, ins);
}
void LIRGenerator::visitDateHoursFromSecondsIntoYear(
MDateHoursFromSecondsIntoYear* ins) {
auto* lir = new (alloc()) LDateHoursFromSecondsIntoYear(
useBox(ins->secondsIntoYear()), temp(), temp());
defineBox(lir, ins);
}
void LIRGenerator::visitDateMinutesFromSecondsIntoYear(
MDateMinutesFromSecondsIntoYear* ins) {
auto* lir = new (alloc()) LDateMinutesFromSecondsIntoYear(
useBox(ins->secondsIntoYear()), temp(), temp());
defineBox(lir, ins);
}
void LIRGenerator::visitDateSecondsFromSecondsIntoYear(
MDateSecondsFromSecondsIntoYear* ins) {
auto* lir = new (alloc()) LDateSecondsFromSecondsIntoYear(
useBox(ins->secondsIntoYear()), temp(), temp());
defineBox(lir, ins);
}
void LIRGenerator::visitPostIntPtrConversion(MPostIntPtrConversion* ins) {
// This operation is a no-op.
redefine(ins, ins->input());

View File

@ -3327,6 +3327,36 @@
generate_lir: true
lir_temps: 1
- name: DateHoursFromSecondsIntoYear
operands:
secondsIntoYear: Value
result_type: Value
movable: true
congruent_to: if_operands_equal
alias_set: none
generate_lir: true
lir_temps: 2
- name: DateMinutesFromSecondsIntoYear
operands:
secondsIntoYear: Value
result_type: Value
movable: true
congruent_to: if_operands_equal
alias_set: none
generate_lir: true
lir_temps: 2
- name: DateSecondsFromSecondsIntoYear
operands:
secondsIntoYear: Value
result_type: Value
movable: true
congruent_to: if_operands_equal
alias_set: none
generate_lir: true
lir_temps: 2
- name: PostIntPtrConversion
gen_boilerplate: false

View File

@ -3464,6 +3464,124 @@ void MacroAssembler::dateFillLocalTimeSlots(
bind(&done);
}
void MacroAssembler::udiv32ByConstant(Register src, uint32_t divisor,
Register dest) {
auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(divisor);
MOZ_ASSERT(rmc.multiplier <= UINT32_MAX, "division needs scratch register");
// We first compute |q = (M * n) >> 32), where M = rmc.multiplier.
mulHighUnsigned32(Imm32(rmc.multiplier), src, dest);
// Finish the computation |q = floor(n / d)|.
rshift32(Imm32(rmc.shiftAmount), dest);
}
void MacroAssembler::umod32ByConstant(Register src, uint32_t divisor,
Register dest, Register scratch) {
MOZ_ASSERT(dest != scratch);
auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(divisor);
MOZ_ASSERT(rmc.multiplier <= UINT32_MAX, "division needs scratch register");
if (src != dest) {
move32(src, dest);
}
// We first compute |q = (M * n) >> 32), where M = rmc.multiplier.
mulHighUnsigned32(Imm32(rmc.multiplier), dest, scratch);
// Finish the computation |q = floor(n / d)|.
rshift32(Imm32(rmc.shiftAmount), scratch);
// Compute the remainder from |r = n - q * d|.
mul32(Imm32(divisor), scratch);
sub32(scratch, dest);
}
template <typename GetTimeFn>
void MacroAssembler::dateTimeFromSecondsIntoYear(ValueOperand secondsIntoYear,
ValueOperand output,
Register scratch1,
Register scratch2,
GetTimeFn getTimeFn) {
#ifdef DEBUG
Label okValue;
branchTestInt32(Assembler::Equal, secondsIntoYear, &okValue);
branchTestValue(Assembler::Equal, secondsIntoYear, JS::NaNValue(), &okValue);
assumeUnreachable("secondsIntoYear is an int32 or NaN");
bind(&okValue);
#endif
moveValue(secondsIntoYear, output);
Label done;
fallibleUnboxInt32(secondsIntoYear, scratch1, &done);
#ifdef DEBUG
Label okInt;
branchTest32(Assembler::NotSigned, scratch1, scratch1, &okInt);
assumeUnreachable("secondsIntoYear is an unsigned int32");
bind(&okInt);
#endif
getTimeFn(scratch1, scratch1, scratch2);
tagValue(JSVAL_TYPE_INT32, scratch1, output);
bind(&done);
}
void MacroAssembler::dateHoursFromSecondsIntoYear(ValueOperand secondsIntoYear,
ValueOperand output,
Register scratch1,
Register scratch2) {
// Inline implementation of seconds-into-year to local hours computation from
// date_getHours.
// Compute `(yearSeconds / SecondsPerHour) % HoursPerDay`.
auto hoursFromSecondsIntoYear = [this](Register src, Register dest,
Register scratch) {
udiv32ByConstant(src, SecondsPerHour, dest);
umod32ByConstant(dest, HoursPerDay, dest, scratch);
};
dateTimeFromSecondsIntoYear(secondsIntoYear, output, scratch1, scratch2,
hoursFromSecondsIntoYear);
}
void MacroAssembler::dateMinutesFromSecondsIntoYear(
ValueOperand secondsIntoYear, ValueOperand output, Register scratch1,
Register scratch2) {
// Inline implementation of seconds-into-year to local minutes computation
// from date_getMinutes.
// Compute `(yearSeconds / SecondsPerMinute) % MinutesPerHour`.
auto minutesFromSecondsIntoYear = [this](Register src, Register dest,
Register scratch) {
udiv32ByConstant(src, SecondsPerMinute, dest);
umod32ByConstant(dest, MinutesPerHour, dest, scratch);
};
dateTimeFromSecondsIntoYear(secondsIntoYear, output, scratch1, scratch2,
minutesFromSecondsIntoYear);
}
void MacroAssembler::dateSecondsFromSecondsIntoYear(
ValueOperand secondsIntoYear, ValueOperand output, Register scratch1,
Register scratch2) {
// Inline implementation of seconds-into-year to local seconds computation
// from date_getSeconds.
// Compute `yearSeconds % SecondsPerMinute`.
auto secondsFromSecondsIntoYear = [this](Register src, Register dest,
Register scratch) {
umod32ByConstant(src, SecondsPerMinute, dest, scratch);
};
dateTimeFromSecondsIntoYear(secondsIntoYear, output, scratch1, scratch2,
secondsFromSecondsIntoYear);
}
void MacroAssembler::computeImplicitThis(Register env, ValueOperand output,
Label* slowPath) {
// Inline implementation of ComputeImplicitThis.

View File

@ -5849,6 +5849,30 @@ class MacroAssembler : public MacroAssemblerSpecific {
void dateFillLocalTimeSlots(Register obj, Register scratch,
const LiveRegisterSet& volatileRegs);
private:
void udiv32ByConstant(Register src, uint32_t divisor, Register dest);
void umod32ByConstant(Register src, uint32_t divisor, Register dest,
Register scratch);
template <typename GetTimeFn>
void dateTimeFromSecondsIntoYear(ValueOperand secondsIntoYear,
ValueOperand output, Register scratch1,
Register scratch2, GetTimeFn getTimeFn);
public:
void dateHoursFromSecondsIntoYear(ValueOperand secondsIntoYear,
ValueOperand output, Register scratch1,
Register scratch2);
void dateMinutesFromSecondsIntoYear(ValueOperand secondsIntoYear,
ValueOperand output, Register scratch1,
Register scratch2);
void dateSecondsFromSecondsIntoYear(ValueOperand secondsIntoYear,
ValueOperand output, Register scratch1,
Register scratch2);
void computeImplicitThis(Register env, ValueOperand output, Label* slowPath);
private:

View File

@ -5492,6 +5492,39 @@ bool WarpCacheIRTranspiler::emitDateFillLocalTimeSlots(ObjOperandId dateId) {
return true;
}
bool WarpCacheIRTranspiler::emitDateHoursFromSecondsIntoYearResult(
ValOperandId secondsIntoYearId) {
MDefinition* secondsIntoYear = getOperand(secondsIntoYearId);
auto* ins = MDateHoursFromSecondsIntoYear::New(alloc(), secondsIntoYear);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitDateMinutesFromSecondsIntoYearResult(
ValOperandId secondsIntoYearId) {
MDefinition* secondsIntoYear = getOperand(secondsIntoYearId);
auto* ins = MDateMinutesFromSecondsIntoYear::New(alloc(), secondsIntoYear);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitDateSecondsFromSecondsIntoYearResult(
ValOperandId secondsIntoYearId) {
MDefinition* secondsIntoYear = getOperand(secondsIntoYearId);
auto* ins = MDateSecondsFromSecondsIntoYear::New(alloc(), secondsIntoYear);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitTruthyResult(OperandId inputId) {
MDefinition* input = getOperand(inputId);

View File

@ -4462,11 +4462,11 @@ static const JSFunctionSpec date_methods[] = {
JS_FN("getUTCDate", date_getUTCDate, 0, 0),
JS_INLINABLE_FN("getDay", date_getDay, 0, 0, DateGetDay),
JS_FN("getUTCDay", date_getUTCDay, 0, 0),
JS_FN("getHours", date_getHours, 0, 0),
JS_INLINABLE_FN("getHours", date_getHours, 0, 0, DateGetHours),
JS_FN("getUTCHours", date_getUTCHours, 0, 0),
JS_FN("getMinutes", date_getMinutes, 0, 0),
JS_INLINABLE_FN("getMinutes", date_getMinutes, 0, 0, DateGetMinutes),
JS_FN("getUTCMinutes", date_getUTCMinutes, 0, 0),
JS_FN("getSeconds", date_getSeconds, 0, 0),
JS_INLINABLE_FN("getSeconds", date_getSeconds, 0, 0, DateGetSeconds),
JS_FN("getUTCSeconds", date_getUTCSeconds, 0, 0),
JS_FN("getMilliseconds", date_getMilliseconds, 0, 0),
JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0, 0),

View File

@ -154,6 +154,9 @@ class DateObject : public NativeObject {
static constexpr size_t offsetOfLocalDaySlot() {
return getFixedSlotOffset(LOCAL_DAY_SLOT);
}
static constexpr size_t offsetOfLocalSecondsIntoYearSlot() {
return getFixedSlotOffset(LOCAL_SECONDS_INTO_YEAR_SLOT);
}
};
} // namespace js