[maglev][arm64] Add StringFromCharCode

... and any other node needed to test it.

Bug: v8:7700
Change-Id: Ia37fdcb1db3b6fb986f026696454d443236d011c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4111600
Reviewed-by: Patrick Thier <pthier@chromium.org>
Auto-Submit: Victor Gomes <victorgomes@chromium.org>
Commit-Queue: Patrick Thier <pthier@chromium.org>
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84893}
This commit is contained in:
Victor Gomes 2022-12-16 10:15:40 +01:00 committed by V8 LUCI CQ
parent 3706e2e29a
commit 9c2ac00eb1
10 changed files with 240 additions and 46 deletions

View File

@ -4848,6 +4848,7 @@ v8_source_set("v8_base_without_compiler") {
if (v8_enable_maglev) { if (v8_enable_maglev) {
sources += [ sources += [
"src/maglev/maglev-assembler.cc",
"src/maglev/maglev-code-generator.cc", "src/maglev/maglev-code-generator.cc",
"src/maglev/maglev-compilation-info.cc", "src/maglev/maglev-compilation-info.cc",
"src/maglev/maglev-compilation-unit.cc", "src/maglev/maglev-compilation-unit.cc",

View File

@ -361,6 +361,12 @@ inline void MaglevAssembler::FinishCode() {
ForceConstantPoolEmissionWithoutJump(); ForceConstantPoolEmissionWithoutJump();
} }
template <typename NodeT>
inline void MaglevAssembler::EmitEagerDeoptIfNotEqual(DeoptimizeReason reason,
NodeT* node) {
EmitEagerDeoptIf(ne, reason, node);
}
inline void MaglevAssembler::MaterialiseValueNode(Register dst, inline void MaglevAssembler::MaterialiseValueNode(Register dst,
ValueNode* value) { ValueNode* value) {
// TODO(v8:7700): Implement! // TODO(v8:7700): Implement!

View File

@ -338,6 +338,70 @@ void MaglevAssembler::MaybeEmitDeoptBuiltinsCall(size_t eager_deopt_count,
} }
} }
void MaglevAssembler::AllocateTwoByteString(RegisterSnapshot register_snapshot,
Register result, int length) {
Allocate(register_snapshot, result, SeqTwoByteString::SizeFor(length));
UseScratchRegisterScope scope(this);
Register scratch = scope.AcquireX();
LoadRoot(scratch, RootIndex::kStringMap);
StoreTaggedField(scratch, FieldMemOperand(result, HeapObject::kMapOffset));
Move(scratch, Name::kEmptyHashField);
StoreTaggedField(scratch, FieldMemOperand(result, Name::kRawHashFieldOffset));
Move(scratch, length);
StoreTaggedField(scratch, FieldMemOperand(result, String::kLengthOffset));
}
void MaglevAssembler::LoadSingleCharacterString(Register result,
Register char_code,
Register scratch) {
DCHECK_NE(char_code, scratch);
if (v8_flags.debug_code) {
Cmp(char_code, Immediate(String::kMaxOneByteCharCode));
Assert(ls, AbortReason::kUnexpectedValue);
}
Register table = scratch;
LoadRoot(table, RootIndex::kSingleCharacterStringTable);
Add(table, table, Operand(char_code, LSL, kTaggedSizeLog2));
DecompressAnyTagged(result, FieldMemOperand(table, FixedArray::kHeaderSize));
}
void MaglevAssembler::StringFromCharCode(RegisterSnapshot register_snapshot,
Label* char_code_fits_one_byte,
Register result, Register char_code,
Register scratch) {
DCHECK_NE(char_code, scratch);
ZoneLabelRef done(this);
Cmp(char_code, Immediate(String::kMaxOneByteCharCode));
JumpToDeferredIf(
hi,
[](MaglevAssembler* masm, RegisterSnapshot register_snapshot,
ZoneLabelRef done, Register result, Register char_code,
Register scratch) {
// Be sure to save {char_code}. If it aliases with {result}, use
// the scratch register.
if (char_code == result) {
// This is guaranteed to be true since we've already checked
// char_code != scratch.
DCHECK_NE(scratch, result);
__ Move(scratch, char_code);
char_code = scratch;
}
DCHECK(!register_snapshot.live_tagged_registers.has(char_code));
register_snapshot.live_registers.set(char_code);
__ AllocateTwoByteString(register_snapshot, result, 1);
__ And(scratch, char_code, Immediate(0xFFFF));
__ Strh(scratch.W(),
FieldMemOperand(result, SeqTwoByteString::kHeaderSize));
__ B(*done);
},
register_snapshot, done, result, char_code, scratch);
if (char_code_fits_one_byte != nullptr) {
bind(char_code_fits_one_byte);
}
LoadSingleCharacterString(result, char_code, scratch);
bind(*done);
}
void MaglevAssembler::StringCharCodeAt(RegisterSnapshot& register_snapshot, void MaglevAssembler::StringCharCodeAt(RegisterSnapshot& register_snapshot,
Register result, Register string, Register result, Register string,
Register index, Register not_used, Register index, Register not_used,
@ -478,6 +542,47 @@ void MaglevAssembler::StringCharCodeAt(RegisterSnapshot& register_snapshot,
} }
} }
void MaglevAssembler::TruncateDoubleToInt32(Register dst, DoubleRegister src) {
if (CpuFeatures::IsSupported(JSCVT)) {
Fjcvtzs(dst.W(), src);
return;
}
ZoneLabelRef done(this);
// Try to convert with an FPU convert instruction. It's trivial to compute
// the modulo operation on an integer register so we convert to a 64-bit
// integer.
//
// Fcvtzs will saturate to INT64_MIN (0x800...00) or INT64_MAX (0x7FF...FF)
// when the double is out of range. NaNs and infinities will be converted to 0
// (as ECMA-262 requires).
Fcvtzs(dst.X(), src);
// The values INT64_MIN (0x800...00) or INT64_MAX (0x7FF...FF) are not
// representable using a double, so if the result is one of those then we know
// that saturation occurred, and we need to manually handle the conversion.
//
// It is easy to detect INT64_MIN and INT64_MAX because adding or subtracting
// 1 will cause signed overflow.
Cmp(dst.X(), 1);
Ccmp(dst.X(), -1, VFlag, vc);
JumpToDeferredIf(
vs,
[](MaglevAssembler* masm, DoubleRegister src, Register dst,
ZoneLabelRef done) {
__ MacroAssembler::Push(xzr, src);
__ CallBuiltin(Builtin::kDoubleToI);
__ Ldr(dst.W(), MemOperand(sp, 0));
DCHECK_EQ(xzr.SizeInBytes(), src.SizeInBytes());
__ Drop(2);
__ B(*done);
},
src, dst, done);
Bind(*done);
}
} // namespace maglev } // namespace maglev
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -112,7 +112,6 @@ void Int32DecrementWithOverflow::GenerateCode(MaglevAssembler* masm,
} }
UNIMPLEMENTED_NODE_WITH_CALL(Float64Ieee754Unary) UNIMPLEMENTED_NODE_WITH_CALL(Float64Ieee754Unary)
UNIMPLEMENTED_NODE_WITH_CALL(BuiltinStringFromCharCode)
UNIMPLEMENTED_NODE_WITH_CALL(CallBuiltin) UNIMPLEMENTED_NODE_WITH_CALL(CallBuiltin)
UNIMPLEMENTED_NODE_WITH_CALL(Construct) UNIMPLEMENTED_NODE_WITH_CALL(Construct)
UNIMPLEMENTED_NODE_WITH_CALL(ConstructWithSpread) UNIMPLEMENTED_NODE_WITH_CALL(ConstructWithSpread)
@ -125,7 +124,6 @@ UNIMPLEMENTED_NODE(LoadUnsignedIntTypedArrayElement, elements_kind_)
UNIMPLEMENTED_NODE(LoadDoubleTypedArrayElement, elements_kind_) UNIMPLEMENTED_NODE(LoadDoubleTypedArrayElement, elements_kind_)
UNIMPLEMENTED_NODE(CheckedSmiTagUint32) UNIMPLEMENTED_NODE(CheckedSmiTagUint32)
UNIMPLEMENTED_NODE_WITH_CALL(CheckedObjectToIndex) UNIMPLEMENTED_NODE_WITH_CALL(CheckedObjectToIndex)
UNIMPLEMENTED_NODE(CheckedTruncateNumberToInt32)
UNIMPLEMENTED_NODE(CheckedInt32ToUint32) UNIMPLEMENTED_NODE(CheckedInt32ToUint32)
UNIMPLEMENTED_NODE(CheckedUint32ToInt32) UNIMPLEMENTED_NODE(CheckedUint32ToInt32)
UNIMPLEMENTED_NODE(ChangeInt32ToFloat64) UNIMPLEMENTED_NODE(ChangeInt32ToFloat64)
@ -143,7 +141,6 @@ UNIMPLEMENTED_NODE(TestTypeOf, literal_)
UNIMPLEMENTED_NODE_WITH_CALL(ToObject) UNIMPLEMENTED_NODE_WITH_CALL(ToObject)
UNIMPLEMENTED_NODE_WITH_CALL(ToString) UNIMPLEMENTED_NODE_WITH_CALL(ToString)
UNIMPLEMENTED_NODE(AssertInt32, condition_, reason_) UNIMPLEMENTED_NODE(AssertInt32, condition_, reason_)
UNIMPLEMENTED_NODE(CheckDynamicValue)
UNIMPLEMENTED_NODE(CheckUint32IsSmi) UNIMPLEMENTED_NODE(CheckUint32IsSmi)
UNIMPLEMENTED_NODE(CheckHeapObject) UNIMPLEMENTED_NODE(CheckHeapObject)
UNIMPLEMENTED_NODE(CheckJSArrayBounds) UNIMPLEMENTED_NODE(CheckJSArrayBounds)
@ -153,9 +150,7 @@ UNIMPLEMENTED_NODE(CheckJSTypedArrayBounds, elements_kind_)
UNIMPLEMENTED_NODE(CheckMaps, check_type_) UNIMPLEMENTED_NODE(CheckMaps, check_type_)
UNIMPLEMENTED_NODE_WITH_CALL(CheckMapsWithMigration, check_type_) UNIMPLEMENTED_NODE_WITH_CALL(CheckMapsWithMigration, check_type_)
UNIMPLEMENTED_NODE(CheckNumber) UNIMPLEMENTED_NODE(CheckNumber)
UNIMPLEMENTED_NODE(CheckSmi)
UNIMPLEMENTED_NODE(CheckString, check_type_) UNIMPLEMENTED_NODE(CheckString, check_type_)
UNIMPLEMENTED_NODE(CheckValue)
UNIMPLEMENTED_NODE(CheckInstanceType, check_type_) UNIMPLEMENTED_NODE(CheckInstanceType, check_type_)
UNIMPLEMENTED_NODE_WITH_CALL(GeneratorStore) UNIMPLEMENTED_NODE_WITH_CALL(GeneratorStore)
UNIMPLEMENTED_NODE_WITH_CALL(JumpLoopPrologue, loop_depth_, unit_) UNIMPLEMENTED_NODE_WITH_CALL(JumpLoopPrologue, loop_depth_, unit_)
@ -173,6 +168,38 @@ UNIMPLEMENTED_NODE(BranchIfUndefinedOrNull)
UNIMPLEMENTED_NODE(BranchIfJSReceiver) UNIMPLEMENTED_NODE(BranchIfJSReceiver)
UNIMPLEMENTED_NODE(Switch) UNIMPLEMENTED_NODE(Switch)
int BuiltinStringFromCharCode::MaxCallStackArgs() const {
return AllocateDescriptor::GetStackParameterCount();
}
void BuiltinStringFromCharCode::SetValueLocationConstraints() {
if (code_input().node()->Is<Int32Constant>()) {
UseAny(code_input());
} else {
UseRegister(code_input());
set_temporaries_needed(1);
}
DefineAsRegister(this);
}
void BuiltinStringFromCharCode::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register scratch = general_temporaries().PopFirst();
Register result_string = ToRegister(result());
if (Int32Constant* constant = code_input().node()->TryCast<Int32Constant>()) {
int32_t char_code = constant->value();
if (0 <= char_code && char_code < String::kMaxOneByteCharCode) {
__ LoadSingleCharacterString(result_string, char_code);
} else {
__ AllocateTwoByteString(register_snapshot(), result_string, 1);
__ Move(scratch, char_code & 0xFFFF);
__ Strh(scratch.W(),
FieldMemOperand(result_string, SeqTwoByteString::kHeaderSize));
}
} else {
__ StringFromCharCode(register_snapshot(), nullptr, result_string,
ToRegister(code_input()), scratch);
}
}
int BuiltinStringPrototypeCharCodeAt::MaxCallStackArgs() const { int BuiltinStringPrototypeCharCodeAt::MaxCallStackArgs() const {
DCHECK_EQ(Runtime::FunctionForId(Runtime::kStringCharCodeAt)->nargs, 2); DCHECK_EQ(Runtime::FunctionForId(Runtime::kStringCharCodeAt)->nargs, 2);
return 2; return 2;
@ -219,6 +246,33 @@ void CreateEmptyObjectLiteral::GenerateCode(MaglevAssembler* masm,
} }
} }
void CheckedTruncateNumberToInt32::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void CheckedTruncateNumberToInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(input());
Register result_reg = ToRegister(result());
Label is_not_smi, done;
// Check if Smi.
__ JumpIfNotSmi(value, &is_not_smi);
// If Smi, convert to Int32.
__ SmiToInt32(result_reg, value);
__ B(&done);
__ bind(&is_not_smi);
// Check if HeapNumber, deopt otherwise.
UseScratchRegisterScope temps(masm);
Register scratch = temps.AcquireW();
__ Ldr(scratch, FieldMemOperand(value, HeapObject::kMapOffset));
__ CompareRoot(scratch, RootIndex::kHeapNumberMap);
__ EmitEagerDeoptIf(ne, DeoptimizeReason::kNotANumber, this);
DoubleRegister double_value = temps.AcquireD();
__ Ldr(double_value, FieldMemOperand(value, HeapNumber::kValueOffset));
__ TruncateDoubleToInt32(result_reg, double_value);
__ bind(&done);
}
void CheckSymbol::SetValueLocationConstraints() { void CheckSymbol::SetValueLocationConstraints() {
UseRegister(receiver_input()); UseRegister(receiver_input());
} }

