[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:
Maya Lekova 2018-09-19 09:51:17 +02:00 committed by Commit Bot
parent e611e1cdea
commit fef047a4a5
39 changed files with 656 additions and 35 deletions

View File

@ -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",

View File

@ -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 */)) =

View File

@ -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 {

View File

@ -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_;
}

View File

@ -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());

View File

@ -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;

View File

@ -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,

View File

@ -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:

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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>( // --

View File

@ -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_; }

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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();
}

View File

@ -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_;

View File

@ -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();
}

View File

@ -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);

View File

@ -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:

View File

@ -430,6 +430,7 @@
V(NewSmiOrObjectElements) \
V(NewArgumentsElements) \
V(NewConsString) \
V(DelayedStringConstant) \
V(EnsureWritableFastElements) \
V(MaybeGrowFastElements) \
V(TransitionElementsKind) \

View File

@ -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

View File

@ -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:

View File

@ -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());

View File

@ -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());
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
View 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
View 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_

View File

@ -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

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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",

View File

@ -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