[turbofan] Implement constant folding of string concatenations
This CL implements the following design doc: https://docs.google.com/document/d/1h5kdfemMQMpUd15PSKW1lqikJW5hsGwrmOvoqhGFRts/edit?ts=5b978756#heading=h.urs7r34mx9p Bug: v8:7790 Change-Id: I5f758c6d906ea9275c30b28f339063c64a2dc8d8 Reviewed-on: https://chromium-review.googlesource.com/1221807 Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Commit-Queue: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#56021}
This commit is contained in:
parent
e611e1cdea
commit
fef047a4a5
2
BUILD.gn
2
BUILD.gn
@ -2478,6 +2478,8 @@ v8_source_set("v8_base") {
|
||||
"src/string-builder.cc",
|
||||
"src/string-case.cc",
|
||||
"src/string-case.h",
|
||||
"src/string-constants.cc",
|
||||
"src/string-constants.h",
|
||||
"src/string-hasher-inl.h",
|
||||
"src/string-hasher.h",
|
||||
"src/string-search.h",
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "src/deoptimizer.h"
|
||||
#include "src/macro-assembler.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/string-constants.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -417,6 +418,13 @@ Operand Operand::EmbeddedCode(CodeStub* stub) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Operand Operand::EmbeddedStringConstant(const StringConstantBase* str) {
|
||||
Operand result(0, RelocInfo::EMBEDDED_OBJECT);
|
||||
result.is_heap_object_request_ = true;
|
||||
result.value_.heap_object_request = HeapObjectRequest(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
MemOperand::MemOperand(Register rn, int32_t offset, AddrMode am)
|
||||
: rn_(rn), rm_(no_reg), offset_(offset), am_(am) {
|
||||
// Accesses below the stack pointer are not safe, and are prohibited by the
|
||||
@ -484,6 +492,12 @@ void Assembler::AllocateAndInstallRequestedHeapObjects(Isolate* isolate) {
|
||||
request.code_stub()->set_isolate(isolate);
|
||||
object = request.code_stub()->GetCode();
|
||||
break;
|
||||
case HeapObjectRequest::kStringConstant: {
|
||||
const StringConstantBase* str = request.string();
|
||||
CHECK_NOT_NULL(str);
|
||||
object = str->AllocateStringConstant(isolate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Address pc = reinterpret_cast<Address>(buffer_) + request.offset();
|
||||
Memory<Address>(constant_pool_entry_address(pc, 0 /* unused */)) =
|
||||
|
@ -425,6 +425,7 @@ class Operand {
|
||||
|
||||
static Operand EmbeddedNumber(double number); // Smi or HeapNumber.
|
||||
static Operand EmbeddedCode(CodeStub* stub);
|
||||
static Operand EmbeddedStringConstant(const StringConstantBase* str);
|
||||
|
||||
// Return true if this is a register operand.
|
||||
bool IsRegister() const {
|
||||
|
@ -341,7 +341,9 @@ Immediate Operand::immediate_for_heap_object_request() const {
|
||||
DCHECK((heap_object_request().kind() == HeapObjectRequest::kHeapNumber &&
|
||||
immediate_.rmode() == RelocInfo::EMBEDDED_OBJECT) ||
|
||||
(heap_object_request().kind() == HeapObjectRequest::kCodeStub &&
|
||||
immediate_.rmode() == RelocInfo::CODE_TARGET));
|
||||
immediate_.rmode() == RelocInfo::CODE_TARGET) ||
|
||||
(heap_object_request().kind() == HeapObjectRequest::kStringConstant &&
|
||||
immediate_.rmode() == RelocInfo::EMBEDDED_OBJECT));
|
||||
return immediate_;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "src/code-stubs.h"
|
||||
#include "src/frame-constants.h"
|
||||
#include "src/register-configuration.h"
|
||||
#include "src/string-constants.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -602,6 +603,13 @@ void Assembler::AllocateAndInstallRequestedHeapObjects(Isolate* isolate) {
|
||||
request.code_stub()->GetCode());
|
||||
break;
|
||||
}
|
||||
case HeapObjectRequest::kStringConstant: {
|
||||
const StringConstantBase* str = request.string();
|
||||
CHECK_NOT_NULL(str);
|
||||
set_target_address_at(pc, 0 /* unused */,
|
||||
str->AllocateStringConstant(isolate).address());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1718,6 +1726,13 @@ Operand Operand::EmbeddedCode(CodeStub* stub) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Operand Operand::EmbeddedStringConstant(const StringConstantBase* str) {
|
||||
Operand result(0, RelocInfo::EMBEDDED_OBJECT);
|
||||
result.heap_object_request_.emplace(str);
|
||||
DCHECK(result.IsHeapObjectRequest());
|
||||
return result;
|
||||
}
|
||||
|
||||
void Assembler::ldr(const CPURegister& rt, const Operand& operand) {
|
||||
if (operand.IsHeapObjectRequest()) {
|
||||
RequestHeapObject(operand.heap_object_request());
|
||||
|
@ -718,6 +718,7 @@ class Operand {
|
||||
|
||||
static Operand EmbeddedNumber(double number); // Smi or HeapNumber.
|
||||
static Operand EmbeddedCode(CodeStub* stub);
|
||||
static Operand EmbeddedStringConstant(const StringConstantBase* str);
|
||||
|
||||
inline bool IsHeapObjectRequest() const;
|
||||
inline HeapObjectRequest heap_object_request() const;
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "src/simulator.h" // For flushing instruction cache.
|
||||
#include "src/snapshot/serializer-common.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
#include "src/string-constants.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -371,6 +372,13 @@ HeapObjectRequest::HeapObjectRequest(CodeStub* code_stub, int offset)
|
||||
DCHECK_NOT_NULL(value_.code_stub);
|
||||
}
|
||||
|
||||
HeapObjectRequest::HeapObjectRequest(const StringConstantBase* string,
|
||||
int offset)
|
||||
: kind_(kStringConstant), offset_(offset) {
|
||||
value_.string = string;
|
||||
DCHECK_NOT_NULL(value_.string);
|
||||
}
|
||||
|
||||
// Platform specific but identical code for all the platforms.
|
||||
|
||||
void Assembler::RecordDeoptReason(DeoptimizeReason reason,
|
||||
|
@ -67,6 +67,7 @@ class Isolate;
|
||||
class SCTableReference;
|
||||
class SourcePosition;
|
||||
class StatsCounter;
|
||||
class StringConstantBase;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Optimization for far-jmp like instructions that can be replaced by shorter.
|
||||
@ -97,8 +98,9 @@ class HeapObjectRequest {
|
||||
public:
|
||||
explicit HeapObjectRequest(double heap_number, int offset = -1);
|
||||
explicit HeapObjectRequest(CodeStub* code_stub, int offset = -1);
|
||||
explicit HeapObjectRequest(const StringConstantBase* string, int offset = -1);
|
||||
|
||||
enum Kind { kHeapNumber, kCodeStub };
|
||||
enum Kind { kHeapNumber, kCodeStub, kStringConstant };
|
||||
Kind kind() const { return kind_; }
|
||||
|
||||
double heap_number() const {
|
||||
@ -111,6 +113,11 @@ class HeapObjectRequest {
|
||||
return value_.code_stub;
|
||||
}
|
||||
|
||||
const StringConstantBase* string() const {
|
||||
DCHECK_EQ(kind(), kStringConstant);
|
||||
return value_.string;
|
||||
}
|
||||
|
||||
// The code buffer offset at the time of the request.
|
||||
int offset() const {
|
||||
DCHECK_GE(offset_, 0);
|
||||
@ -128,6 +135,7 @@ class HeapObjectRequest {
|
||||
union {
|
||||
double heap_number;
|
||||
CodeStub* code_stub;
|
||||
const StringConstantBase* string;
|
||||
} value_;
|
||||
|
||||
int offset_;
|
||||
@ -274,11 +282,11 @@ class V8_EXPORT_PRIVATE AssemblerBase : public Malloced {
|
||||
}
|
||||
}
|
||||
|
||||
// {RequestHeapObject} records the need for a future heap number allocation or
|
||||
// code stub generation. After code assembly, each platform's
|
||||
// {Assembler::AllocateAndInstallRequestedHeapObjects} will allocate these
|
||||
// objects and place them where they are expected (determined by the pc offset
|
||||
// associated with each request).
|
||||
// {RequestHeapObject} records the need for a future heap number allocation,
|
||||
// code stub generation or string allocation. After code assembly, each
|
||||
// platform's {Assembler::AllocateAndInstallRequestedHeapObjects} will
|
||||
// allocate these objects and place them where they are expected (determined
|
||||
// by the pc offset associated with each request).
|
||||
void RequestHeapObject(HeapObjectRequest request);
|
||||
|
||||
private:
|
||||
|
@ -125,6 +125,9 @@ class ArmOperandConverter final : public InstructionOperandConverter {
|
||||
return Operand::EmbeddedNumber(constant.ToFloat64().value());
|
||||
case Constant::kExternalReference:
|
||||
return Operand(constant.ToExternalReference());
|
||||
case Constant::kDelayedStringConstant:
|
||||
return Operand::EmbeddedStringConstant(
|
||||
constant.ToDelayedStringConstant());
|
||||
case Constant::kInt64:
|
||||
case Constant::kHeapObject:
|
||||
// TODO(dcarney): loading RPO constants on arm.
|
||||
|
@ -225,6 +225,9 @@ class Arm64OperandConverter final : public InstructionOperandConverter {
|
||||
return Operand(constant.ToExternalReference());
|
||||
case Constant::kHeapObject:
|
||||
return Operand(constant.ToHeapObject());
|
||||
case Constant::kDelayedStringConstant:
|
||||
return Operand::EmbeddedStringConstant(
|
||||
constant.ToDelayedStringConstant());
|
||||
case Constant::kRpoNumber:
|
||||
UNREACHABLE(); // TODO(dcarney): RPO immediates on arm64.
|
||||
break;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "src/lsan.h"
|
||||
#include "src/macro-assembler-inl.h"
|
||||
#include "src/optimized-compilation-info.h"
|
||||
#include "src/string-constants.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -1213,6 +1214,10 @@ void CodeGenerator::AddTranslationForOperand(Translation* translation,
|
||||
DCHECK_EQ(MachineRepresentation::kTagged, type.representation());
|
||||
literal = DeoptimizationLiteral(constant.ToHeapObject());
|
||||
break;
|
||||
case Constant::kDelayedStringConstant:
|
||||
DCHECK_EQ(MachineRepresentation::kTagged, type.representation());
|
||||
literal = DeoptimizationLiteral(constant.ToDelayedStringConstant());
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -1271,7 +1276,18 @@ OutOfLineCode::OutOfLineCode(CodeGenerator* gen)
|
||||
OutOfLineCode::~OutOfLineCode() = default;
|
||||
|
||||
Handle<Object> DeoptimizationLiteral::Reify(Isolate* isolate) const {
|
||||
return object_.is_null() ? isolate->factory()->NewNumber(number_) : object_;
|
||||
switch (kind_) {
|
||||
case DeoptimizationLiteralKind::kObject: {
|
||||
return object_;
|
||||
}
|
||||
case DeoptimizationLiteralKind::kNumber: {
|
||||
return isolate->factory()->NewNumber(number_);
|
||||
}
|
||||
case DeoptimizationLiteralKind::kString: {
|
||||
return string_->AllocateStringConstant(isolate);
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
|
@ -50,28 +50,40 @@ class InstructionOperandIterator {
|
||||
size_t pos_;
|
||||
};
|
||||
|
||||
// Either a non-null Handle<Object> or a double.
|
||||
enum class DeoptimizationLiteralKind { kObject, kNumber, kString };
|
||||
|
||||
// Either a non-null Handle<Object>, a double or a StringConstantBase.
|
||||
class DeoptimizationLiteral {
|
||||
public:
|
||||
DeoptimizationLiteral() : object_(), number_(0) {}
|
||||
DeoptimizationLiteral() : object_(), number_(0), string_(nullptr) {}
|
||||
explicit DeoptimizationLiteral(Handle<Object> object)
|
||||
: object_(object), number_(0) {
|
||||
: kind_(DeoptimizationLiteralKind::kObject), object_(object) {
|
||||
DCHECK(!object_.is_null());
|
||||
}
|
||||
explicit DeoptimizationLiteral(double number) : object_(), number_(number) {}
|
||||
explicit DeoptimizationLiteral(double number)
|
||||
: kind_(DeoptimizationLiteralKind::kNumber), number_(number) {}
|
||||
explicit DeoptimizationLiteral(const StringConstantBase* string)
|
||||
: kind_(DeoptimizationLiteralKind::kString), string_(string) {}
|
||||
|
||||
Handle<Object> object() const { return object_; }
|
||||
const StringConstantBase* string() const { return string_; }
|
||||
|
||||
bool operator==(const DeoptimizationLiteral& other) const {
|
||||
return object_.equals(other.object_) &&
|
||||
bit_cast<uint64_t>(number_) == bit_cast<uint64_t>(other.number_);
|
||||
return kind_ == other.kind_ && object_.equals(other.object_) &&
|
||||
bit_cast<uint64_t>(number_) == bit_cast<uint64_t>(other.number_) &&
|
||||
bit_cast<intptr_t>(string_) == bit_cast<intptr_t>(other.string_);
|
||||
}
|
||||
|
||||
Handle<Object> Reify(Isolate* isolate) const;
|
||||
|
||||
DeoptimizationLiteralKind kind() const { return kind_; }
|
||||
|
||||
private:
|
||||
DeoptimizationLiteralKind kind_;
|
||||
|
||||
Handle<Object> object_;
|
||||
double number_;
|
||||
double number_ = 0;
|
||||
const StringConstantBase* string_ = nullptr;
|
||||
};
|
||||
|
||||
// Generates native code for a sequence of instructions.
|
||||
|
@ -136,6 +136,13 @@ const Operator* CommonOperatorBuilder::MarkAsSafetyCheck(
|
||||
}
|
||||
}
|
||||
|
||||
const Operator* CommonOperatorBuilder::DelayedStringConstant(
|
||||
const StringConstantBase* str) {
|
||||
return new (zone()) Operator1<const StringConstantBase*>(
|
||||
IrOpcode::kDelayedStringConstant, Operator::kPure,
|
||||
"DelayedStringConstant", 0, 0, 0, 1, 0, 0, str);
|
||||
}
|
||||
|
||||
bool operator==(SelectParameters const& lhs, SelectParameters const& rhs) {
|
||||
return lhs.representation() == rhs.representation() &&
|
||||
lhs.hint() == rhs.hint();
|
||||
@ -1194,6 +1201,11 @@ Handle<HeapObject> HeapConstantOf(const Operator* op) {
|
||||
return OpParameter<Handle<HeapObject>>(op);
|
||||
}
|
||||
|
||||
const StringConstantBase* StringConstantBaseOf(const Operator* op) {
|
||||
DCHECK_EQ(IrOpcode::kDelayedStringConstant, op->opcode());
|
||||
return OpParameter<const StringConstantBase*>(op);
|
||||
}
|
||||
|
||||
const Operator* CommonOperatorBuilder::RelocatableInt32Constant(
|
||||
int32_t value, RelocInfo::Mode rmode) {
|
||||
return new (zone()) Operator1<RelocatablePtrConstantInfo>( // --
|
||||
|
@ -11,12 +11,16 @@
|
||||
#include "src/globals.h"
|
||||
#include "src/machine-type.h"
|
||||
#include "src/reloc-info.h"
|
||||
#include "src/string-constants.h"
|
||||
#include "src/vector-slot-pair.h"
|
||||
#include "src/zone/zone-containers.h"
|
||||
#include "src/zone/zone-handle-set.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class StringConstantBase;
|
||||
|
||||
namespace compiler {
|
||||
|
||||
// Forward declarations.
|
||||
@ -433,6 +437,9 @@ const FrameStateInfo& FrameStateInfoOf(const Operator* op)
|
||||
|
||||
Handle<HeapObject> HeapConstantOf(const Operator* op) V8_WARN_UNUSED_RESULT;
|
||||
|
||||
const StringConstantBase* StringConstantBaseOf(const Operator* op)
|
||||
V8_WARN_UNUSED_RESULT;
|
||||
|
||||
// Interface for building common operators that can be used at any level of IR,
|
||||
// including JavaScript, mid-level, and low-level.
|
||||
class V8_EXPORT_PRIVATE CommonOperatorBuilder final
|
||||
@ -535,6 +542,8 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
|
||||
const Operator* MarkAsSafetyCheck(const Operator* op,
|
||||
IsSafetyCheck safety_check);
|
||||
|
||||
const Operator* DelayedStringConstant(const StringConstantBase* str);
|
||||
|
||||
private:
|
||||
Zone* zone() const { return zone_; }
|
||||
|
||||
|
@ -80,6 +80,9 @@ class IA32OperandConverter : public InstructionOperandConverter {
|
||||
return Immediate(constant.ToExternalReference());
|
||||
case Constant::kHeapObject:
|
||||
return Immediate(constant.ToHeapObject());
|
||||
case Constant::kDelayedStringConstant:
|
||||
return Immediate::EmbeddedStringConstant(
|
||||
constant.ToDelayedStringConstant());
|
||||
case Constant::kInt64:
|
||||
break;
|
||||
case Constant::kRpoNumber:
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef V8_COMPILER_INSTRUCTION_SELECTOR_IMPL_H_
|
||||
#define V8_COMPILER_INSTRUCTION_SELECTOR_IMPL_H_
|
||||
|
||||
#include "src/compiler/common-operator.h"
|
||||
#include "src/compiler/instruction-selector.h"
|
||||
#include "src/compiler/instruction.h"
|
||||
#include "src/compiler/linkage.h"
|
||||
@ -319,6 +320,8 @@ class OperandGenerator {
|
||||
}
|
||||
case IrOpcode::kHeapConstant:
|
||||
return Constant(HeapConstantOf(node->op()));
|
||||
case IrOpcode::kDelayedStringConstant:
|
||||
return Constant(StringConstantBaseOf(node->op()));
|
||||
case IrOpcode::kDeadValue: {
|
||||
switch (DeadValueRepresentationOf(node->op())) {
|
||||
case MachineRepresentation::kBit:
|
||||
|
@ -456,6 +456,7 @@ InstructionOperand OperandForDeopt(Isolate* isolate, OperandGenerator* g,
|
||||
case IrOpcode::kNumberConstant:
|
||||
case IrOpcode::kFloat32Constant:
|
||||
case IrOpcode::kFloat64Constant:
|
||||
case IrOpcode::kDelayedStringConstant:
|
||||
return g->UseImmediate(input);
|
||||
case IrOpcode::kHeapConstant: {
|
||||
if (!CanBeTaggedPointer(rep)) {
|
||||
@ -1294,6 +1295,8 @@ void InstructionSelector::VisitNode(Node* node) {
|
||||
if (!IsSmiDouble(value)) MarkAsReference(node);
|
||||
return VisitConstant(node);
|
||||
}
|
||||
case IrOpcode::kDelayedStringConstant:
|
||||
return MarkAsReference(node), VisitConstant(node);
|
||||
case IrOpcode::kCall:
|
||||
return VisitCall(node);
|
||||
case IrOpcode::kCallWithCallerSavedRegisters:
|
||||
|
@ -593,6 +593,13 @@ Handle<Code> Constant::ToCode() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
const StringConstantBase* Constant::ToDelayedStringConstant() const {
|
||||
DCHECK_EQ(kDelayedStringConstant, type());
|
||||
const StringConstantBase* value =
|
||||
bit_cast<StringConstantBase*>(static_cast<intptr_t>(value_));
|
||||
return value;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Constant& constant) {
|
||||
switch (constant.type()) {
|
||||
case Constant::kInt32:
|
||||
@ -609,6 +616,9 @@ std::ostream& operator<<(std::ostream& os, const Constant& constant) {
|
||||
return os << Brief(*constant.ToHeapObject());
|
||||
case Constant::kRpoNumber:
|
||||
return os << "RPO" << constant.ToRpoNumber().ToInt();
|
||||
case Constant::kDelayedStringConstant:
|
||||
return os << "DelayedStringConstant: "
|
||||
<< constant.ToDelayedStringConstant();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -1039,7 +1039,8 @@ class V8_EXPORT_PRIVATE Constant final {
|
||||
kFloat64,
|
||||
kExternalReference,
|
||||
kHeapObject,
|
||||
kRpoNumber
|
||||
kRpoNumber,
|
||||
kDelayedStringConstant
|
||||
};
|
||||
|
||||
explicit Constant(int32_t v);
|
||||
@ -1051,6 +1052,8 @@ class V8_EXPORT_PRIVATE Constant final {
|
||||
explicit Constant(Handle<HeapObject> obj)
|
||||
: type_(kHeapObject), value_(bit_cast<intptr_t>(obj)) {}
|
||||
explicit Constant(RpoNumber rpo) : type_(kRpoNumber), value_(rpo.ToInt()) {}
|
||||
explicit Constant(const StringConstantBase* str)
|
||||
: type_(kDelayedStringConstant), value_(bit_cast<intptr_t>(str)) {}
|
||||
explicit Constant(RelocatablePtrConstantInfo info);
|
||||
|
||||
Type type() const { return type_; }
|
||||
@ -1100,6 +1103,7 @@ class V8_EXPORT_PRIVATE Constant final {
|
||||
|
||||
Handle<HeapObject> ToHeapObject() const;
|
||||
Handle<Code> ToCode() const;
|
||||
const StringConstantBase* ToDelayedStringConstant() const;
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
|
@ -17,12 +17,14 @@
|
||||
#include "src/compiler/node-matchers.h"
|
||||
#include "src/compiler/property-access-builder.h"
|
||||
#include "src/compiler/type-cache.h"
|
||||
#include "src/dtoa.h"
|
||||
#include "src/feedback-vector.h"
|
||||
#include "src/field-index-inl.h"
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/objects/js-array-buffer-inl.h"
|
||||
#include "src/objects/js-array-inl.h"
|
||||
#include "src/objects/templates.h"
|
||||
#include "src/string-constants.h"
|
||||
#include "src/vector-slot-pair.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -62,7 +64,7 @@ struct JSNativeContextSpecialization::ScriptContextTableLookupResult {
|
||||
JSNativeContextSpecialization::JSNativeContextSpecialization(
|
||||
Editor* editor, JSGraph* jsgraph, JSHeapBroker* js_heap_broker, Flags flags,
|
||||
Handle<Context> native_context, CompilationDependencies* dependencies,
|
||||
Zone* zone)
|
||||
Zone* zone, Zone* shared_zone)
|
||||
: AdvancedReducer(editor),
|
||||
jsgraph_(jsgraph),
|
||||
js_heap_broker_(js_heap_broker),
|
||||
@ -73,6 +75,7 @@ JSNativeContextSpecialization::JSNativeContextSpecialization(
|
||||
native_context_(js_heap_broker, native_context),
|
||||
dependencies_(dependencies),
|
||||
zone_(zone),
|
||||
shared_zone_(shared_zone),
|
||||
type_cache_(TypeCache::Get()) {}
|
||||
|
||||
Reduction JSNativeContextSpecialization::Reduce(Node* node) {
|
||||
@ -121,6 +124,29 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// static
|
||||
base::Optional<size_t> JSNativeContextSpecialization::GetMaxStringLength(
|
||||
Node* node) {
|
||||
if (node->opcode() == IrOpcode::kDelayedStringConstant) {
|
||||
return StringConstantBaseOf(node->op())->GetMaxStringConstantLength();
|
||||
}
|
||||
|
||||
HeapObjectMatcher matcher(node);
|
||||
if (matcher.HasValue() && matcher.Value()->IsString()) {
|
||||
Handle<String> input = Handle<String>::cast(matcher.Value());
|
||||
return input->length();
|
||||
}
|
||||
|
||||
NumberMatcher number_matcher(node);
|
||||
if (number_matcher.HasValue()) {
|
||||
return kBase10MaximalLength + 1;
|
||||
}
|
||||
|
||||
// We don't support objects with possibly monkey-patched prototype.toString
|
||||
// as it might have side-effects, so we shouldn't attempt lowering them.
|
||||
return base::nullopt;
|
||||
}
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceJSToString(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kJSToString, node->opcode());
|
||||
Node* const input = node->InputAt(0);
|
||||
@ -139,8 +165,10 @@ Reduction JSNativeContextSpecialization::ReduceJSToString(Node* node) {
|
||||
// regressions and the stronger optimization should be re-implemented.
|
||||
NumberMatcher number_matcher(input);
|
||||
if (number_matcher.HasValue()) {
|
||||
reduction = Replace(jsgraph()->HeapConstant(factory()->NumberToString(
|
||||
factory()->NewNumber(number_matcher.Value()))));
|
||||
const StringConstantBase* base =
|
||||
new (shared_zone()) NumberToStringConstant(number_matcher.Value());
|
||||
reduction =
|
||||
Replace(graph()->NewNode(common()->DelayedStringConstant(base)));
|
||||
ReplaceWithValue(node, reduction.replacement());
|
||||
return reduction;
|
||||
}
|
||||
@ -148,6 +176,35 @@ Reduction JSNativeContextSpecialization::ReduceJSToString(Node* node) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
const StringConstantBase*
|
||||
JSNativeContextSpecialization::CreateDelayedStringConstant(Node* node) {
|
||||
if (node->opcode() == IrOpcode::kDelayedStringConstant) {
|
||||
return StringConstantBaseOf(node->op());
|
||||
} else {
|
||||
NumberMatcher number_matcher(node);
|
||||
if (number_matcher.HasValue()) {
|
||||
return new (shared_zone()) NumberToStringConstant(number_matcher.Value());
|
||||
} else {
|
||||
HeapObjectMatcher matcher(node);
|
||||
if (matcher.HasValue() && matcher.Value()->IsString()) {
|
||||
return new (shared_zone())
|
||||
StringLiteral(Handle<String>::cast(matcher.Value()));
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsStringConstant(Node* node) {
|
||||
if (node->opcode() == IrOpcode::kDelayedStringConstant) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HeapObjectMatcher matcher(node);
|
||||
return matcher.HasValue() && matcher.Value()->IsString();
|
||||
}
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceJSAdd(Node* node) {
|
||||
// TODO(turbofan): This has to run together with the inlining and
|
||||
// native context specialization to be able to leverage the string
|
||||
@ -155,20 +212,29 @@ Reduction JSNativeContextSpecialization::ReduceJSAdd(Node* node) {
|
||||
// nevertheless find a better home for this at some point.
|
||||
DCHECK_EQ(IrOpcode::kJSAdd, node->opcode());
|
||||
|
||||
// Constant-fold string concatenation.
|
||||
HeapObjectBinopMatcher m(node);
|
||||
if (m.left().HasValue() && m.left().Value()->IsString() &&
|
||||
m.right().HasValue() && m.right().Value()->IsString()) {
|
||||
Handle<String> left = Handle<String>::cast(m.left().Value());
|
||||
Handle<String> right = Handle<String>::cast(m.right().Value());
|
||||
if (left->length() + right->length() <= String::kMaxLength) {
|
||||
Handle<String> result =
|
||||
factory()->NewConsString(left, right).ToHandleChecked();
|
||||
Node* value = jsgraph()->HeapConstant(result);
|
||||
ReplaceWithValue(node, value);
|
||||
return Replace(value);
|
||||
}
|
||||
Node* const lhs = node->InputAt(0);
|
||||
Node* const rhs = node->InputAt(1);
|
||||
|
||||
base::Optional<size_t> lhs_len = GetMaxStringLength(lhs);
|
||||
base::Optional<size_t> rhs_len = GetMaxStringLength(rhs);
|
||||
if (!lhs_len || !rhs_len) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// Fold into DelayedStringConstant if at least one of the parameters is a
|
||||
// string constant and the addition won't throw due to too long result.
|
||||
if (*lhs_len + *rhs_len <= String::kMaxLength &&
|
||||
(IsStringConstant(lhs) || IsStringConstant(rhs))) {
|
||||
const StringConstantBase* left = CreateDelayedStringConstant(lhs);
|
||||
const StringConstantBase* right = CreateDelayedStringConstant(rhs);
|
||||
const StringConstantBase* cons =
|
||||
new (shared_zone()) StringCons(left, right);
|
||||
|
||||
Node* reduced = graph()->NewNode(common()->DelayedStringConstant(cons));
|
||||
ReplaceWithValue(node, reduced);
|
||||
return Replace(reduced);
|
||||
}
|
||||
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ namespace internal {
|
||||
// Forward declarations.
|
||||
class Factory;
|
||||
class FeedbackNexus;
|
||||
class StringConstantBase;
|
||||
|
||||
namespace compiler {
|
||||
|
||||
@ -36,7 +37,8 @@ class TypeCache;
|
||||
// folding some {LoadGlobal} nodes or strength reducing some {StoreGlobal}
|
||||
// nodes. And also specializes {LoadNamed} and {StoreNamed} nodes according
|
||||
// to type feedback (if available).
|
||||
class JSNativeContextSpecialization final : public AdvancedReducer {
|
||||
class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
|
||||
: public AdvancedReducer {
|
||||
public:
|
||||
// Flags that control the mode of operation.
|
||||
enum Flag {
|
||||
@ -50,7 +52,7 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
|
||||
JSHeapBroker* js_heap_broker, Flags flags,
|
||||
Handle<Context> native_context,
|
||||
CompilationDependencies* dependencies,
|
||||
Zone* zone);
|
||||
Zone* zone, Zone* shared_zone);
|
||||
|
||||
const char* reducer_name() const override {
|
||||
return "JSNativeContextSpecialization";
|
||||
@ -58,6 +60,11 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
|
||||
|
||||
Reduction Reduce(Node* node) final;
|
||||
|
||||
// Utility for folding string constant concatenation.
|
||||
// Supports JSAdd nodes and nodes typed as string or number.
|
||||
// Public for the sake of unit testing.
|
||||
static base::Optional<size_t> GetMaxStringLength(Node* node);
|
||||
|
||||
private:
|
||||
Reduction ReduceJSAdd(Node* node);
|
||||
Reduction ReduceJSGetSuperConstructor(Node* node);
|
||||
@ -103,6 +110,8 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
|
||||
Reduction ReduceSoftDeoptimize(Node* node, DeoptimizeReason reason);
|
||||
Reduction ReduceJSToString(Node* node);
|
||||
|
||||
const StringConstantBase* CreateDelayedStringConstant(Node* node);
|
||||
|
||||
// A triple of nodes that represents a continuation.
|
||||
class ValueEffectControl final {
|
||||
public:
|
||||
@ -231,6 +240,7 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
|
||||
const NativeContextRef& native_context() const { return native_context_; }
|
||||
CompilationDependencies* dependencies() const { return dependencies_; }
|
||||
Zone* zone() const { return zone_; }
|
||||
Zone* shared_zone() const { return shared_zone_; }
|
||||
|
||||
JSGraph* const jsgraph_;
|
||||
JSHeapBroker* const js_heap_broker_;
|
||||
@ -240,6 +250,7 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
|
||||
NativeContextRef native_context_;
|
||||
CompilationDependencies* const dependencies_;
|
||||
Zone* const zone_;
|
||||
Zone* const shared_zone_;
|
||||
TypeCache const& type_cache_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(JSNativeContextSpecialization);
|
||||
|
@ -168,6 +168,7 @@ class MachineRepresentationInferrer {
|
||||
break;
|
||||
case IrOpcode::kHeapConstant:
|
||||
case IrOpcode::kNumberConstant:
|
||||
case IrOpcode::kDelayedStringConstant:
|
||||
case IrOpcode::kChangeBitToTagged:
|
||||
case IrOpcode::kIfException:
|
||||
case IrOpcode::kOsrValue:
|
||||
|
@ -430,6 +430,7 @@
|
||||
V(NewSmiOrObjectElements) \
|
||||
V(NewArgumentsElements) \
|
||||
V(NewConsString) \
|
||||
V(DelayedStringConstant) \
|
||||
V(EnsureWritableFastElements) \
|
||||
V(MaybeGrowFastElements) \
|
||||
V(TransitionElementsKind) \
|
||||
|
@ -1185,6 +1185,7 @@ struct InliningPhase {
|
||||
|
||||
void Run(PipelineData* data, Zone* temp_zone) {
|
||||
Isolate* isolate = data->isolate();
|
||||
OptimizedCompilationInfo* info = data->info();
|
||||
GraphReducer graph_reducer(temp_zone, data->graph(),
|
||||
data->jsgraph()->Dead());
|
||||
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
|
||||
@ -1213,9 +1214,12 @@ struct InliningPhase {
|
||||
if (data->info()->is_bailout_on_uninitialized()) {
|
||||
flags |= JSNativeContextSpecialization::kBailoutOnUninitialized;
|
||||
}
|
||||
// Passing the OptimizedCompilationInfo's shared zone here as
|
||||
// JSNativeContextSpecialization allocates out-of-heap objects
|
||||
// that need to live until code generation.
|
||||
JSNativeContextSpecialization native_context_specialization(
|
||||
&graph_reducer, data->jsgraph(), data->js_heap_broker(), flags,
|
||||
data->native_context(), data->dependencies(), temp_zone);
|
||||
data->native_context(), data->dependencies(), temp_zone, info->zone());
|
||||
JSInliningHeuristic inlining(
|
||||
&graph_reducer, data->info()->is_inlining_enabled()
|
||||
? JSInliningHeuristic::kGeneralInlining
|
||||
|
@ -350,6 +350,7 @@ Node* RepresentationChanger::GetTaggedPointerRepresentationFor(
|
||||
// Eagerly fold representation changes for constants.
|
||||
switch (node->opcode()) {
|
||||
case IrOpcode::kHeapConstant:
|
||||
case IrOpcode::kDelayedStringConstant:
|
||||
return node; // No change necessary.
|
||||
case IrOpcode::kInt32Constant:
|
||||
case IrOpcode::kFloat64Constant:
|
||||
@ -433,6 +434,7 @@ Node* RepresentationChanger::GetTaggedRepresentationFor(
|
||||
switch (node->opcode()) {
|
||||
case IrOpcode::kNumberConstant:
|
||||
case IrOpcode::kHeapConstant:
|
||||
case IrOpcode::kDelayedStringConstant:
|
||||
return node; // No change necessary.
|
||||
case IrOpcode::kInt32Constant:
|
||||
case IrOpcode::kFloat64Constant:
|
||||
|
@ -1561,6 +1561,7 @@ class RepresentationSelector {
|
||||
return;
|
||||
}
|
||||
case IrOpcode::kHeapConstant:
|
||||
case IrOpcode::kDelayedStringConstant:
|
||||
return VisitLeaf(node, MachineRepresentation::kTaggedPointer);
|
||||
case IrOpcode::kPointerConstant: {
|
||||
VisitLeaf(node, MachineType::PointerRepresentation());
|
||||
|
@ -2192,6 +2192,10 @@ Type Typer::Visitor::TypeNewArgumentsElements(Node* node) {
|
||||
|
||||
Type Typer::Visitor::TypeNewConsString(Node* node) { return Type::String(); }
|
||||
|
||||
Type Typer::Visitor::TypeDelayedStringConstant(Node* node) {
|
||||
return Type::String();
|
||||
}
|
||||
|
||||
Type Typer::Visitor::TypeFindOrderedHashMapEntry(Node* node) {
|
||||
return Type::Range(-1.0, FixedArray::kMaxLength, zone());
|
||||
}
|
||||
|
@ -1252,6 +1252,9 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
||||
CheckValueInputIs(node, 2, Type::String());
|
||||
CheckTypeIs(node, Type::String());
|
||||
break;
|
||||
case IrOpcode::kDelayedStringConstant:
|
||||
CheckTypeIs(node, Type::String());
|
||||
break;
|
||||
case IrOpcode::kAllocate:
|
||||
CheckValueInputIs(node, 0, Type::PlainNumber());
|
||||
break;
|
||||
|
@ -3281,6 +3281,11 @@ void CodeGenerator::AssembleMove(InstructionOperand* source,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Constant::kDelayedStringConstant: {
|
||||
const StringConstantBase* src_constant = src.ToDelayedStringConstant();
|
||||
__ MoveStringConstant(dst, src_constant);
|
||||
break;
|
||||
}
|
||||
case Constant::kRpoNumber:
|
||||
UNREACHABLE(); // TODO(dcarney): load of labels on x64.
|
||||
break;
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "src/deoptimizer.h"
|
||||
#include "src/disassembler.h"
|
||||
#include "src/macro-assembler.h"
|
||||
#include "src/string-constants.h"
|
||||
#include "src/v8.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -76,6 +77,13 @@ Immediate Immediate::EmbeddedCode(CodeStub* stub) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Immediate Immediate::EmbeddedStringConstant(const StringConstantBase* str) {
|
||||
Immediate result(0, RelocInfo::EMBEDDED_OBJECT);
|
||||
result.is_heap_object_request_ = true;
|
||||
result.value_.heap_object_request = HeapObjectRequest(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation of CpuFeatures
|
||||
|
||||
@ -312,6 +320,12 @@ void Assembler::AllocateAndInstallRequestedHeapObjects(Isolate* isolate) {
|
||||
request.code_stub()->set_isolate(isolate);
|
||||
object = request.code_stub()->GetCode();
|
||||
break;
|
||||
case HeapObjectRequest::kStringConstant: {
|
||||
const StringConstantBase* str = request.string();
|
||||
CHECK_NOT_NULL(str);
|
||||
object = str->AllocateStringConstant(isolate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Address pc = reinterpret_cast<Address>(buffer_) + request.offset();
|
||||
Memory<Handle<Object>>(pc) = object;
|
||||
|
@ -224,6 +224,7 @@ class Immediate {
|
||||
|
||||
static Immediate EmbeddedNumber(double number); // Smi or HeapNumber.
|
||||
static Immediate EmbeddedCode(CodeStub* code);
|
||||
static Immediate EmbeddedStringConstant(const StringConstantBase* str);
|
||||
|
||||
static Immediate CodeRelativeOffset(Label* label) {
|
||||
return Immediate(label);
|
||||
|
188
src/string-constants.cc
Normal file
188
src/string-constants.cc
Normal file
@ -0,0 +1,188 @@
|
||||
// Copyright 2018 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/string-constants.h"
|
||||
|
||||
#include "src/base/functional.h"
|
||||
#include "src/dtoa.h"
|
||||
#include "src/objects.h"
|
||||
#include "src/objects/string-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
Handle<String> StringConstantBase::AllocateStringConstant(
|
||||
Isolate* isolate) const {
|
||||
if (!flattened_.is_null()) {
|
||||
return flattened_;
|
||||
}
|
||||
|
||||
Handle<String> result;
|
||||
switch (kind()) {
|
||||
case StringConstantKind::kStringLiteral: {
|
||||
result = static_cast<const StringLiteral*>(this)->str();
|
||||
break;
|
||||
}
|
||||
case StringConstantKind::kNumberToStringConstant: {
|
||||
auto num_constant = static_cast<const NumberToStringConstant*>(this);
|
||||
Handle<Object> num_obj =
|
||||
isolate->factory()->NewNumber(num_constant->num());
|
||||
result = isolate->factory()->NumberToString(num_obj);
|
||||
break;
|
||||
}
|
||||
case StringConstantKind::kStringCons: {
|
||||
Handle<String> lhs =
|
||||
static_cast<const StringCons*>(this)->lhs()->AllocateStringConstant(
|
||||
isolate);
|
||||
Handle<String> rhs =
|
||||
static_cast<const StringCons*>(this)->rhs()->AllocateStringConstant(
|
||||
isolate);
|
||||
result = isolate->factory()->NewConsString(lhs, rhs).ToHandleChecked();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(mslekova): Normally we'd want to flatten the string here
|
||||
// but that results in OOM for too long strings.
|
||||
Memoize(result);
|
||||
return flattened_;
|
||||
}
|
||||
|
||||
bool StringConstantBase::operator==(const StringConstantBase& other) const {
|
||||
if (kind() != other.kind()) return false;
|
||||
|
||||
switch (kind()) {
|
||||
case StringConstantKind::kStringLiteral: {
|
||||
return static_cast<const StringLiteral*>(this) ==
|
||||
static_cast<const StringLiteral*>(&other);
|
||||
}
|
||||
case StringConstantKind::kNumberToStringConstant: {
|
||||
return static_cast<const NumberToStringConstant*>(this) ==
|
||||
static_cast<const NumberToStringConstant*>(&other);
|
||||
}
|
||||
case StringConstantKind::kStringCons: {
|
||||
return static_cast<const StringCons*>(this) ==
|
||||
static_cast<const StringCons*>(&other);
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
size_t hash_value(StringConstantBase const& base) {
|
||||
switch (base.kind()) {
|
||||
case StringConstantKind::kStringLiteral: {
|
||||
return hash_value(*static_cast<const StringLiteral*>(&base));
|
||||
}
|
||||
case StringConstantKind::kNumberToStringConstant: {
|
||||
return hash_value(*static_cast<const NumberToStringConstant*>(&base));
|
||||
}
|
||||
case StringConstantKind::kStringCons: {
|
||||
return hash_value(*static_cast<const StringCons*>(&base));
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool operator==(StringLiteral const& lhs, StringLiteral const& rhs) {
|
||||
return lhs.str().address() == rhs.str().address();
|
||||
}
|
||||
|
||||
bool operator!=(StringLiteral const& lhs, StringLiteral const& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
size_t hash_value(StringLiteral const& p) {
|
||||
return base::hash_combine(p.str().address());
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, StringLiteral const& p) {
|
||||
return os << Brief(*p.str());
|
||||
}
|
||||
|
||||
bool operator==(NumberToStringConstant const& lhs,
|
||||
NumberToStringConstant const& rhs) {
|
||||
return lhs.num() == rhs.num();
|
||||
}
|
||||
|
||||
bool operator!=(NumberToStringConstant const& lhs,
|
||||
NumberToStringConstant const& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
size_t hash_value(NumberToStringConstant const& p) {
|
||||
return base::hash_combine(p.num());
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, NumberToStringConstant const& p) {
|
||||
return os << p.num();
|
||||
}
|
||||
|
||||
bool operator==(StringCons const& lhs, StringCons const& rhs) {
|
||||
// TODO(mslekova): Think if we can express this in a more readable manner
|
||||
return *(lhs.lhs()) == *(rhs.lhs()) && *(lhs.rhs()) == *(rhs.rhs());
|
||||
}
|
||||
|
||||
bool operator!=(StringCons const& lhs, StringCons const& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
size_t hash_value(StringCons const& p) {
|
||||
return base::hash_combine(*(p.lhs()), *(p.rhs()));
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const StringConstantBase* base) {
|
||||
os << "DelayedStringConstant: ";
|
||||
switch (base->kind()) {
|
||||
case StringConstantKind::kStringLiteral: {
|
||||
os << *static_cast<const StringLiteral*>(base);
|
||||
break;
|
||||
}
|
||||
case StringConstantKind::kNumberToStringConstant: {
|
||||
os << *static_cast<const NumberToStringConstant*>(base);
|
||||
break;
|
||||
}
|
||||
case StringConstantKind::kStringCons: {
|
||||
os << *static_cast<const StringCons*>(base);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, StringCons const& p) {
|
||||
return os << p.lhs() << ", " << p.rhs();
|
||||
}
|
||||
|
||||
size_t StringConstantBase::GetMaxStringConstantLength() const {
|
||||
switch (kind()) {
|
||||
case StringConstantKind::kStringLiteral: {
|
||||
return static_cast<const StringLiteral*>(this)
|
||||
->GetMaxStringConstantLength();
|
||||
}
|
||||
case StringConstantKind::kNumberToStringConstant: {
|
||||
return static_cast<const NumberToStringConstant*>(this)
|
||||
->GetMaxStringConstantLength();
|
||||
}
|
||||
case StringConstantKind::kStringCons: {
|
||||
return static_cast<const StringCons*>(this)->GetMaxStringConstantLength();
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
size_t StringLiteral::GetMaxStringConstantLength() const {
|
||||
return str()->length();
|
||||
}
|
||||
|
||||
size_t NumberToStringConstant::GetMaxStringConstantLength() const {
|
||||
return kBase10MaximalLength + 1;
|
||||
}
|
||||
|
||||
size_t StringCons::GetMaxStringConstantLength() const {
|
||||
return lhs()->GetMaxStringConstantLength() +
|
||||
rhs()->GetMaxStringConstantLength();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
112
src/string-constants.h
Normal file
112
src/string-constants.h
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
#ifndef V8_STRING_CONSTANTS_H_
|
||||
#define V8_STRING_CONSTANTS_H_
|
||||
|
||||
#include "src/objects/string.h"
|
||||
#include "src/zone/zone.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
enum class StringConstantKind {
|
||||
kStringLiteral,
|
||||
kNumberToStringConstant,
|
||||
kStringCons
|
||||
};
|
||||
|
||||
class StringConstantBase : public ZoneObject {
|
||||
public:
|
||||
explicit StringConstantBase(StringConstantKind kind) : kind_(kind) {}
|
||||
|
||||
StringConstantKind kind() const { return kind_; }
|
||||
Handle<String> AllocateStringConstant(Isolate* isolate) const;
|
||||
|
||||
size_t GetMaxStringConstantLength() const;
|
||||
|
||||
bool operator==(const StringConstantBase& other) const;
|
||||
|
||||
private:
|
||||
void Memoize(Handle<String> flattened) const { flattened_ = flattened; }
|
||||
|
||||
StringConstantKind kind_;
|
||||
mutable Handle<String> flattened_ = Handle<String>::null();
|
||||
};
|
||||
|
||||
size_t hash_value(StringConstantBase const& base);
|
||||
|
||||
class StringLiteral final : public StringConstantBase {
|
||||
public:
|
||||
explicit StringLiteral(Handle<String> str)
|
||||
: StringConstantBase(StringConstantKind::kStringLiteral), str_(str) {}
|
||||
|
||||
Handle<String> str() const { return str_; }
|
||||
|
||||
size_t GetMaxStringConstantLength() const;
|
||||
|
||||
private:
|
||||
Handle<String> str_;
|
||||
};
|
||||
|
||||
bool operator==(StringLiteral const& lhs, StringLiteral const& rhs);
|
||||
bool operator!=(StringLiteral const& lhs, StringLiteral const& rhs);
|
||||
|
||||
size_t hash_value(StringLiteral const& parameters);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, StringLiteral const& parameters);
|
||||
|
||||
class NumberToStringConstant final : public StringConstantBase {
|
||||
public:
|
||||
explicit NumberToStringConstant(double num)
|
||||
: StringConstantBase(StringConstantKind::kNumberToStringConstant),
|
||||
num_(num) {}
|
||||
|
||||
double num() const { return num_; }
|
||||
|
||||
size_t GetMaxStringConstantLength() const;
|
||||
|
||||
private:
|
||||
double num_;
|
||||
};
|
||||
|
||||
bool operator==(NumberToStringConstant const& lhs,
|
||||
NumberToStringConstant const& rhs);
|
||||
bool operator!=(NumberToStringConstant const& lhs,
|
||||
NumberToStringConstant const& rhs);
|
||||
|
||||
size_t hash_value(NumberToStringConstant const& parameters);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
NumberToStringConstant const& parameters);
|
||||
|
||||
class StringCons final : public StringConstantBase {
|
||||
public:
|
||||
explicit StringCons(const StringConstantBase* lhs,
|
||||
const StringConstantBase* rhs)
|
||||
: StringConstantBase(StringConstantKind::kStringCons),
|
||||
lhs_(lhs),
|
||||
rhs_(rhs) {}
|
||||
|
||||
const StringConstantBase* lhs() const { return lhs_; }
|
||||
const StringConstantBase* rhs() const { return rhs_; }
|
||||
|
||||
size_t GetMaxStringConstantLength() const;
|
||||
|
||||
private:
|
||||
const StringConstantBase* lhs_;
|
||||
const StringConstantBase* rhs_;
|
||||
};
|
||||
|
||||
bool operator==(StringCons const& lhs, StringCons const& rhs);
|
||||
bool operator!=(StringCons const& lhs, StringCons const& rhs);
|
||||
|
||||
size_t hash_value(StringCons const& parameters);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, StringCons const& parameters);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_STRING_CONSTANTS_H_
|
@ -21,6 +21,7 @@
|
||||
#include "src/code-stubs.h"
|
||||
#include "src/deoptimizer.h"
|
||||
#include "src/macro-assembler.h"
|
||||
#include "src/string-constants.h"
|
||||
#include "src/v8.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -350,6 +351,13 @@ void Assembler::AllocateAndInstallRequestedHeapObjects(Isolate* isolate) {
|
||||
UpdateCodeTarget(Memory<int32_t>(pc), request.code_stub()->GetCode());
|
||||
break;
|
||||
}
|
||||
case HeapObjectRequest::kStringConstant: {
|
||||
const StringConstantBase* str = request.string();
|
||||
CHECK_NOT_NULL(str);
|
||||
Handle<String> allocated = str->AllocateStringConstant(isolate);
|
||||
Memory<Handle<Object>>(pc) = allocated;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1836,6 +1844,14 @@ void Assembler::movp_heap_number(Register dst, double value) {
|
||||
emitp(0, RelocInfo::EMBEDDED_OBJECT);
|
||||
}
|
||||
|
||||
void Assembler::movp_string(Register dst, const StringConstantBase* str) {
|
||||
EnsureSpace ensure_space(this);
|
||||
emit_rex(dst, kPointerSize);
|
||||
emit(0xB8 | dst.low_bits());
|
||||
RequestHeapObject(HeapObjectRequest(str));
|
||||
emitp(0, RelocInfo::EMBEDDED_OBJECT);
|
||||
}
|
||||
|
||||
void Assembler::movq(Register dst, int64_t value, RelocInfo::Mode rmode) {
|
||||
if (constpool_.TryRecordEntry(value, rmode)) {
|
||||
// Emit rip-relative move with offset = 0
|
||||
|
@ -689,6 +689,8 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
|
||||
// move.
|
||||
void movp_heap_number(Register dst, double value);
|
||||
|
||||
void movp_string(Register dst, const StringConstantBase* str);
|
||||
|
||||
// Loads a 64-bit immediate into a register.
|
||||
void movq(Register dst, int64_t value,
|
||||
RelocInfo::Mode rmode = RelocInfo::NONE);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/register-configuration.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
#include "src/string-constants.h"
|
||||
#include "src/x64/assembler-x64.h"
|
||||
|
||||
#include "src/x64/macro-assembler-x64.h" // Cannot be the first include.
|
||||
@ -1339,6 +1340,12 @@ void TurboAssembler::Move(Operand dst, Handle<HeapObject> object,
|
||||
movp(dst, kScratchRegister);
|
||||
}
|
||||
|
||||
void TurboAssembler::MoveStringConstant(Register result,
|
||||
const StringConstantBase* string,
|
||||
RelocInfo::Mode rmode) {
|
||||
movp_string(result, string);
|
||||
}
|
||||
|
||||
void MacroAssembler::Drop(int stack_elements) {
|
||||
if (stack_elements > 0) {
|
||||
addp(rsp, Immediate(stack_elements * kPointerSize));
|
||||
|
@ -50,6 +50,8 @@ constexpr Register kOffHeapTrampolineRegister = kScratchRegister;
|
||||
// Convenience for platform-independent signatures.
|
||||
typedef Operand MemOperand;
|
||||
|
||||
class StringConstantBase;
|
||||
|
||||
enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
|
||||
enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
|
||||
|
||||
@ -353,6 +355,9 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
|
||||
movp(dst, ptr, rmode);
|
||||
}
|
||||
|
||||
void MoveStringConstant(Register result, const StringConstantBase* string,
|
||||
RelocInfo::Mode rmode = RelocInfo::EMBEDDED_OBJECT);
|
||||
|
||||
// Convert smi to word-size sign-extended value.
|
||||
void SmiUntag(Register dst, Register src);
|
||||
void SmiUntag(Register dst, Operand src);
|
||||
|
@ -114,6 +114,7 @@ v8_source_set("unittests_sources") {
|
||||
"compiler/js-call-reducer-unittest.cc",
|
||||
"compiler/js-create-lowering-unittest.cc",
|
||||
"compiler/js-intrinsic-lowering-unittest.cc",
|
||||
"compiler/js-native-context-specialization-unittest.cc",
|
||||
"compiler/js-operator-unittest.cc",
|
||||
"compiler/js-typed-lowering-unittest.cc",
|
||||
"compiler/linkage-tail-call-unittest.cc",
|
||||
|
@ -0,0 +1,48 @@
|
||||
// Copyright 2014 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 "test/unittests/compiler/graph-unittest.h"
|
||||
|
||||
#include "src/compiler/js-native-context-specialization.h"
|
||||
#include "src/compiler/js-operator.h"
|
||||
#include "src/compiler/machine-operator.h"
|
||||
#include "src/compiler/simplified-operator.h"
|
||||
#include "src/dtoa.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
namespace js_native_context_specialization_unittest {
|
||||
|
||||
class JSNativeContextSpecializationTest : public GraphTest {
|
||||
public:
|
||||
explicit JSNativeContextSpecializationTest(int num_parameters = 1)
|
||||
: GraphTest(num_parameters), javascript_(zone()) {}
|
||||
~JSNativeContextSpecializationTest() override {}
|
||||
|
||||
protected:
|
||||
JSOperatorBuilder* javascript() { return &javascript_; }
|
||||
|
||||
private:
|
||||
JSOperatorBuilder javascript_;
|
||||
};
|
||||
|
||||
TEST_F(JSNativeContextSpecializationTest, GetMaxStringLengthOfString) {
|
||||
const size_t str_len = 3;
|
||||
const size_t num_len = kBase10MaximalLength + 1;
|
||||
|
||||
Node* const str_node = graph()->NewNode(
|
||||
common()->HeapConstant(factory()->InternalizeUtf8String("str")));
|
||||
EXPECT_EQ(JSNativeContextSpecialization::GetMaxStringLength(str_node),
|
||||
str_len);
|
||||
|
||||
Node* const num_node = graph()->NewNode(common()->NumberConstant(10.0 / 3));
|
||||
EXPECT_EQ(JSNativeContextSpecialization::GetMaxStringLength(num_node),
|
||||
num_len);
|
||||
}
|
||||
|
||||
} // namespace js_native_context_specialization_unittest
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
Loading…
Reference in New Issue
Block a user