View File

@ -0,0 +1,32 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/maglev/maglev-code-generator.h"
#ifdef V8_TARGET_ARCH_ARM64
#include "src/maglev/arm64/maglev-assembler-arm64-inl.h"
#elif V8_TARGET_ARCH_X64
#include "src/maglev/x64/maglev-assembler-x64-inl.h"
#else
#error "Maglev does not supported this architecture."
#endif
namespace v8 {
namespace internal {
namespace maglev {
void MaglevAssembler::LoadSingleCharacterString(Register result,
int char_code) {
DCHECK_GE(char_code, 0);
DCHECK_LT(char_code, String::kMaxOneByteCharCode);
Register table = result;
LoadRoot(table, RootIndex::kSingleCharacterStringTable);
DecompressAnyTagged(
result, FieldMemOperand(
table, FixedArray::kHeaderSize + char_code * kTaggedSize));
}
} // namespace maglev
} // namespace internal
} // namespace v8

View File

@ -127,6 +127,8 @@ class MaglevAssembler : public MacroAssembler {
template <typename NodeT> template <typename NodeT>
inline void EmitEagerDeoptIf(Condition cond, DeoptimizeReason reason, inline void EmitEagerDeoptIf(Condition cond, DeoptimizeReason reason,
NodeT* node); NodeT* node);
template <typename NodeT>
inline void EmitEagerDeoptIfNotEqual(DeoptimizeReason reason, NodeT* node);
inline void MaterialiseValueNode(Register dst, ValueNode* value); inline void MaterialiseValueNode(Register dst, ValueNode* value);

View File

@ -915,6 +915,35 @@ void StoreGlobal::GenerateCode(MaglevAssembler* masm,
masm->DefineExceptionHandlerAndLazyDeoptPoint(this); masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
} }
void CheckValue::SetValueLocationConstraints() { UseRegister(target_input()); }
void CheckValue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register target = ToRegister(target_input());
__ Cmp(target, value().object());
__ EmitEagerDeoptIfNotEqual(DeoptimizeReason::kWrongValue, this);
}
void CheckDynamicValue::SetValueLocationConstraints() {
UseRegister(first_input());
UseRegister(second_input());
}
void CheckDynamicValue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register first = ToRegister(first_input());
Register second = ToRegister(second_input());
__ CompareInt32(first, second);
__ EmitEagerDeoptIfNotEqual(DeoptimizeReason::kWrongValue, this);
}
void CheckSmi::SetValueLocationConstraints() { UseRegister(receiver_input()); }
void CheckSmi::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
Condition is_smi = __ CheckSmi(object);
__ EmitEagerDeoptIf(NegateCondition(is_smi), DeoptimizeReason::kNotASmi,
this);
}
void CheckInt32Condition::SetValueLocationConstraints() { void CheckInt32Condition::SetValueLocationConstraints() {
UseRegister(left_input()); UseRegister(left_input());
UseRegister(right_input()); UseRegister(right_input());

View File

@ -329,6 +329,12 @@ inline void MaglevAssembler::JumpIfTaggedEqual(Register r1, Register r2,
inline void MaglevAssembler::Pop(Register dst) { MacroAssembler::Pop(dst); } inline void MaglevAssembler::Pop(Register dst) { MacroAssembler::Pop(dst); }
template <typename NodeT>
inline void MaglevAssembler::EmitEagerDeoptIfNotEqual(DeoptimizeReason reason,
NodeT* node) {
EmitEagerDeoptIf(not_equal, reason, node);
}
inline void MaglevAssembler::MaterialiseValueNode(Register dst, inline void MaglevAssembler::MaterialiseValueNode(Register dst,
ValueNode* value) { ValueNode* value) {
switch (value->opcode()) { switch (value->opcode()) {

View File

@ -97,16 +97,6 @@ void MaglevAssembler::AllocateTwoByteString(RegisterSnapshot register_snapshot,
Immediate(length)); Immediate(length));
} }
void MaglevAssembler::LoadSingleCharacterString(Register result,
int char_code) {
DCHECK_GE(char_code, 0);
DCHECK_LT(char_code, String::kMaxOneByteCharCode);
Register table = result;
LoadRoot(table, RootIndex::kSingleCharacterStringTable);
DecompressAnyTagged(result, FieldOperand(table, FixedArray::kHeaderSize +
char_code * kTaggedSize));
}
void MaglevAssembler::LoadSingleCharacterString(Register result, void MaglevAssembler::LoadSingleCharacterString(Register result,
Register char_code, Register char_code,
Register scratch) { Register scratch) {

View File

@ -297,37 +297,6 @@ void CheckMaps::GenerateCode(MaglevAssembler* masm,
__ bind(&done); __ bind(&done);
} }
void CheckValue::SetValueLocationConstraints() { UseRegister(target_input()); }
void CheckValue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register target = ToRegister(target_input());
__ Cmp(target, value().object());
__ EmitEagerDeoptIf(not_equal, DeoptimizeReason::kWrongValue, this);
}
void CheckDynamicValue::SetValueLocationConstraints() {
UseRegister(first_input());
UseRegister(second_input());
}
void CheckDynamicValue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register first = ToRegister(first_input());
Register second = ToRegister(second_input());
__ cmpl(first, second);
__ EmitEagerDeoptIf(not_equal, DeoptimizeReason::kWrongValue, this);
}
void CheckSmi::SetValueLocationConstraints() { UseRegister(receiver_input()); }
void CheckSmi::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
Condition is_smi = __ CheckSmi(object);
__ EmitEagerDeoptIf(NegateCondition(is_smi), DeoptimizeReason::kNotASmi,
this);
}
void CheckNumber::SetValueLocationConstraints() { void CheckNumber::SetValueLocationConstraints() {
UseRegister(receiver_input()); UseRegister(receiver_input());
} }