[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:
parent
3706e2e29a
commit
9c2ac00eb1
1
BUILD.gn
1
BUILD.gn
@ -4848,6 +4848,7 @@ v8_source_set("v8_base_without_compiler") {
|
||||
|
||||
if (v8_enable_maglev) {
|
||||
sources += [
|
||||
"src/maglev/maglev-assembler.cc",
|
||||
"src/maglev/maglev-code-generator.cc",
|
||||
"src/maglev/maglev-compilation-info.cc",
|
||||
"src/maglev/maglev-compilation-unit.cc",
|
||||
|
@ -361,6 +361,12 @@ inline void MaglevAssembler::FinishCode() {
|
||||
ForceConstantPoolEmissionWithoutJump();
|
||||
}
|
||||
|
||||
template <typename NodeT>
|
||||
inline void MaglevAssembler::EmitEagerDeoptIfNotEqual(DeoptimizeReason reason,
|
||||
NodeT* node) {
|
||||
EmitEagerDeoptIf(ne, reason, node);
|
||||
}
|
||||
|
||||
inline void MaglevAssembler::MaterialiseValueNode(Register dst,
|
||||
ValueNode* value) {
|
||||
// TODO(v8:7700): Implement!
|
||||
|
@ -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,
|
||||
Register result, Register string,
|
||||
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 internal
|
||||
} // namespace v8
|
||||
|
@ -112,7 +112,6 @@ void Int32DecrementWithOverflow::GenerateCode(MaglevAssembler* masm,
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(Float64Ieee754Unary)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(BuiltinStringFromCharCode)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(CallBuiltin)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(Construct)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(ConstructWithSpread)
|
||||
@ -125,7 +124,6 @@ UNIMPLEMENTED_NODE(LoadUnsignedIntTypedArrayElement, elements_kind_)
|
||||
UNIMPLEMENTED_NODE(LoadDoubleTypedArrayElement, elements_kind_)
|
||||
UNIMPLEMENTED_NODE(CheckedSmiTagUint32)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(CheckedObjectToIndex)
|
||||
UNIMPLEMENTED_NODE(CheckedTruncateNumberToInt32)
|
||||
UNIMPLEMENTED_NODE(CheckedInt32ToUint32)
|
||||
UNIMPLEMENTED_NODE(CheckedUint32ToInt32)
|
||||
UNIMPLEMENTED_NODE(ChangeInt32ToFloat64)
|
||||
@ -143,7 +141,6 @@ UNIMPLEMENTED_NODE(TestTypeOf, literal_)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(ToObject)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(ToString)
|
||||
UNIMPLEMENTED_NODE(AssertInt32, condition_, reason_)
|
||||
UNIMPLEMENTED_NODE(CheckDynamicValue)
|
||||
UNIMPLEMENTED_NODE(CheckUint32IsSmi)
|
||||
UNIMPLEMENTED_NODE(CheckHeapObject)
|
||||
UNIMPLEMENTED_NODE(CheckJSArrayBounds)
|
||||
@ -153,9 +150,7 @@ UNIMPLEMENTED_NODE(CheckJSTypedArrayBounds, elements_kind_)
|
||||
UNIMPLEMENTED_NODE(CheckMaps, check_type_)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(CheckMapsWithMigration, check_type_)
|
||||
UNIMPLEMENTED_NODE(CheckNumber)
|
||||
UNIMPLEMENTED_NODE(CheckSmi)
|
||||
UNIMPLEMENTED_NODE(CheckString, check_type_)
|
||||
UNIMPLEMENTED_NODE(CheckValue)
|
||||
UNIMPLEMENTED_NODE(CheckInstanceType, check_type_)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(GeneratorStore)
|
||||
UNIMPLEMENTED_NODE_WITH_CALL(JumpLoopPrologue, loop_depth_, unit_)
|
||||
@ -173,6 +168,38 @@ UNIMPLEMENTED_NODE(BranchIfUndefinedOrNull)
|
||||
UNIMPLEMENTED_NODE(BranchIfJSReceiver)
|
||||
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 {
|
||||
DCHECK_EQ(Runtime::FunctionForId(Runtime::kStringCharCodeAt)->nargs, 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() {
|
||||
UseRegister(receiver_input());
|
||||
}
|
||||
|
32
src/maglev/maglev-assembler.cc
Normal file
32
src/maglev/maglev-assembler.cc
Normal 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
|
@ -127,6 +127,8 @@ class MaglevAssembler : public MacroAssembler {
|
||||
template <typename NodeT>
|
||||
inline void EmitEagerDeoptIf(Condition cond, DeoptimizeReason reason,
|
||||
NodeT* node);
|
||||
template <typename NodeT>
|
||||
inline void EmitEagerDeoptIfNotEqual(DeoptimizeReason reason, NodeT* node);
|
||||
|
||||
inline void MaterialiseValueNode(Register dst, ValueNode* value);
|
||||
|
||||
|
@ -915,6 +915,35 @@ void StoreGlobal::GenerateCode(MaglevAssembler* masm,
|
||||
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() {
|
||||
UseRegister(left_input());
|
||||
UseRegister(right_input());
|
||||
|
@ -329,6 +329,12 @@ inline void MaglevAssembler::JumpIfTaggedEqual(Register r1, Register r2,
|
||||
|
||||
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,
|
||||
ValueNode* value) {
|
||||
switch (value->opcode()) {
|
||||
|
@ -97,16 +97,6 @@ void MaglevAssembler::AllocateTwoByteString(RegisterSnapshot register_snapshot,
|
||||
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,
|
||||
Register char_code,
|
||||
Register scratch) {
|
||||
|
@ -297,37 +297,6 @@ void CheckMaps::GenerateCode(MaglevAssembler* masm,
|
||||
__ 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() {
|
||||
UseRegister(receiver_input());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user