summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/effect-control-linearizer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/effect-control-linearizer.cc')
-rw-r--r--deps/v8/src/compiler/effect-control-linearizer.cc1703
1 files changed, 1516 insertions, 187 deletions
diff --git a/deps/v8/src/compiler/effect-control-linearizer.cc b/deps/v8/src/compiler/effect-control-linearizer.cc
index 66973abcbc..0b50f3e14f 100644
--- a/deps/v8/src/compiler/effect-control-linearizer.cc
+++ b/deps/v8/src/compiler/effect-control-linearizer.cc
@@ -4,11 +4,16 @@
#include "src/compiler/effect-control-linearizer.h"
+#include <cstdint>
+#include <type_traits>
+
#include "include/v8-fast-api-calls.h"
#include "src/base/bits.h"
+#include "src/base/small-vector.h"
#include "src/codegen/code-factory.h"
#include "src/codegen/interface-descriptors-inl.h"
#include "src/codegen/machine-type.h"
+#include "src/common/globals.h"
#include "src/common/ptr-compr-inl.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/compiler-source-position-table.h"
@@ -18,15 +23,21 @@
#include "src/compiler/js-graph.h"
#include "src/compiler/js-heap-broker.h"
#include "src/compiler/linkage.h"
+#include "src/compiler/machine-operator.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-origin-table.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/compiler/schedule.h"
+#include "src/compiler/string-builder-optimizer.h"
#include "src/heap/factory-inl.h"
#include "src/objects/heap-number.h"
+#include "src/objects/instance-type-inl.h"
+#include "src/objects/js-objects.h"
+#include "src/objects/object-list-macros.h"
#include "src/objects/oddball.h"
#include "src/objects/ordered-hash-table.h"
+#include "src/objects/string.h"
#include "src/objects/turbofan-types.h"
namespace v8 {
@@ -42,7 +53,8 @@ class EffectControlLinearizer {
SourcePositionTable* source_positions,
NodeOriginTable* node_origins,
MaintainSchedule maintain_schedule,
- JSHeapBroker* broker)
+ JSHeapBroker* broker,
+ StringBuilderOptimizer* string_builder_optimizer)
: js_graph_(js_graph),
schedule_(schedule),
temp_zone_(temp_zone),
@@ -51,6 +63,7 @@ class EffectControlLinearizer {
node_origins_(node_origins),
broker_(broker),
graph_assembler_(graph_assembler),
+ string_builder_optimizer_(string_builder_optimizer),
frame_state_zapper_(nullptr) {}
void Run();
@@ -83,7 +96,6 @@ class EffectControlLinearizer {
Node* LowerCheckReceiver(Node* node, Node* frame_state);
Node* LowerCheckReceiverOrNullOrUndefined(Node* node, Node* frame_state);
Node* LowerCheckString(Node* node, Node* frame_state);
- Node* LowerCheckBigInt(Node* node, Node* frame_state);
Node* LowerCheckedBigIntToBigInt64(Node* node, Node* frame_state);
Node* LowerCheckSymbol(Node* node, Node* frame_state);
void LowerCheckIf(Node* node, Node* frame_state);
@@ -118,6 +130,7 @@ class EffectControlLinearizer {
Node* LowerCheckedTaggedToFloat64(Node* node, Node* frame_state);
Node* LowerCheckedTaggedToTaggedSigned(Node* node, Node* frame_state);
Node* LowerCheckedTaggedToTaggedPointer(Node* node, Node* frame_state);
+ Node* LowerCheckBigInt(Node* node, Node* frame_state);
Node* LowerChangeInt64ToBigInt(Node* node);
Node* LowerChangeUint64ToBigInt(Node* node);
Node* LowerTruncateBigIntToWord64(Node* node);
@@ -185,6 +198,13 @@ class EffectControlLinearizer {
Node* LowerBigIntDivide(Node* node, Node* frame_state);
Node* LowerBigIntModulus(Node* node, Node* frame_state);
Node* LowerBigIntBitwiseAnd(Node* node, Node* frame_state);
+ Node* LowerBigIntBitwiseOr(Node* node);
+ Node* LowerBigIntBitwiseXor(Node* node, Node* frame_state);
+ Node* LowerBigIntShiftLeft(Node* node, Node* frame_state);
+ Node* LowerBigIntShiftRight(Node* node, Node* frame_state);
+ Node* LowerBigIntEqual(Node* node);
+ Node* LowerBigIntLessThan(Node* node);
+ Node* LowerBigIntLessThanOrEqual(Node* node);
Node* LowerBigIntNegate(Node* node);
Node* LowerCheckFloat64Hole(Node* node, Node* frame_state);
Node* LowerCheckNotTaggedHole(Node* node, Node* frame_state);
@@ -302,6 +322,7 @@ class EffectControlLinearizer {
Node* ChangeSmiToInt32(Node* value);
Node* ChangeSmiToInt64(Node* value);
Node* ObjectIsSmi(Node* value);
+ Node* JSAnyIsNotPrimitiveHeapObject(Node* value, Node* map = nullptr);
Node* LoadFromSeqString(Node* receiver, Node* position, Node* is_one_byte);
Node* TruncateWordToInt32(Node* value);
Node* MakeWeakForComparison(Node* heap_object);
@@ -312,9 +333,32 @@ class EffectControlLinearizer {
Node* SmiMaxValueConstant();
Node* SmiShiftBitsConstant();
+ void IfThenElse(Node* condition, std::function<void(void)> then_body,
+ std::function<void(void)> else_body);
+ Node* SizeForString(Node* length, Node* is_two_byte);
+ Node* AllocateSeqString(Node* size, Node* one_byte);
+ Node* AllocateSeqString(Node* size, bool one_byte);
+ Node* AllocateOneByteSlicedString();
+ Node* AllocateTwoByteSlicedString();
+ void CopyString(Node* src, Node* dst, Node* len, Node* is_one_byte);
+ Node* StringIsOneByte(Node* node);
+ Node* StringIsTwoByte(Node* node);
+ Node* ConstStringIsOneByte(Node* node);
+ void StoreLiteralStringToBuffer(Node* buffer, Node* offset, Node* node,
+ Node* is_one_byte);
+ Node* ConvertOneByteStringToTwoByte(Node* orig, Node* total_len,
+ Node* initialized_len);
+ template <typename Char>
+ void StoreConstantLiteralStringToBuffer(Node* buffer, Node* offset,
+ Node* node, Node* is_one_byte);
+ void EndStringBuilderConcatForLoopPhi(Node* node, BasicBlock* block);
+ Node* EndStringBuilderConcat(Node* node);
+
// Pass {bitfield} = {digit} = nullptr to construct the canoncial 0n BigInt.
Node* BuildAllocateBigInt(Node* bitfield, Node* digit);
+ Node* BuildAllocateJSExternalObject(Node* pointer);
+
void TransitionElementsTo(Node* node, Node* array, ElementsKind from,
ElementsKind to);
@@ -328,6 +372,8 @@ class EffectControlLinearizer {
// deopt on failure.
void TryMigrateInstance(Node* value, Node* value_map);
+ Node* CallBuiltinForBigIntBinop(Node* left, Node* right, Builtin builtin);
+
bool should_maintain_schedule() const {
return maintain_schedule_ == MaintainSchedule::kMaintain;
}
@@ -356,6 +402,7 @@ class EffectControlLinearizer {
NodeOriginTable* node_origins_;
JSHeapBroker* broker_;
JSGraphAssembler* graph_assembler_;
+ StringBuilderOptimizer* string_builder_optimizer_;
Node* frame_state_zapper_; // For tracking down compiler::Node::New crashes.
};
@@ -657,6 +704,13 @@ void EffectControlLinearizer::Run() {
}
instr++;
+ // We collect in {string_builder_end_phis} the non-loop-Phis that end String
+ // Builders: before processing regular nodes of the current block, we'll
+ // insert truncations of those Phis. (note that when loop Phis end String
+ // Builders, the truncation is inserted in the next block rather than in the
+ // current block, see where BlockShouldFinalizeStringBuilders is used in
+ // this file)
+ base::SmallVector<Node*, 8> string_builder_end_phis;
// Iterate over the phis and update the effect phis.
Node* effect_phi = nullptr;
Node* terminate = nullptr;
@@ -670,6 +724,9 @@ void EffectControlLinearizer::Run() {
DCHECK_NE(IrOpcode::kIfException, control->opcode());
effect_phi = node;
} else if (node->opcode() == IrOpcode::kPhi) {
+ if (string_builder_optimizer_->IsNonLoopPhiStringBuilderEnd(node)) {
+ string_builder_end_phis.push_back(node);
+ }
// Just skip phis.
} else if (node->opcode() == IrOpcode::kTerminate) {
DCHECK_NULL(terminate);
@@ -768,6 +825,34 @@ void EffectControlLinearizer::Run() {
gasm()->InitializeEffectControl(effect, control);
+ // Inserting trimmings for finished string builders that ended with a loop
+ // Phi.
+ if (string_builder_optimizer_->BlockShouldFinalizeStringBuilders(block)) {
+ for (Node* node :
+ string_builder_optimizer_->GetStringBuildersToFinalize(block)) {
+ EndStringBuilderConcatForLoopPhi(node, block);
+ }
+ }
+
+ // Finishing string builders that end with non-loop Phi nodes.
+ for (Node* phi : string_builder_end_phis) {
+ size_t node_count = graph()->NodeCount();
+
+ Node* trimmed = EndStringBuilderConcat(phi);
+
+ // Replacing uses of {phi} by {trimmed}.
+ for (Edge edge : phi->use_edges()) {
+ if (edge.from()->id() >= node_count) {
+ // This is an edge to a new node that was introduced to do the
+ // trimming; we certainly don't want to replace it.
+ continue;
+ }
+ DCHECK(!NodeProperties::IsControlEdge(edge) &&
+ !NodeProperties::IsEffectEdge(edge));
+ edge.UpdateTo(trimmed);
+ }
+ }
+
// Process the ordinary instructions.
for (; instr != end_instr; instr++) {
Node* node = *instr;
@@ -875,7 +960,10 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state) {
inside_region_ = false;
// Update the value uses to the value input of the finish node and
// the effect uses to the effect input.
- return RemoveRenameNode(node);
+ if (!v8_flags.turboshaft) {
+ RemoveRenameNode(node);
+ return;
+ }
}
if (node->opcode() == IrOpcode::kBeginRegion) {
// Determine the observability for this region and use that for all
@@ -886,7 +974,10 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state) {
inside_region_ = true;
// Update the value uses to the value input of the finish node and
// the effect uses to the effect input.
- return RemoveRenameNode(node);
+ if (!v8_flags.turboshaft) {
+ RemoveRenameNode(node);
+ return;
+ }
}
if (node->opcode() == IrOpcode::kTypeGuard) {
return RemoveRenameNode(node);
@@ -898,7 +989,9 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state) {
// effect that is passed. The frame state is preserved for lowering.
DCHECK_EQ(RegionObservability::kObservable, region_observability_);
*frame_state = NodeProperties::GetFrameStateInput(node);
- return;
+ if (!v8_flags.turboshaft) return;
+ // We keep checkpoints to allow Turboshaft's graph builder to recompute the
+ // correct FrameStates for nodes.
}
if (node->opcode() == IrOpcode::kStoreField) {
@@ -929,60 +1022,78 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
Node* result = nullptr;
switch (node->opcode()) {
case IrOpcode::kChangeBitToTagged:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeBitToTagged(node);
break;
case IrOpcode::kChangeInt31ToTaggedSigned:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeInt31ToTaggedSigned(node);
break;
case IrOpcode::kChangeInt32ToTagged:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeInt32ToTagged(node);
break;
case IrOpcode::kChangeInt64ToTagged:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeInt64ToTagged(node);
break;
case IrOpcode::kChangeUint32ToTagged:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeUint32ToTagged(node);
break;
case IrOpcode::kChangeUint64ToTagged:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeUint64ToTagged(node);
break;
case IrOpcode::kChangeFloat64ToTagged:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeFloat64ToTagged(node);
break;
case IrOpcode::kChangeFloat64ToTaggedPointer:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeFloat64ToTaggedPointer(node);
break;
case IrOpcode::kChangeTaggedSignedToInt32:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeTaggedSignedToInt32(node);
break;
case IrOpcode::kChangeTaggedSignedToInt64:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeTaggedSignedToInt64(node);
break;
case IrOpcode::kChangeTaggedToBit:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeTaggedToBit(node);
break;
case IrOpcode::kChangeTaggedToInt32:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeTaggedToInt32(node);
break;
case IrOpcode::kChangeTaggedToUint32:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeTaggedToUint32(node);
break;
case IrOpcode::kChangeTaggedToInt64:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeTaggedToInt64(node);
break;
case IrOpcode::kChangeTaggedToFloat64:
+ if (v8_flags.turboshaft) return false;
result = LowerChangeTaggedToFloat64(node);
break;
case IrOpcode::kChangeTaggedToTaggedSigned:
result = LowerChangeTaggedToTaggedSigned(node);
break;
case IrOpcode::kTruncateTaggedToBit:
+ if (v8_flags.turboshaft) return false;
result = LowerTruncateTaggedToBit(node);
break;
case IrOpcode::kTruncateTaggedPointerToBit:
+ if (v8_flags.turboshaft) return false;
result = LowerTruncateTaggedPointerToBit(node);
break;
case IrOpcode::kTruncateTaggedToFloat64:
+ if (v8_flags.turboshaft) return false;
result = LowerTruncateTaggedToFloat64(node);
break;
case IrOpcode::kCheckClosure:
@@ -995,102 +1106,215 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
result = LowerCompareMaps(node);
break;
case IrOpcode::kCheckNumber:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckNumber(node, frame_state);
break;
case IrOpcode::kCheckReceiver:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckReceiver(node, frame_state);
break;
case IrOpcode::kCheckReceiverOrNullOrUndefined:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckReceiverOrNullOrUndefined(node, frame_state);
break;
case IrOpcode::kCheckSymbol:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckSymbol(node, frame_state);
break;
case IrOpcode::kCheckString:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckString(node, frame_state);
break;
case IrOpcode::kCheckedUint64ToInt64:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedUint64ToInt64(node, frame_state);
break;
- case IrOpcode::kCheckBigInt:
- result = LowerCheckBigInt(node, frame_state);
- break;
case IrOpcode::kCheckedBigIntToBigInt64:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedBigIntToBigInt64(node, frame_state);
break;
case IrOpcode::kCheckInternalizedString:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckInternalizedString(node, frame_state);
break;
case IrOpcode::kCheckIf:
LowerCheckIf(node, frame_state);
break;
case IrOpcode::kCheckedInt32Add:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt32Add(node, frame_state);
break;
case IrOpcode::kCheckedInt32Sub:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt32Sub(node, frame_state);
break;
case IrOpcode::kCheckedInt32Div:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt32Div(node, frame_state);
break;
case IrOpcode::kCheckedInt32Mod:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt32Mod(node, frame_state);
break;
case IrOpcode::kCheckedUint32Div:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedUint32Div(node, frame_state);
break;
case IrOpcode::kCheckedUint32Mod:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedUint32Mod(node, frame_state);
break;
case IrOpcode::kCheckedInt32Mul:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt32Mul(node, frame_state);
break;
case IrOpcode::kCheckedInt64Add:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt64Add(node, frame_state);
break;
case IrOpcode::kCheckedInt64Sub:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt64Sub(node, frame_state);
break;
case IrOpcode::kCheckedInt64Mul:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt64Mul(node, frame_state);
break;
case IrOpcode::kCheckedInt64Div:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt64Div(node, frame_state);
break;
case IrOpcode::kCheckedInt64Mod:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt64Mod(node, frame_state);
break;
case IrOpcode::kCheckedInt32ToTaggedSigned:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt32ToTaggedSigned(node, frame_state);
break;
case IrOpcode::kCheckedInt64ToInt32:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt64ToInt32(node, frame_state);
break;
case IrOpcode::kCheckedInt64ToTaggedSigned:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedInt64ToTaggedSigned(node, frame_state);
break;
case IrOpcode::kCheckedUint32Bounds:
result = LowerCheckedUint32Bounds(node, frame_state);
break;
case IrOpcode::kCheckedUint32ToInt32:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedUint32ToInt32(node, frame_state);
break;
case IrOpcode::kCheckedUint32ToTaggedSigned:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedUint32ToTaggedSigned(node, frame_state);
break;
case IrOpcode::kCheckedUint64Bounds:
result = LowerCheckedUint64Bounds(node, frame_state);
break;
case IrOpcode::kCheckedUint64ToInt32:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedUint64ToInt32(node, frame_state);
break;
case IrOpcode::kCheckedUint64ToTaggedSigned:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedUint64ToTaggedSigned(node, frame_state);
break;
case IrOpcode::kCheckedFloat64ToInt32:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedFloat64ToInt32(node, frame_state);
break;
case IrOpcode::kCheckedFloat64ToInt64:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedFloat64ToInt64(node, frame_state);
break;
case IrOpcode::kCheckedTaggedSignedToInt32:
@@ -1098,18 +1322,38 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
FATAL("No frame state (zapped by #%d: %s)", frame_state_zapper_->id(),
frame_state_zapper_->op()->mnemonic());
}
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedTaggedSignedToInt32(node, frame_state);
break;
case IrOpcode::kCheckedTaggedToArrayIndex:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedTaggedToArrayIndex(node, frame_state);
break;
case IrOpcode::kCheckedTaggedToInt32:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedTaggedToInt32(node, frame_state);
break;
case IrOpcode::kCheckedTaggedToInt64:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedTaggedToInt64(node, frame_state);
break;
case IrOpcode::kCheckedTaggedToFloat64:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerCheckedTaggedToFloat64(node, frame_state);
break;
case IrOpcode::kCheckedTaggedToTaggedSigned:
@@ -1118,16 +1362,43 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckedTaggedToTaggedPointer:
result = LowerCheckedTaggedToTaggedPointer(node, frame_state);
break;
+ case IrOpcode::kCheckBigInt:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
+ result = LowerCheckBigInt(node, frame_state);
+ break;
case IrOpcode::kChangeInt64ToBigInt:
- result = LowerChangeInt64ToBigInt(node);
+ if (v8_flags.turboshaft) {
+ DCHECK(machine()->Is64());
+ // ChangeInt64ToBigInt is allocting when lowered, so we must fix its
+ // position in the effect chain such that it is non-floating after ECL
+ // and cannot mess up when rescheduling (e.g. in Turboshaft's graph
+ // builder).
+ result = gasm()->Chained(node->op(), node->InputAt(0));
+ } else {
+ result = LowerChangeInt64ToBigInt(node);
+ }
break;
case IrOpcode::kChangeUint64ToBigInt:
- result = LowerChangeUint64ToBigInt(node);
+ if (v8_flags.turboshaft) {
+ DCHECK(machine()->Is64());
+ // ChangeUint64ToBigInt is allocting when lowered, so we must fix its
+ // position in the effect chain such that it is non-floating after ECL
+ // and cannot mess up when rescheduling (e.g. in Turboshaft's graph
+ // builder).
+ result = gasm()->Chained(node->op(), node->InputAt(0));
+ } else {
+ result = LowerChangeUint64ToBigInt(node);
+ }
break;
case IrOpcode::kTruncateBigIntToWord64:
+ if (v8_flags.turboshaft) return false;
result = LowerTruncateBigIntToWord64(node);
break;
case IrOpcode::kTruncateTaggedToWord32:
+ if (v8_flags.turboshaft) return false;
result = LowerTruncateTaggedToWord32(node);
break;
case IrOpcode::kCheckedTruncateTaggedToWord32:
@@ -1137,18 +1408,23 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
result = LowerNumberToString(node);
break;
case IrOpcode::kObjectIsArrayBufferView:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsArrayBufferView(node);
break;
case IrOpcode::kObjectIsBigInt:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsBigInt(node);
break;
case IrOpcode::kObjectIsCallable:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsCallable(node);
break;
case IrOpcode::kObjectIsConstructor:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsConstructor(node);
break;
case IrOpcode::kObjectIsDetectableCallable:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsDetectableCallable(node);
break;
case IrOpcode::kObjectIsMinusZero:
@@ -1161,27 +1437,35 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
result = LowerObjectIsNaN(node);
break;
case IrOpcode::kNumberIsNaN:
+ if (v8_flags.turboshaft) return false;
result = LowerNumberIsNaN(node);
break;
case IrOpcode::kObjectIsNonCallable:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsNonCallable(node);
break;
case IrOpcode::kObjectIsNumber:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsNumber(node);
break;
case IrOpcode::kObjectIsReceiver:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsReceiver(node);
break;
case IrOpcode::kObjectIsSmi:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsSmi(node);
break;
case IrOpcode::kObjectIsString:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsString(node);
break;
case IrOpcode::kObjectIsSymbol:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsSymbol(node);
break;
case IrOpcode::kObjectIsUndetectable:
+ if (v8_flags.turboshaft) return false;
result = LowerObjectIsUndetectable(node);
break;
case IrOpcode::kArgumentsLength:
@@ -1197,15 +1481,18 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
result = LowerTypeOf(node);
break;
case IrOpcode::kNewDoubleElements:
+ if (v8_flags.turboshaft) return false;
result = LowerNewDoubleElements(node);
break;
case IrOpcode::kNewSmiOrObjectElements:
+ if (v8_flags.turboshaft) return false;
result = LowerNewSmiOrObjectElements(node);
break;
case IrOpcode::kNewArgumentsElements:
result = LowerNewArgumentsElements(node);
break;
case IrOpcode::kNewConsString:
+ if (v8_flags.turboshaft) return false;
result = LowerNewConsString(node);
break;
case IrOpcode::kSameValue:
@@ -1224,66 +1511,147 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
result = LowerStringConcat(node);
break;
case IrOpcode::kStringFromSingleCharCode:
+ if (v8_flags.turboshaft) return false;
result = LowerStringFromSingleCharCode(node);
break;
case IrOpcode::kStringFromSingleCodePoint:
+ if (v8_flags.turboshaft) return false;
result = LowerStringFromSingleCodePoint(node);
break;
case IrOpcode::kStringIndexOf:
+ if (v8_flags.turboshaft) return false;
result = LowerStringIndexOf(node);
break;
case IrOpcode::kStringFromCodePointAt:
+ if (v8_flags.turboshaft) return false;
result = LowerStringFromCodePointAt(node);
break;
case IrOpcode::kStringLength:
+ if (v8_flags.turboshaft) return false;
result = LowerStringLength(node);
break;
case IrOpcode::kStringToNumber:
result = LowerStringToNumber(node);
break;
case IrOpcode::kStringCharCodeAt:
+ if (v8_flags.turboshaft) return false;
result = LowerStringCharCodeAt(node);
break;
case IrOpcode::kStringCodePointAt:
+ if (v8_flags.turboshaft) return false;
result = LowerStringCodePointAt(node);
break;
case IrOpcode::kStringToLowerCaseIntl:
+ if (v8_flags.turboshaft) return false;
result = LowerStringToLowerCaseIntl(node);
break;
case IrOpcode::kStringToUpperCaseIntl:
+ if (v8_flags.turboshaft) return false;
result = LowerStringToUpperCaseIntl(node);
break;
case IrOpcode::kStringSubstring:
+ if (v8_flags.turboshaft) return false;
result = LowerStringSubstring(node);
break;
case IrOpcode::kStringEqual:
+ if (v8_flags.turboshaft) return false;
result = LowerStringEqual(node);
break;
case IrOpcode::kStringLessThan:
+ if (v8_flags.turboshaft) return false;
result = LowerStringLessThan(node);
break;
case IrOpcode::kStringLessThanOrEqual:
+ if (v8_flags.turboshaft) return false;
result = LowerStringLessThanOrEqual(node);
break;
case IrOpcode::kBigIntAdd:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerBigIntAdd(node, frame_state);
break;
case IrOpcode::kBigIntSubtract:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerBigIntSubtract(node, frame_state);
break;
case IrOpcode::kBigIntMultiply:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerBigIntMultiply(node, frame_state);
break;
case IrOpcode::kBigIntDivide:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerBigIntDivide(node, frame_state);
break;
case IrOpcode::kBigIntModulus:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerBigIntModulus(node, frame_state);
break;
case IrOpcode::kBigIntBitwiseAnd:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
result = LowerBigIntBitwiseAnd(node, frame_state);
break;
+ case IrOpcode::kBigIntBitwiseOr:
+ if (v8_flags.turboshaft) {
+ // Although bitwise or doesn't need a FrameState (because it cannot
+ // deopt), we keep it here for Turboshaft, because this allows us to
+ // handle `or` uniformly with all other binary operations.
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
+ result = LowerBigIntBitwiseOr(node);
+ break;
+ case IrOpcode::kBigIntBitwiseXor:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
+ result = LowerBigIntBitwiseXor(node, frame_state);
+ break;
+ case IrOpcode::kBigIntShiftLeft:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
+ result = LowerBigIntShiftLeft(node, frame_state);
+ break;
+ case IrOpcode::kBigIntShiftRight:
+ if (v8_flags.turboshaft) {
+ gasm()->Checkpoint(FrameState{frame_state});
+ return false;
+ }
+ result = LowerBigIntShiftRight(node, frame_state);
+ break;
+ case IrOpcode::kBigIntEqual:
+ if (v8_flags.turboshaft) return false;
+ result = LowerBigIntEqual(node);
+ break;
+ case IrOpcode::kBigIntLessThan:
+ if (v8_flags.turboshaft) return false;
+ result = LowerBigIntLessThan(node);
+ break;
+ case IrOpcode::kBigIntLessThanOrEqual:
+ if (v8_flags.turboshaft) return false;
+ result = LowerBigIntLessThanOrEqual(node);
+ break;
case IrOpcode::kBigIntNegate:
+ if (v8_flags.turboshaft) return false;
result = LowerBigIntNegate(node);
break;
case IrOpcode::kNumberIsFloat64Hole:
@@ -1320,6 +1688,7 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
LowerCheckEqualsInternalizedString(node, frame_state);
break;
case IrOpcode::kAllocate:
+ if (v8_flags.turboshaft) return false;
result = LowerAllocate(node);
break;
case IrOpcode::kCheckEqualsSymbol:
@@ -1353,6 +1722,7 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
result = LowerFastApiCall(node);
break;
case IrOpcode::kLoadFieldByIndex:
+ if (v8_flags.turboshaft) return false;
result = LowerLoadFieldByIndex(node);
break;
case IrOpcode::kLoadTypedElement:
@@ -1428,6 +1798,7 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
break;
case IrOpcode::kDoubleArrayMax:
case IrOpcode::kDoubleArrayMin:
+ if (v8_flags.turboshaft) return false;
result = LowerDoubleArrayMinMax(node);
break;
default:
@@ -1449,6 +1820,7 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
#define __ gasm()->
Node* EffectControlLinearizer::LowerChangeFloat64ToTagged(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
CheckForMinusZeroMode mode = CheckMinusZeroModeOf(node->op());
Node* value = node->InputAt(0);
return ChangeFloat64ToTagged(value, mode);
@@ -1505,11 +1877,13 @@ Node* EffectControlLinearizer::ChangeFloat64ToTagged(
}
Node* EffectControlLinearizer::LowerChangeFloat64ToTaggedPointer(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
return AllocateHeapNumberWithValue(value);
}
Node* EffectControlLinearizer::LowerChangeBitToTagged(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
return ChangeBitToTagged(value);
}
@@ -1529,11 +1903,13 @@ Node* EffectControlLinearizer::ChangeBitToTagged(Node* value) {
}
Node* EffectControlLinearizer::LowerChangeInt31ToTaggedSigned(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
return ChangeInt32ToSmi(value);
}
Node* EffectControlLinearizer::LowerChangeInt32ToTagged(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
return ChangeInt32ToTagged(value);
}
@@ -1558,6 +1934,7 @@ Node* EffectControlLinearizer::ChangeInt32ToTagged(Node* value) {
}
Node* EffectControlLinearizer::LowerChangeInt64ToTagged(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_not_in_smi_range = __ MakeDeferredLabel();
@@ -1583,6 +1960,7 @@ Node* EffectControlLinearizer::LowerChangeInt64ToTagged(Node* node) {
}
Node* EffectControlLinearizer::LowerChangeUint32ToTagged(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
return ChangeUint32ToTagged(value);
}
@@ -1605,6 +1983,7 @@ Node* EffectControlLinearizer::ChangeUint32ToTagged(Node* value) {
}
Node* EffectControlLinearizer::LowerChangeUint64ToTagged(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_not_in_smi_range = __ MakeDeferredLabel();
@@ -1625,16 +2004,19 @@ Node* EffectControlLinearizer::LowerChangeUint64ToTagged(Node* node) {
}
Node* EffectControlLinearizer::LowerChangeTaggedSignedToInt32(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
return ChangeSmiToInt32(value);
}
Node* EffectControlLinearizer::LowerChangeTaggedSignedToInt64(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
return ChangeSmiToInt64(value);
}
Node* EffectControlLinearizer::LowerChangeTaggedToBit(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
return __ TaggedEqual(value, __ TrueConstant());
}
@@ -1699,6 +2081,7 @@ void EffectControlLinearizer::TruncateTaggedPointerToBit(
}
Node* EffectControlLinearizer::LowerTruncateTaggedToBit(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
auto done = __ MakeLabel(MachineRepresentation::kBit);
auto if_smi = __ MakeDeferredLabel();
@@ -1719,6 +2102,7 @@ Node* EffectControlLinearizer::LowerTruncateTaggedToBit(Node* node) {
}
Node* EffectControlLinearizer::LowerTruncateTaggedPointerToBit(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
auto done = __ MakeLabel(MachineRepresentation::kBit);
TruncateTaggedPointerToBit(node, &done);
@@ -1728,6 +2112,7 @@ Node* EffectControlLinearizer::LowerTruncateTaggedPointerToBit(Node* node) {
}
Node* EffectControlLinearizer::LowerChangeTaggedToInt32(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_not_smi = __ MakeDeferredLabel();
@@ -1749,6 +2134,7 @@ Node* EffectControlLinearizer::LowerChangeTaggedToInt32(Node* node) {
}
Node* EffectControlLinearizer::LowerChangeTaggedToUint32(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_not_smi = __ MakeDeferredLabel();
@@ -1770,6 +2156,7 @@ Node* EffectControlLinearizer::LowerChangeTaggedToUint32(Node* node) {
}
Node* EffectControlLinearizer::LowerChangeTaggedToInt64(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_not_smi = __ MakeDeferredLabel();
@@ -1791,6 +2178,7 @@ Node* EffectControlLinearizer::LowerChangeTaggedToInt64(Node* node) {
}
Node* EffectControlLinearizer::LowerChangeTaggedToFloat64(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
return LowerTruncateTaggedToFloat64(node);
}
@@ -1798,7 +2186,7 @@ Node* EffectControlLinearizer::LowerChangeTaggedToTaggedSigned(Node* node) {
Node* value = node->InputAt(0);
auto if_not_smi = __ MakeDeferredLabel();
- auto done = __ MakeLabel(MachineRepresentation::kWord32);
+ auto done = __ MakeLabel(MachineRepresentation::kTaggedSigned);
Node* check = ObjectIsSmi(value);
__ GotoIfNot(check, &if_not_smi);
@@ -1817,6 +2205,7 @@ Node* EffectControlLinearizer::LowerChangeTaggedToTaggedSigned(Node* node) {
}
Node* EffectControlLinearizer::LowerTruncateTaggedToFloat64(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_not_smi = __ MakeDeferredLabel();
@@ -1858,7 +2247,7 @@ Node* EffectControlLinearizer::LowerCheckClosure(Node* node,
// we recorded before.
Node* value_cell =
__ LoadField(AccessBuilder::ForJSFunctionFeedbackCell(), value);
- Node* check_cell = __ WordEqual(value_cell, __ HeapConstant(feedback_cell));
+ Node* check_cell = __ TaggedEqual(value_cell, __ HeapConstant(feedback_cell));
__ DeoptimizeIfNot(DeoptimizeReason::kWrongFeedbackCell, FeedbackSource(),
check_cell, frame_state);
return value;
@@ -1981,6 +2370,18 @@ void EffectControlLinearizer::TryMigrateInstance(Node* value, Node* value_map) {
__ Bind(&done);
}
+Node* EffectControlLinearizer::CallBuiltinForBigIntBinop(Node* left,
+ Node* right,
+ Builtin builtin) {
+ Callable const callable = Builtins::CallableFor(isolate(), builtin);
+ auto call_descriptor = Linkage::GetStubCallDescriptor(
+ graph()->zone(), callable.descriptor(),
+ callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
+ Operator::kFoldable | Operator::kNoThrow);
+ return __ Call(call_descriptor, __ HeapConstant(callable.code()), left, right,
+ __ NoContextConstant());
+}
+
Node* EffectControlLinearizer::LowerCompareMaps(Node* node) {
ZoneHandleSet<Map> const& maps = CompareMapsParametersOf(node->op());
size_t const map_count = maps.size();
@@ -2011,6 +2412,7 @@ Node* EffectControlLinearizer::LowerCompareMaps(Node* node) {
}
Node* EffectControlLinearizer::LowerCheckNumber(Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
@@ -2034,15 +2436,9 @@ Node* EffectControlLinearizer::LowerCheckNumber(Node* node, Node* frame_state) {
Node* EffectControlLinearizer::LowerCheckReceiver(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
-
- Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
- Node* value_instance_type =
- __ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
-
- static_assert(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
- Node* check = __ Uint32LessThanOrEqual(
- __ Uint32Constant(FIRST_JS_RECEIVER_TYPE), value_instance_type);
+ Node* check = JSAnyIsNotPrimitiveHeapObject(value);
__ DeoptimizeIfNot(DeoptimizeReason::kNotAJavaScriptObject, FeedbackSource(),
check, frame_state);
return value;
@@ -2050,8 +2446,20 @@ Node* EffectControlLinearizer::LowerCheckReceiver(Node* node,
Node* EffectControlLinearizer::LowerCheckReceiverOrNullOrUndefined(
Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
+#if V8_STATIC_ROOTS_BOOL
+ Node* check0 = JSAnyIsNotPrimitiveHeapObject(value);
+ Node* check1 = __ TaggedEqual(value, __ UndefinedConstant());
+ Node* check2 = __ TaggedEqual(value, __ NullConstant());
+
+ __ DeoptimizeIfNot(DeoptimizeReason::kNotAJavaScriptObjectOrNullOrUndefined,
+ FeedbackSource(),
+ __ Word32Or(check0, __ Word32Or(check1, check2)),
+ frame_state);
+
+#else
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* value_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
@@ -2068,10 +2476,12 @@ Node* EffectControlLinearizer::LowerCheckReceiverOrNullOrUndefined(
Node* check1 = __ TaggedEqual(value_map, __ BooleanMapConstant());
__ DeoptimizeIf(DeoptimizeReason::kNotAJavaScriptObjectOrNullOrUndefined,
FeedbackSource(), check1, frame_state);
+#endif
return value;
}
Node* EffectControlLinearizer::LowerCheckSymbol(Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
@@ -2084,6 +2494,7 @@ Node* EffectControlLinearizer::LowerCheckSymbol(Node* node, Node* frame_state) {
}
Node* EffectControlLinearizer::LowerCheckString(Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
@@ -2100,6 +2511,7 @@ Node* EffectControlLinearizer::LowerCheckString(Node* node, Node* frame_state) {
Node* EffectControlLinearizer::LowerCheckInternalizedString(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
@@ -2122,25 +2534,732 @@ void EffectControlLinearizer::LowerCheckIf(Node* node, Node* frame_state) {
__ DeoptimizeIfNot(p.reason(), p.feedback(), value, frame_state);
}
+namespace {
+
+int GetLiteralStringLen(Node* node, JSHeapBroker* broker) {
+ if (node->opcode() == IrOpcode::kStringFromSingleCharCode) {
+ return 1;
+ }
+ HeapObjectMatcher m(node);
+ DCHECK(m.HasResolvedValue() && m.Ref(broker).IsString());
+ StringRef string = m.Ref(broker).AsString();
+ return string.length();
+}
+
+int IsTwoByteString(Node* node, JSHeapBroker* broker) {
+ HeapObjectMatcher m(node);
+ DCHECK(m.HasResolvedValue() && m.Ref(broker).IsString());
+ StringRef string = m.Ref(broker).AsString();
+ return string.object()->IsTwoByteRepresentation();
+}
+
+template <typename Char>
+const Char* GetLiteralString(Node* node, JSHeapBroker* broker) {
+ DCHECK_EQ(node->opcode(), IrOpcode::kHeapConstant);
+ HeapObjectMatcher m(node);
+ DCHECK(m.HasResolvedValue() && m.Ref(broker).IsString());
+ StringRef string = m.Ref(broker).AsString();
+ DisallowGarbageCollection no_gc;
+ SharedStringAccessGuardIfNeeded access_guard(broker->isolate());
+ const Char* str = string.object()->template GetDirectStringChars<Char>(
+ broker->isolate(), no_gc, access_guard);
+ return str;
+}
+
+} // namespace
+
+// Generated-code equivalent of:
+// if (condition) { then_body } else { else_body }
+void EffectControlLinearizer::IfThenElse(Node* condition,
+ std::function<void(void)> then_body,
+ std::function<void(void)> else_body) {
+ auto if_true = __ MakeLabel(), if_false = __ MakeLabel(),
+ done = __ MakeLabel();
+ __ Branch(condition, &if_true, &if_false);
+ __ Bind(&if_true);
+ then_body();
+ __ Goto(&done);
+ __ Bind(&if_false);
+ else_body();
+ __ Goto(&done);
+ __ Bind(&done);
+}
+
+// This function is the generated-code equivalent of SeqOneByteString::SizeFor
+// and SeqTwoByteString::SizeFor: it calculates how many bytes should be
+// allocated if we want to allocate a string of length {length} (in particular,
+// it takes headers and alignment into account).
+Node* EffectControlLinearizer::SizeForString(Node* length, Node* is_two_byte) {
+ Node* size = __ Int32Constant(String::kHeaderSize);
+ size = __ Int32Add(size, __ Word32Shl(length, is_two_byte));
+
+ auto object_pointer_align = [&](Node* value) -> Node* {
+ // Generated-code equivalent of the OBJECT_POINTER_ALIGN macro.
+ return __ Word32And(
+ __ Int32Add(value, __ Int32Constant(kObjectAlignmentMask)),
+ __ Int32Constant(~kObjectAlignmentMask));
+ };
+ size = object_pointer_align(size);
+ size = ChangeInt32ToIntPtr(size);
+ return size;
+}
+
+// Allocates a new 1/2-byte (depending on {is_one_byte}) SeqString of length
+// {length}.
+Node* EffectControlLinearizer::AllocateSeqString(Node* length, bool one_byte) {
+ Node* size = SizeForString(length, __ Int32Constant(!one_byte));
+ Node* seq_string = __ Allocate(AllocationType::kYoung, size);
+ __ StoreField(AccessBuilder::ForMap(), seq_string,
+ __ HeapConstant(one_byte ? factory()->one_byte_string_map()
+ : factory()->string_map()));
+ __ StoreField(AccessBuilder::ForNameRawHashField(), seq_string,
+ __ Int32Constant(Name::kEmptyHashField));
+ __ StoreField(AccessBuilder::ForStringLength(), seq_string, length);
+ // zero-ing the padding bytes.
+ __ Store(
+ StoreRepresentation(MachineRepresentation::kTaggedSigned,
+ kNoWriteBarrier),
+ seq_string,
+ __ IntPtrAdd(size, __ IntPtrConstant(-kObjectAlignment - kHeapObjectTag)),
+ __ SmiConstant(0));
+
+ return seq_string;
+}
+
+// Allocates a new 1/2-byte (depending on {is_one_byte}) SeqString of length
+// {length}. This function is a dynamic version of the previous one for cases
+// where {is_one_byte} is not known at compile time.
+Node* EffectControlLinearizer::AllocateSeqString(Node* length,
+ Node* is_one_byte) {
+ Node* is_two_byte = __ Word32Xor(is_one_byte, __ Int32Constant(1));
+ Node* size = SizeForString(length, is_two_byte);
+ Node* seq_string = __ Allocate(AllocationType::kYoung, size);
+ __ StoreField(AccessBuilder::ForNameRawHashField(), seq_string,
+ __ Int32Constant(Name::kEmptyHashField));
+ __ StoreField(AccessBuilder::ForStringLength(), seq_string, length);
+ // zero-ing the padding bytes.
+ __ Store(
+ StoreRepresentation(MachineRepresentation::kTaggedSigned,
+ kNoWriteBarrier),
+ seq_string,
+ __ IntPtrAdd(size, __ IntPtrConstant(-kObjectAlignment - kHeapObjectTag)),
+ __ SmiConstant(0));
+
+ IfThenElse(
+ is_one_byte,
+ [&]() {
+ __ StoreField(AccessBuilder::ForMap(), seq_string,
+ __ HeapConstant(factory()->one_byte_string_map()));
+ },
+ [&]() {
+ __ StoreField(AccessBuilder::ForMap(), seq_string,
+ __ HeapConstant(factory()->string_map()));
+ });
+ return seq_string;
+}
+
+Node* EffectControlLinearizer::AllocateOneByteSlicedString() {
+ Node* sliced_string = __ Allocate(
+ AllocationType::kYoung,
+ __ IntPtrConstant(
+ jsgraph()->factory()->sliced_one_byte_string_map()->instance_size()));
+ __ StoreField(AccessBuilder::ForMap(), sliced_string,
+ __ HeapConstant(factory()->sliced_one_byte_string_map()));
+ return sliced_string;
+}
+
+Node* EffectControlLinearizer::AllocateTwoByteSlicedString() {
+ Node* sliced_string = __ Allocate(
+ AllocationType::kYoung,
+ __ IntPtrConstant(
+ jsgraph()->factory()->sliced_string_map()->instance_size()));
+ __ StoreField(AccessBuilder::ForMap(), sliced_string,
+ __ HeapConstant(factory()->sliced_string_map()));
+ return sliced_string;
+}
+
+// Copies the first {length} characters of {src} into {dst}, assuming that they
+// both have the same 1/2-byte representation described by {is_one_byte}.
+void EffectControlLinearizer::CopyString(Node* src, Node* dst, Node* length,
+ Node* is_one_byte) {
+ auto one_byte_lbl = __ MakeLabel(), two_byte_lbl = __ MakeLabel(),
+ done = __ MakeLabel();
+
+ auto copy_string_fun = [&](auto label, auto access) {
+ __ Bind(label);
+ auto loop = __ MakeLoopLabel(MachineRepresentation::kWord32);
+ __ Goto(&loop, __ Int32Constant(0));
+
+ __ Bind(&loop);
+ Node* index = loop.PhiAt(0);
+ __ GotoIf(__ Word32Equal(index, length), &done);
+ __ StoreElement(access, dst, index, __ LoadElement(access, src, index));
+ index = __ Int32Add(index, __ Int32Constant(1));
+ __ Goto(&loop, index);
+ };
+
+ __ Branch(is_one_byte, &one_byte_lbl, &two_byte_lbl);
+ copy_string_fun(&one_byte_lbl, AccessBuilder::ForSeqOneByteStringCharacter());
+ copy_string_fun(&two_byte_lbl, AccessBuilder::ForSeqTwoByteStringCharacter());
+
+ __ Bind(&done);
+}
+
+// For any string, returns true if it's representation is 1-byte.
+Node* EffectControlLinearizer::StringIsOneByte(Node* node) {
+ Node* map = __ LoadField(AccessBuilder::ForMap(), node);
+ Node* instance_type = __ LoadField(AccessBuilder::ForMapInstanceType(), map);
+ Node* is_one_byte = __ Word32Equal(
+ __ Word32And(instance_type, __ Int32Constant(kStringEncodingMask)),
+ __ Int32Constant(kOneByteStringTag));
+ return is_one_byte;
+}
+
+// For any string, returns true if its representation is 2-byte. The result
+// should be equivalent to `!StringIsOneByte(node)`, but we avoid the negation
+// to make things a bit faster.
+Node* EffectControlLinearizer::StringIsTwoByte(Node* node) {
+ Node* map = __ LoadField(AccessBuilder::ForMap(), node);
+ Node* instance_type = __ LoadField(AccessBuilder::ForMapInstanceType(), map);
+ Node* is_one_byte = __ Word32Equal(
+ __ Word32And(instance_type, __ Int32Constant(kStringEncodingMask)),
+ __ Int32Constant(kTwoByteStringTag));
+ return is_one_byte;
+}
+
+// For a literal-ish string (a HeapConstant or a StringFromSingleCharCode),
+// computes whether it's 1-byte, or 2-byte.
+Node* EffectControlLinearizer::ConstStringIsOneByte(Node* node) {
+ if (node->opcode() == IrOpcode::kHeapConstant) {
+ HeapObjectMatcher m(node);
+ DCHECK(m.HasResolvedValue() && m.Ref(broker()).IsString());
+ StringRef string = m.Ref(broker()).AsString();
+ return __ Int32Constant(string.object()->IsOneByteRepresentation());
+ } else {
+ DCHECK_EQ(node->opcode(), IrOpcode::kStringFromSingleCharCode);
+ Node* c = __ Word32And(node->InputAt(0), __ Uint32Constant(0xFFFF));
+ return __ Int32LessThan(c, __ Int32Constant(0xFF + 1));
+ }
+}
+
+// Assuming that {orig} is a 1-byte backing store for a string builder of total
+// length of {total_length}, including {initialized_length} initialized
+// characters (ie, the SlicedString has length {initialized_length}), this
+// function allocates a 2-byte backing store of {total_length} length, and
+// copies the first {initialized_length} characters of {orig} to this new
+// backing store.
+Node* EffectControlLinearizer::ConvertOneByteStringToTwoByte(
+ Node* orig, Node* total_length, Node* initialized_length) {
+ Node* new_string = AllocateSeqString(total_length, false);
+ auto loop = __ MakeLoopLabel(MachineRepresentation::kWord32);
+ auto done = __ MakeLabel();
+ __ Goto(&loop, __ Int32Constant(0));
+ __ Bind(&loop);
+
+ Node* index = loop.PhiAt(0);
+ __ GotoIf(__ Word32Equal(index, initialized_length), &done);
+ __ StoreElement(AccessBuilder::ForSeqTwoByteStringCharacter(), new_string,
+ index,
+ __ LoadElement(AccessBuilder::ForSeqOneByteStringCharacter(),
+ orig, index));
+ index = __ Int32Add(index, __ Int32Constant(1));
+ __ Goto(&loop, index);
+
+ __ Bind(&done);
+ return new_string;
+}
+
+// Stores {node} (which should be a kHeapConstant containing a SeqString) into
+// {buffer} at offset {offset}. {Char} should be {uint8_t} if {node} is a 1-byte
+// string, and {uint16_t} if {node} is a 2-byte string.
+// If {node}'s length is more than {kMaxUnrollSize}, we generate a loop that
+// inserts each character of {node} in {buffer} one by one. Otherwise, we unroll
+// the loop and generate one StoreElement per character.
+template <typename Char>
+void EffectControlLinearizer::StoreConstantLiteralStringToBuffer(
+ Node* buffer, Node* offset, Node* node, Node* is_one_byte) {
+ const int kMaxUnrollSize = 6;
+ int len = GetLiteralStringLen(node, broker());
+ if (len < kMaxUnrollSize) {
+ // For {len} below 6, we unroll the loop and generate one Store per
+ // character.
+ auto copy_constant = [&](auto access) {
+ const Char* chars = GetLiteralString<Char>(node, broker());
+ for (int i = 0; i < len; i++) {
+ __ StoreElement(access, buffer,
+ __ Int32Add(offset, __ Int32Constant(i)),
+ __ Int32Constant(chars[i]));
+ }
+ };
+ if (std::is_same<Char, uint16_t>()) {
+ DCHECK(IsTwoByteString(node, broker()));
+ // If {node} is a literal 2-byte string, then we can just generate the
+ // 2-byte case (because it's guaranteed that {is_one_byte} is false).
+ copy_constant(AccessBuilder::ForSeqTwoByteStringCharacter());
+ } else {
+ // On the other hand, if {node} is 1-byte, then we can't infer anything,
+ // because {node} can be a 1-byte string that we need to add into a 2-byte
+ // string. Thus, we generate both 1-byte and 2-byte cases, and we'll
+ // decide dynamically which one to execute based on the value of
+ // {is_one_byte}.
+ IfThenElse(
+ is_one_byte,
+ [&]() {
+ copy_constant(AccessBuilder::ForSeqOneByteStringCharacter());
+ },
+ [&]() {
+ copy_constant(AccessBuilder::ForSeqTwoByteStringCharacter());
+ });
+ }
+ } else {
+ // For {len} above 6, we generate a proper loop, in order to keep code size
+ // not too high.
+ auto copy_constant = [&](auto buffer_access, auto constant_access) {
+ auto loop = __ MakeLoopLabel(MachineRepresentation::kWord32);
+ auto done = __ MakeLabel();
+ __ Goto(&loop, __ Int32Constant(0));
+
+ __ Bind(&loop);
+ Node* index = loop.PhiAt(0);
+ __ GotoIf(__ Word32Equal(index, __ Int32Constant(len)), &done);
+ __ StoreElement(buffer_access, buffer, __ Int32Add(offset, index),
+ __ LoadElement(constant_access, node, index));
+ __ Goto(&loop, __ Int32Add(index, __ Int32Constant(1)));
+ __ Bind(&done);
+ };
+ auto constant_access = IsTwoByteString(node, broker())
+ ? AccessBuilder::ForSeqTwoByteStringCharacter()
+ : AccessBuilder::ForSeqOneByteStringCharacter();
+ IfThenElse(
+ is_one_byte,
+ [&]() {
+ copy_constant(AccessBuilder::ForSeqOneByteStringCharacter(),
+ constant_access);
+ },
+ [&]() {
+ copy_constant(AccessBuilder::ForSeqTwoByteStringCharacter(),
+ constant_access);
+ });
+ }
+}
+
+// Stores {node} (which should be a kHeapConstant or a StringFromSingleCharCode)
+// into {buffer} at offset {offset}.
+void EffectControlLinearizer::StoreLiteralStringToBuffer(Node* buffer,
+ Node* offset,
+ Node* node,
+ Node* is_one_byte) {
+ DCHECK(node->opcode() == IrOpcode::kHeapConstant ||
+ node->opcode() == IrOpcode::kStringFromSingleCharCode);
+
+ if (node->opcode() == IrOpcode::kHeapConstant) {
+ if (IsTwoByteString(node, broker())) {
+ StoreConstantLiteralStringToBuffer<uint16_t>(buffer, offset, node,
+ is_one_byte);
+ } else {
+ StoreConstantLiteralStringToBuffer<uint8_t>(buffer, offset, node,
+ is_one_byte);
+ }
+ } else {
+ IfThenElse(
+ is_one_byte,
+ [&]() {
+ __ StoreElement(
+ AccessBuilder::ForSeqOneByteStringCharacter(), buffer, offset,
+ __ Word32And(node->InputAt(0), __ Uint32Constant(0xFFFF)));
+ },
+ [&]() {
+ __ StoreElement(
+ AccessBuilder::ForSeqTwoByteStringCharacter(), buffer, offset,
+ __ Word32And(node->InputAt(0), __ Uint32Constant(0xFFFF)));
+ });
+ }
+}
+
+void EffectControlLinearizer::EndStringBuilderConcatForLoopPhi(
+ Node* node, BasicBlock* block) {
+ Node* backing_store = EndStringBuilderConcat(node);
+
+ // Updating the uses of {node} to use {backing_store} instead.
+ BasicBlock* loop_header = schedule()->block(node);
+ DCHECK(loop_header->IsLoopHeader());
+ for (Edge edge : node->use_edges()) {
+ BasicBlock* user_block = schedule()->block(edge.from());
+ if (!user_block) {
+ // If {schedule_} doesn't have a block |edge.from()|, then this is a new
+ // node, which means that it is within the Phi's loop (because
+ // EffectControlLinearize iterates the graph in RPO order).
+ continue;
+ }
+ if (loop_header->LoopContains(user_block)) {
+ // |edge.from()| is within the loop, so it's part of the string builder,
+ // and should use the regular Phi node.
+ continue;
+ }
+ if ((*user_block->nodes())[0]->opcode() == IrOpcode::kMerge) {
+ if (std::find(user_block->predecessors().begin(),
+ user_block->predecessors().end(),
+ block) != user_block->predecessors().end()) {
+ // {user_block} merges {block} (+ some other block), so we update the
+ // edge, even though {block} will not dominate {user_block}.
+ edge.UpdateTo(backing_store);
+ continue;
+ }
+ }
+
+ // If we reach this point:
+ // - {user_block} cannot be before {block}, otherwise it couldn't use
+ // {node} which is defined in {block} (that was true at the beginning of
+ // the loop already).
+ // - {user_block} cannot be inside the loop (we've checked that).
+ // So {user_block} has to be after the loop. Then, since {user_block} is
+ // after {block} and uses {node}:
+ // - either it's not dominated by {block}, in which case it has to be a
+ // merge so that is can receive another value for {node} from its other
+ // predecessor(s) (otherwise it couldn't use {node}). We've checked that
+ // right above, so we're not in this case.
+ // - it is dominated by {block}. This is the only case that remains; we
+ // DCHECK it just to be safe.
+ DCHECK_EQ(BasicBlock::GetCommonDominator(block, user_block), block);
+ // {user_block} is dominated by {block}, which mean that we can safely
+ // update the edge.
+ DCHECK(!NodeProperties::IsControlEdge(edge) &&
+ !NodeProperties::IsEffectEdge(edge));
+ edge.UpdateTo(backing_store);
+ }
+}
+
+// Once the string builder is done building the string, we get rid of the
+// SlicedString and right-trim the backing store SeqString, to keep only the
+// SeqString.
+Node* EffectControlLinearizer::EndStringBuilderConcat(Node* node) {
+ Node* new_length = __ LoadField(AccessBuilder::ForStringLength(), node);
+ Node* backing_store =
+ __ LoadField(AccessBuilder::ForSlicedStringParent(), node);
+ Node* backing_store_length =
+ __ LoadField(AccessBuilder::ForStringLength(), backing_store);
+
+ Node* is_two_byte = StringIsTwoByte(backing_store);
+ Node* backing_store_real_size =
+ SizeForString(backing_store_length, is_two_byte);
+ Node* new_backing_store_real_size = SizeForString(new_length, is_two_byte);
+
+ Node* freed_size =
+ __ Int32Sub(backing_store_real_size, new_backing_store_real_size);
+
+ // Right-trimming code inspired by heap.cc:CreateFillerObjectAtImpl
+ IfThenElse(
+ __ Word32Equal(freed_size, __ Int32Constant(0)),
+ []() {
+ // If the filler has size 0, do nothing.
+ },
+ [&]() {
+ Node* filler_map_location =
+ __ IntPtrAdd(backing_store, new_backing_store_real_size);
+ IfThenElse(
+ __ Word32Equal(freed_size, __ Int32Constant(kTaggedSize)),
+ [&]() {
+ // If the filler has size kTaggedSize, insert
+ // one_pointer_filler_map()
+ __ StoreField(
+ AccessBuilder::ForMap(kNoWriteBarrier), filler_map_location,
+ __ HeapConstant(factory()->one_pointer_filler_map()));
+ },
+ [&]() {
+ IfThenElse(
+ __ Word32Equal(freed_size, __ Int32Constant(2 * kTaggedSize)),
+ [&]() {
+ // If the filler has size kTaggedSize*2, insert
+ // two_pointer_filler_map()
+ __ StoreField(
+ AccessBuilder::ForMap(kNoWriteBarrier),
+ filler_map_location,
+ __ HeapConstant(factory()->two_pointer_filler_map()));
+ },
+ [&]() {
+ // Otherwise, insert free_space_map(), with the proper size.
+ __ StoreField(AccessBuilder::ForMap(kNoWriteBarrier),
+ filler_map_location,
+ __ HeapConstant(factory()->free_space_map()));
+ __ StoreField(AccessBuilder::ForFreeSpaceSize(),
+ filler_map_location,
+ ChangeIntPtrToSmi(freed_size));
+ });
+ });
+ });
+
+ // Updating backing store length after trimming
+ __ StoreField(AccessBuilder::ForStringLength(), backing_store, new_length);
+
+ // Setting the padding bytes to 0
+ {
+ Node* end =
+ __ IntPtrSub(__ IntPtrAdd(backing_store, new_backing_store_real_size),
+ __ IntPtrConstant(kHeapObjectTag));
+ Node* start = __ IntPtrSub(
+ end, __ IntPtrSub(new_backing_store_real_size,
+ __ IntPtrAdd(__ IntPtrConstant(String::kHeaderSize),
+ ChangeInt32ToIntPtr(__ Word32Shl(
+ new_length, is_two_byte)))));
+ auto loop = __ MakeLoopLabel(MachineType::PointerRepresentation());
+ auto done = __ MakeLabel();
+ __ Goto(&loop, start);
+ __ Bind(&loop);
+ Node* addr = loop.PhiAt(0);
+ Node* check = __ UintLessThan(addr, end);
+ __ GotoIfNot(check, &done);
+ __ Store(
+ StoreRepresentation(MachineRepresentation::kWord8, kNoWriteBarrier),
+ addr, 0, __ Int32Constant(0));
+ __ Goto(&loop, __ IntPtrAdd(addr, __ IntPtrConstant(1)));
+
+ __ Bind(&done);
+ }
+
+ // Overwriting {node} with a filler object, so that we don't leave around a
+ // potentially-too-small SlicedString. Note that:
+ // - the GC can still see too-small SlicedString, but it shouldn't care
+ // except in debug builds, and even then, the isolate has a
+ // has_turbofan_string_builders method that tells the GC to ignore
+ // too-small SlicedString when checking verifying heap integrity.
+ // - overwriting {node} with a filler is not strictly needed, but is
+ // relatively cheap, and useful to catch bugs (eg, if after a string
+ // builder, we get an exception saying "Expected String, got FreeSpace",
+ // we'll have an idea of what went wrong!).
+ __ StoreField(AccessBuilder::ForMap(kNoWriteBarrier), node,
+ __ HeapConstant(factory()->free_space_map()));
+ __ StoreField(AccessBuilder::ForFreeSpaceSize(), node,
+ ChangeInt32ToSmi(__ Int32Constant(SlicedString::kSize)));
+
+ return backing_store;
+}
+
Node* EffectControlLinearizer::LowerStringConcat(Node* node) {
- Node* lhs = node->InputAt(1);
- Node* rhs = node->InputAt(2);
+ if (string_builder_optimizer_->IsFirstConcatInStringBuilder(node)) {
+ // This is the 1st node of a string builder. We thus need to create and
+ // initialize the string builder's backing store and SlicedString.
+ OneOrTwoByteAnalysis::State one_or_two_byte =
+ string_builder_optimizer_->GetOneOrTwoByte(node);
+
+ int left_length = GetLiteralStringLen(node->InputAt(1), broker());
+ int right_length = GetLiteralStringLen(node->InputAt(2), broker());
+
+ int initial_length = left_length + right_length;
+ int backing_store_initial_length = initial_length * 4;
+
+ // Creating the backing store.
+ Node* is_one_byte =
+ one_or_two_byte == OneOrTwoByteAnalysis::State::kOneByte
+ ? __ Int32Constant(1)
+ : one_or_two_byte == OneOrTwoByteAnalysis::State::kTwoByte
+ ? __ Int32Constant(0)
+ : __ Word32And(ConstStringIsOneByte(node->InputAt(1)),
+ ConstStringIsOneByte(node->InputAt(2)));
+
+ Node* length = __ Int32Constant(initial_length);
+ Node* backing_store_length = __ Int32Constant(backing_store_initial_length);
+ Node* backing_store = AllocateSeqString(backing_store_length, is_one_byte);
+
+ // Storing first two strings into the backing store
+ if (left_length != 0) {
+ StoreLiteralStringToBuffer(backing_store, __ Int32Constant(0),
+ node->InputAt(1), is_one_byte);
+ }
+ if (right_length != 0) {
+ StoreLiteralStringToBuffer(backing_store, __ Int32Constant(left_length),
+ node->InputAt(2), is_one_byte);
+ }
- Callable const callable =
- CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE);
- auto call_descriptor = Linkage::GetStubCallDescriptor(
- graph()->zone(), callable.descriptor(),
- callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
- Operator::kNoDeopt | Operator::kNoWrite | Operator::kNoThrow);
+ // Creating the SlicedString view into the backing store
+ Node* sliced_string = nullptr;
+ if (one_or_two_byte == OneOrTwoByteAnalysis::State::kOneByte) {
+ // Allocating 1-byte sliced string
+ sliced_string = AllocateOneByteSlicedString();
+ } else if (one_or_two_byte == OneOrTwoByteAnalysis::State::kTwoByte) {
+ // Allocating 2-byte sliced string
+ sliced_string = AllocateTwoByteSlicedString();
+ } else {
+ // Dynamically choosing to allocate a 1-byte or 2-byte sliced string
+ auto if_true = __ MakeLabel(), if_false = __ MakeLabel();
+ auto done = __ MakeLabel(MachineRepresentation::kTaggedPointer);
+ __ Branch(is_one_byte, &if_true, &if_false);
+ __ Bind(&if_true);
+ __ Goto(&done, AllocateOneByteSlicedString());
+ __ Bind(&if_false);
+ __ Goto(&done, AllocateTwoByteSlicedString());
+ __ Bind(&done);
+ sliced_string = done.PhiAt(0);
+ }
+ // The following StoreFields initialize the SlicedString (except for its map
+ // field, which was already initialized by AllocateOneByteSlicedString or
+ // AllocateTwoByteSlicedString).
+ __ StoreField(AccessBuilder::ForNameRawHashField(), sliced_string,
+ __ Int32Constant(Name::kEmptyHashField));
+ __ StoreField(AccessBuilder::ForSlicedStringParent(), sliced_string,
+ backing_store);
+ __ StoreField(AccessBuilder::ForSlicedStringOffset(), sliced_string,
+ __ Int32Constant(0));
+ __ StoreField(AccessBuilder::ForStringLength(), sliced_string, length);
+
+ return sliced_string;
+ } else if (string_builder_optimizer_->ConcatIsInStringBuilder(node)) {
+ OneOrTwoByteAnalysis::State one_or_two_byte =
+ string_builder_optimizer_->GetOneOrTwoByte(node);
+
+ int literal_length = GetLiteralStringLen(node->InputAt(2), broker());
+
+ Node* sliced_string = node->InputAt(1);
+ Node* current_length =
+ __ LoadField(AccessBuilder::ForStringLength(), sliced_string);
+ Node* init_backing_store =
+ __ LoadField(AccessBuilder::ForSlicedStringParent(), sliced_string);
+ Node* max_length =
+ __ LoadField(AccessBuilder::ForStringLength(), init_backing_store);
+
+ // Checking if we need to convert from 1-byte to 2-byte
+ Node* backing_store_is_onebyte =
+ one_or_two_byte == OneOrTwoByteAnalysis::State::kOneByte
+ ? __ Int32Constant(1)
+ : one_or_two_byte == OneOrTwoByteAnalysis::State::kTwoByte
+ ? __ Int32Constant(0)
+ : StringIsOneByte(init_backing_store);
+ Node* rhs_is_onebyte =
+ one_or_two_byte == OneOrTwoByteAnalysis::State::kOneByte
+ ? __ Int32Constant(1)
+ : one_or_two_byte == OneOrTwoByteAnalysis::State::kTwoByte
+ ? __ Int32Constant(0)
+ : ConstStringIsOneByte(node->InputAt(2));
+ auto has_correct_representation =
+ __ MakeLabel(MachineType::PointerRepresentation());
+ if (one_or_two_byte != OneOrTwoByteAnalysis::State::kOneByte &&
+ one_or_two_byte != OneOrTwoByteAnalysis::State::kTwoByte) {
+ Node* need_to_move_backing_store_to_2_bytes =
+ __ Word32And(backing_store_is_onebyte,
+ __ Word32Equal(rhs_is_onebyte, __ Int32Constant(0)));
+ auto move_backing_store_to_2_bytes = __ MakeDeferredLabel();
+ __ GotoIf(need_to_move_backing_store_to_2_bytes,
+ &move_backing_store_to_2_bytes);
+ __ Goto(&has_correct_representation, init_backing_store);
+
+ // Converting from 1-byte to 2-byte string.
+ __ Bind(&move_backing_store_to_2_bytes);
+ {
+ Node* new_backing_store = ConvertOneByteStringToTwoByte(
+ init_backing_store, max_length, current_length);
+ __ StoreField(AccessBuilder::ForSlicedStringParent(), sliced_string,
+ new_backing_store);
+ __ StoreField(AccessBuilder::ForMap(), sliced_string,
+ __ HeapConstant(factory()->sliced_string_map()));
+ __ Goto(&has_correct_representation, new_backing_store);
+ }
+ } else {
+ // We statically know that the string is 1 (or 2) byte, so we know that we
+ // don't need to change the string from 1 to 2-byte.
+ __ Goto(&has_correct_representation, init_backing_store);
+ }
- Node* value = __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs,
- rhs, __ NoContextConstant());
+ // At this point, the backing store has the correct 1-byte or 2-byte
+ // representation.
+ __ Bind(&has_correct_representation);
+ Node* backing_store = has_correct_representation.PhiAt(0);
+ Node* is_one_byte =
+ one_or_two_byte == OneOrTwoByteAnalysis::State::kOneByte
+ ? __ Int32Constant(1)
+ : one_or_two_byte == OneOrTwoByteAnalysis::State::kTwoByte
+ ? __ Int32Constant(0)
+ : __ Word32And(rhs_is_onebyte, backing_store_is_onebyte);
+
+ // Checking if reallocation is needed (if there is not enough free space
+ // left in the backing store).
+ Node* new_length =
+ __ Int32Add(current_length, __ Int32Constant(literal_length));
+ auto needs_realloc = __ MakeLabel();
+ auto add_to_backing_store =
+ __ MakeLabel(MachineType::PointerRepresentation());
+ Node* realloc_cond = __ Int32LessThan(max_length, new_length);
+
+ __ GotoIf(realloc_cond, &needs_realloc);
+ __ Goto(&add_to_backing_store, backing_store);
+
+ // Reallocating backing store.
+ __ Bind(&needs_realloc);
+ {
+ // The new backing store will have a length of min(2*new_length,
+ // String::kMaxLength).
+ // Note that since early in the pipeline, StringConcat is preceeded by a
+ // check that the size of the resulting string does not exceed
+ // String::kMaxLength (which the String Builder optimizer does not
+ // remove). So, we can safely assume that `new_length*2` is less or equal
+ // to `String::kMaxLength*2`. Thus, since `String::kMaxLength` is less
+ // than `max_uint/2` (checked by the static_assert right here),
+ // `new_length*2` never overflows, and we can safely check if it is more
+ // or less than `String::kMaxLength`: if it's more, then we use
+ // String::kMaxLength instead.
+ static_assert(String::kMaxLength <=
+ std::numeric_limits<unsigned int>::max() / 2);
+ Node* new_backing_store_size_maybe_too_large =
+ __ Word32Shl(new_length, __ Int32Constant(1));
+ auto size_computed_lbl = __ MakeLabel(MachineRepresentation::kWord32);
+ __ GotoIf(__ Int32LessThan(new_backing_store_size_maybe_too_large,
+ __ Int32Constant(String::kMaxLength)),
+ &size_computed_lbl, new_backing_store_size_maybe_too_large);
+ __ Goto(&size_computed_lbl, __ Int32Constant(String::kMaxLength));
+
+ __ Bind(&size_computed_lbl);
+ Node* new_backing_store_size = size_computed_lbl.PhiAt(0);
+ // We allocate a new SeqString, and copy the content of the old backing
+ // store into the new one.
+ Node* new_backing_store =
+ one_or_two_byte == OneOrTwoByteAnalysis::State::kOneByte
+ ? AllocateSeqString(new_backing_store_size, /*one_byte*/ true)
+ : one_or_two_byte == OneOrTwoByteAnalysis::State::kTwoByte
+ ? AllocateSeqString(new_backing_store_size, /*one_byte*/ false)
+ : AllocateSeqString(new_backing_store_size, is_one_byte);
+ CopyString(backing_store, new_backing_store, current_length, is_one_byte);
+ __ StoreField(AccessBuilder::ForSlicedStringParent(), sliced_string,
+ new_backing_store);
+ __ Goto(&add_to_backing_store, new_backing_store);
+ }
- return value;
+ // After reallocation, simply adding the rhs to the backing store.
+ __ Bind(&add_to_backing_store);
+ {
+ Node* real_backing_store = add_to_backing_store.PhiAt(0);
+ StoreLiteralStringToBuffer(real_backing_store, current_length,
+ node->InputAt(2), is_one_byte);
+ __ StoreField(AccessBuilder::ForStringLength(), sliced_string,
+ new_length);
+ }
+
+ if (string_builder_optimizer_->IsStringBuilderEnd(node)) {
+ // If the string builder ends on {node}, then we add the trimming code
+ // right now.
+ return EndStringBuilderConcat(sliced_string);
+ } else {
+ return sliced_string;
+ }
+ } else {
+ Node* lhs = node->InputAt(1);
+ Node* rhs = node->InputAt(2);
+
+ Callable const callable =
+ CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE);
+ auto call_descriptor = Linkage::GetStubCallDescriptor(
+ graph()->zone(), callable.descriptor(),
+ callable.descriptor().GetStackParameterCount(),
+ CallDescriptor::kNoFlags,
+ Operator::kNoDeopt | Operator::kNoWrite | Operator::kNoThrow);
+
+ Node* value = __ Call(call_descriptor, __ HeapConstant(callable.code()),
+ lhs, rhs, __ NoContextConstant());
+
+ return value;
+ }
}
Node* EffectControlLinearizer::LowerCheckedInt32Add(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
@@ -2153,6 +3272,7 @@ Node* EffectControlLinearizer::LowerCheckedInt32Add(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt32Sub(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
@@ -2165,6 +3285,7 @@ Node* EffectControlLinearizer::LowerCheckedInt32Sub(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt32Div(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
Node* zero = __ Int32Constant(0);
@@ -2306,6 +3427,7 @@ Node* EffectControlLinearizer::BuildUint32Mod(Node* lhs, Node* rhs) {
Node* EffectControlLinearizer::LowerCheckedInt32Mod(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
// General case for signed integer modulus, with optimization for (unknown)
// power of 2 right hand side.
//
@@ -2313,7 +3435,7 @@ Node* EffectControlLinearizer::LowerCheckedInt32Mod(Node* node,
// rhs = -rhs
// deopt if rhs == 0
// if lhs < 0 then
- // let lhs_abs = -lsh in
+ // let lhs_abs = -lhs in
// let res = lhs_abs % rhs in
// deopt if res == 0
// -res
@@ -2381,6 +3503,7 @@ Node* EffectControlLinearizer::LowerCheckedInt32Mod(Node* node,
Node* EffectControlLinearizer::LowerCheckedUint32Div(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
Node* zero = __ Int32Constant(0);
@@ -2419,6 +3542,7 @@ Node* EffectControlLinearizer::LowerCheckedUint32Div(Node* node,
Node* EffectControlLinearizer::LowerCheckedUint32Mod(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
@@ -2435,6 +3559,7 @@ Node* EffectControlLinearizer::LowerCheckedUint32Mod(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt32Mul(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
CheckForMinusZeroMode mode = CheckMinusZeroModeOf(node->op());
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
@@ -2469,6 +3594,7 @@ Node* EffectControlLinearizer::LowerCheckedInt32Mul(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt32ToTaggedSigned(
Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(SmiValuesAre31Bits());
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
@@ -2477,6 +3603,7 @@ Node* EffectControlLinearizer::LowerCheckedInt32ToTaggedSigned(
Node* EffectControlLinearizer::LowerCheckedInt64ToInt32(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
@@ -2489,6 +3616,7 @@ Node* EffectControlLinearizer::LowerCheckedInt64ToInt32(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt64ToTaggedSigned(
Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
@@ -2522,7 +3650,7 @@ Node* EffectControlLinearizer::LowerCheckedUint32Bounds(Node* node,
__ Branch(check, &done, &if_abort);
__ Bind(&if_abort);
- __ Unreachable(&done);
+ __ Unreachable();
__ Bind(&done);
}
@@ -2532,6 +3660,7 @@ Node* EffectControlLinearizer::LowerCheckedUint32Bounds(Node* node,
Node* EffectControlLinearizer::LowerCheckedUint32ToInt32(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
Node* unsafe = __ Int32LessThan(value, __ Int32Constant(0));
@@ -2542,6 +3671,7 @@ Node* EffectControlLinearizer::LowerCheckedUint32ToInt32(Node* node,
Node* EffectControlLinearizer::LowerCheckedUint32ToTaggedSigned(
Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
Node* check = __ Uint32LessThanOrEqual(value, SmiMaxValueConstant());
@@ -2568,7 +3698,7 @@ Node* EffectControlLinearizer::LowerCheckedUint64Bounds(Node* node,
__ Branch(check, &done, &if_abort);
__ Bind(&if_abort);
- __ Unreachable(&done);
+ __ Unreachable();
__ Bind(&done);
}
@@ -2577,6 +3707,7 @@ Node* EffectControlLinearizer::LowerCheckedUint64Bounds(Node* node,
Node* EffectControlLinearizer::LowerCheckedUint64ToInt32(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
@@ -2588,6 +3719,7 @@ Node* EffectControlLinearizer::LowerCheckedUint64ToInt32(Node* node,
Node* EffectControlLinearizer::LowerCheckedUint64ToInt64(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
@@ -2600,6 +3732,7 @@ Node* EffectControlLinearizer::LowerCheckedUint64ToInt64(Node* node,
Node* EffectControlLinearizer::LowerCheckedUint64ToTaggedSigned(
Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
@@ -2672,6 +3805,7 @@ Node* EffectControlLinearizer::BuildCheckedFloat64ToIndex(
Node* EffectControlLinearizer::LowerCheckedFloat64ToInt32(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
const CheckMinusZeroParameters& params =
CheckMinusZeroParametersOf(node->op());
Node* value = node->InputAt(0);
@@ -2712,6 +3846,7 @@ Node* EffectControlLinearizer::BuildCheckedFloat64ToInt64(
Node* EffectControlLinearizer::LowerCheckedFloat64ToInt64(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
const CheckMinusZeroParameters& params =
CheckMinusZeroParametersOf(node->op());
Node* value = node->InputAt(0);
@@ -2721,6 +3856,7 @@ Node* EffectControlLinearizer::LowerCheckedFloat64ToInt64(Node* node,
Node* EffectControlLinearizer::LowerCheckedTaggedSignedToInt32(
Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
Node* check = ObjectIsSmi(value);
@@ -2731,6 +3867,7 @@ Node* EffectControlLinearizer::LowerCheckedTaggedSignedToInt32(
Node* EffectControlLinearizer::LowerCheckedTaggedToArrayIndex(
Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
CheckParameters const& params = CheckParametersOf(node->op());
Node* value = node->InputAt(0);
@@ -2783,6 +3920,7 @@ Node* EffectControlLinearizer::LowerCheckedTaggedToArrayIndex(
Node* EffectControlLinearizer::LowerCheckedTaggedToInt32(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
const CheckMinusZeroParameters& params =
CheckMinusZeroParametersOf(node->op());
Node* value = node->InputAt(0);
@@ -2812,6 +3950,7 @@ Node* EffectControlLinearizer::LowerCheckedTaggedToInt32(Node* node,
Node* EffectControlLinearizer::LowerCheckedTaggedToInt64(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
const CheckMinusZeroParameters& params =
CheckMinusZeroParametersOf(node->op());
Node* value = node->InputAt(0);
@@ -2890,6 +4029,7 @@ Node* EffectControlLinearizer::BuildCheckedHeapNumberOrOddballToFloat64(
Node* EffectControlLinearizer::LowerCheckedTaggedToFloat64(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
CheckTaggedInputParameters const& p =
CheckTaggedInputParametersOf(node->op());
Node* value = node->InputAt(0);
@@ -2939,6 +4079,7 @@ Node* EffectControlLinearizer::LowerCheckedTaggedToTaggedPointer(
}
Node* EffectControlLinearizer::LowerCheckBigInt(Node* node, Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
@@ -2958,6 +4099,7 @@ Node* EffectControlLinearizer::LowerCheckBigInt(Node* node, Node* frame_state) {
Node* EffectControlLinearizer::LowerCheckedBigIntToBigInt64(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(machine()->Is64());
auto done = __ MakeLabel();
@@ -3012,6 +4154,7 @@ Node* EffectControlLinearizer::LowerCheckedBigIntToBigInt64(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt64Add(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(machine()->Is64());
Node* lhs = node->InputAt(0);
@@ -3027,6 +4170,7 @@ Node* EffectControlLinearizer::LowerCheckedInt64Add(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt64Sub(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(machine()->Is64());
Node* lhs = node->InputAt(0);
@@ -3042,6 +4186,7 @@ Node* EffectControlLinearizer::LowerCheckedInt64Sub(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt64Mul(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(machine()->Is64());
Node* lhs = node->InputAt(0);
@@ -3057,6 +4202,7 @@ Node* EffectControlLinearizer::LowerCheckedInt64Mul(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt64Div(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(machine()->Is64());
auto division = __ MakeLabel();
@@ -3083,6 +4229,7 @@ Node* EffectControlLinearizer::LowerCheckedInt64Div(Node* node,
Node* EffectControlLinearizer::LowerCheckedInt64Mod(Node* node,
Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(machine()->Is64());
auto modulo_op = __ MakeLabel();
@@ -3110,6 +4257,7 @@ Node* EffectControlLinearizer::LowerCheckedInt64Mod(Node* node,
}
Node* EffectControlLinearizer::LowerChangeInt64ToBigInt(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(machine()->Is64());
auto done = __ MakeLabel(MachineRepresentation::kTagged);
@@ -3125,7 +4273,7 @@ Node* EffectControlLinearizer::LowerChangeInt64ToBigInt(Node* node) {
Node* bitfield =
__ Word32Or(__ Int32Constant(BigInt::LengthBits::encode(1)), sign);
- // We use (value XOR (value >>> 63)) - (value >>> 63) to compute the
+ // We use (value XOR (value >> 63)) - (value >> 63) to compute the
// absolute value, in a branchless fashion.
Node* sign_mask = __ Word64Sar(value, __ Int64Constant(63));
Node* absolute_value = __ Int64Sub(__ Word64Xor(value, sign_mask), sign_mask);
@@ -3136,6 +4284,7 @@ Node* EffectControlLinearizer::LowerChangeInt64ToBigInt(Node* node) {
}
Node* EffectControlLinearizer::LowerChangeUint64ToBigInt(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(machine()->Is64());
auto done = __ MakeLabel(MachineRepresentation::kTagged);
@@ -3153,6 +4302,7 @@ Node* EffectControlLinearizer::LowerChangeUint64ToBigInt(Node* node) {
}
Node* EffectControlLinearizer::LowerTruncateBigIntToWord64(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(machine()->Is64());
auto done = __ MakeLabel(MachineRepresentation::kWord64);
@@ -3183,6 +4333,7 @@ Node* EffectControlLinearizer::LowerTruncateBigIntToWord64(Node* node) {
}
Node* EffectControlLinearizer::LowerTruncateTaggedToWord32(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_not_smi = __ MakeDeferredLabel();
@@ -3230,6 +4381,7 @@ Node* EffectControlLinearizer::LowerCheckedTruncateTaggedToWord32(
}
Node* EffectControlLinearizer::LowerAllocate(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* size = node->InputAt(0);
AllocationType allocation = AllocationTypeOf(node->op());
Node* new_node = __ Allocate(allocation, size);
@@ -3251,6 +4403,7 @@ Node* EffectControlLinearizer::LowerNumberToString(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsArrayBufferView(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
@@ -3277,6 +4430,7 @@ Node* EffectControlLinearizer::LowerObjectIsArrayBufferView(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsBigInt(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
@@ -3284,6 +4438,7 @@ Node* EffectControlLinearizer::LowerObjectIsBigInt(Node* node) {
Node* check = ObjectIsSmi(value);
__ GotoIf(check, &if_smi);
+
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* vfalse = __ TaggedEqual(value_map, __ BigIntMapConstant());
__ Goto(&done, vfalse);
@@ -3296,6 +4451,7 @@ Node* EffectControlLinearizer::LowerObjectIsBigInt(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsCallable(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
@@ -3321,6 +4477,7 @@ Node* EffectControlLinearizer::LowerObjectIsCallable(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsConstructor(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
@@ -3346,6 +4503,7 @@ Node* EffectControlLinearizer::LowerObjectIsConstructor(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsDetectableCallable(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
@@ -3580,6 +4738,7 @@ Node* EffectControlLinearizer::LowerObjectIsNaN(Node* node) {
}
Node* EffectControlLinearizer::LowerNumberIsNaN(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* number = node->InputAt(0);
Node* diff = __ Float64Equal(number, number);
Node* check = __ Word32Equal(diff, __ Int32Constant(0));
@@ -3587,6 +4746,7 @@ Node* EffectControlLinearizer::LowerNumberIsNaN(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsNonCallable(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_primitive = __ MakeDeferredLabel();
@@ -3596,11 +4756,7 @@ Node* EffectControlLinearizer::LowerObjectIsNonCallable(Node* node) {
__ GotoIf(check0, &if_primitive);
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
- Node* value_instance_type =
- __ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
- static_assert(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
- Node* check1 = __ Uint32LessThanOrEqual(
- __ Uint32Constant(FIRST_JS_RECEIVER_TYPE), value_instance_type);
+ Node* check1 = JSAnyIsNotPrimitiveHeapObject(value, value_map);
__ GotoIfNot(check1, &if_primitive);
Node* value_bit_field =
@@ -3619,6 +4775,7 @@ Node* EffectControlLinearizer::LowerObjectIsNonCallable(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsNumber(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeLabel();
@@ -3636,6 +4793,7 @@ Node* EffectControlLinearizer::LowerObjectIsNumber(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsReceiver(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
@@ -3643,13 +4801,7 @@ Node* EffectControlLinearizer::LowerObjectIsReceiver(Node* node) {
__ GotoIf(ObjectIsSmi(value), &if_smi);
- static_assert(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
- Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
- Node* value_instance_type =
- __ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
- Node* result = __ Uint32LessThanOrEqual(
- __ Uint32Constant(FIRST_JS_RECEIVER_TYPE), value_instance_type);
- __ Goto(&done, result);
+ __ Goto(&done, JSAnyIsNotPrimitiveHeapObject(value));
__ Bind(&if_smi);
__ Goto(&done, __ Int32Constant(0));
@@ -3659,11 +4811,13 @@ Node* EffectControlLinearizer::LowerObjectIsReceiver(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsSmi(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
return ObjectIsSmi(value);
}
Node* EffectControlLinearizer::LowerObjectIsString(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
@@ -3671,6 +4825,7 @@ Node* EffectControlLinearizer::LowerObjectIsString(Node* node) {
Node* check = ObjectIsSmi(value);
__ GotoIf(check, &if_smi);
+
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* value_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
@@ -3686,6 +4841,7 @@ Node* EffectControlLinearizer::LowerObjectIsString(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsSymbol(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
@@ -3693,6 +4849,7 @@ Node* EffectControlLinearizer::LowerObjectIsSymbol(Node* node) {
Node* check = ObjectIsSmi(value);
__ GotoIf(check, &if_smi);
+
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* value_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
@@ -3708,6 +4865,7 @@ Node* EffectControlLinearizer::LowerObjectIsSymbol(Node* node) {
}
Node* EffectControlLinearizer::LowerObjectIsUndetectable(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
@@ -3720,11 +4878,9 @@ Node* EffectControlLinearizer::LowerObjectIsUndetectable(Node* node) {
Node* value_bit_field =
__ LoadField(AccessBuilder::ForMapBitField(), value_map);
Node* vfalse = __ Word32Equal(
- __ Word32Equal(
- __ Int32Constant(0),
- __ Word32And(value_bit_field,
- __ Int32Constant(Map::Bits1::IsUndetectableBit::kMask))),
- __ Int32Constant(0));
+ __ Int32Constant(Map::Bits1::IsUndetectableBit::kMask),
+ __ Word32And(value_bit_field,
+ __ Int32Constant(Map::Bits1::IsUndetectableBit::kMask)));
__ Goto(&done, vfalse);
__ Bind(&if_smi);
@@ -3790,6 +4946,7 @@ Node* EffectControlLinearizer::LowerRestLength(Node* node) {
}
Node* EffectControlLinearizer::LowerNewDoubleElements(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
AllocationType const allocation = AllocationTypeOf(node->op());
Node* length = node->InputAt(0);
@@ -3838,6 +4995,7 @@ Node* EffectControlLinearizer::LowerNewDoubleElements(Node* node) {
}
Node* EffectControlLinearizer::LowerNewSmiOrObjectElements(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
AllocationType const allocation = AllocationTypeOf(node->op());
Node* length = node->InputAt(0);
@@ -3912,6 +5070,10 @@ Node* EffectControlLinearizer::LowerNewArgumentsElements(Node* node) {
}
Node* EffectControlLinearizer::LowerNewConsString(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
+ if (string_builder_optimizer_->ConcatIsInStringBuilder(node)) {
+ return LowerStringConcat(node);
+ }
Node* length = node->InputAt(0);
Node* first = node->InputAt(1);
Node* second = node->InputAt(2);
@@ -3968,8 +5130,7 @@ Node* EffectControlLinearizer::LowerSameValue(Node* node) {
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), flags, properties);
- return __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs, rhs,
- __ NoContextConstant());
+ return __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs, rhs);
}
Node* EffectControlLinearizer::LowerSameValueNumbersOnly(Node* node) {
@@ -3983,8 +5144,7 @@ Node* EffectControlLinearizer::LowerSameValueNumbersOnly(Node* node) {
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), flags, properties);
- return __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs, rhs,
- __ NoContextConstant());
+ return __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs, rhs);
}
Node* EffectControlLinearizer::LowerNumberSameValue(Node* node) {
@@ -4095,11 +5255,10 @@ Node* EffectControlLinearizer::StringCharCodeAt(Node* receiver,
__ Bind(&if_seqstring);
{
- Node* receiver_is_onebyte = __ Word32Equal(
+ Node* receiver_is_onebyte =
__ Word32Equal(__ Word32And(receiver_instance_type,
__ Int32Constant(kStringEncodingMask)),
- __ Int32Constant(kTwoByteStringTag)),
- __ Int32Constant(0));
+ __ Int32Constant(kOneByteStringTag));
Node* result = LoadFromSeqString(receiver, position, receiver_is_onebyte);
__ Goto(&loop_done, result);
}
@@ -4187,12 +5346,14 @@ Node* EffectControlLinearizer::StringCharCodeAt(Node* receiver,
}
Node* EffectControlLinearizer::LowerStringCharCodeAt(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* receiver = node->InputAt(0);
Node* position = node->InputAt(1);
return StringCharCodeAt(receiver, position);
}
Node* EffectControlLinearizer::LowerStringCodePointAt(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* receiver = node->InputAt(0);
Node* position = node->InputAt(1);
@@ -4204,7 +5365,8 @@ Node* EffectControlLinearizer::LowerStringCodePointAt(Node* node) {
__ Int32Constant(0xD800)),
&return_result, BranchHint::kFalse, first_code_unit);
- auto length = __ LoadField(AccessBuilder::ForStringLength(), receiver);
+ auto length = __ ChangeUint32ToUintPtr(
+ __ LoadField(AccessBuilder::ForStringLength(), receiver));
auto next_index = __ IntAdd(position, __ IntPtrConstant(1));
__ GotoIfNot(__ IntLessThan(next_index, length), &return_result,
first_code_unit);
@@ -4242,6 +5404,10 @@ Node* EffectControlLinearizer::LoadFromSeqString(Node* receiver, Node* position,
}
Node* EffectControlLinearizer::LowerStringFromSingleCharCode(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
+ if (string_builder_optimizer_->IsStringBuilderConcatInput(node)) {
+ return node;
+ }
Node* value = node->InputAt(0);
Node* code = __ Word32And(value, __ Uint32Constant(0xFFFF));
@@ -4274,6 +5440,11 @@ Node* EffectControlLinearizer::LowerStringFromSingleCharCode(Node* node) {
Node* vfalse1 =
__ Allocate(AllocationType::kYoung,
__ IntPtrConstant(SeqTwoByteString::SizeFor(1)));
+ __ Store(StoreRepresentation(MachineRepresentation::kTaggedSigned,
+ kNoWriteBarrier),
+ vfalse1,
+ SeqTwoByteString::SizeFor(1) - kObjectAlignment - kHeapObjectTag,
+ __ SmiConstant(0));
__ StoreField(AccessBuilder::ForMap(), vfalse1,
__ HeapConstant(factory()->string_map()));
__ StoreField(AccessBuilder::ForNameRawHashField(), vfalse1,
@@ -4295,6 +5466,7 @@ Node* EffectControlLinearizer::LowerStringFromSingleCharCode(Node* node) {
#ifdef V8_INTL_SUPPORT
Node* EffectControlLinearizer::LowerStringToLowerCaseIntl(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* receiver = node->InputAt(0);
Callable callable =
@@ -4309,6 +5481,7 @@ Node* EffectControlLinearizer::LowerStringToLowerCaseIntl(Node* node) {
}
Node* EffectControlLinearizer::LowerStringToUpperCaseIntl(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* receiver = node->InputAt(0);
Operator::Properties properties = Operator::kNoDeopt | Operator::kNoThrow;
Runtime::FunctionId id = Runtime::kStringToUpperCaseIntl;
@@ -4332,6 +5505,7 @@ Node* EffectControlLinearizer::LowerStringToUpperCaseIntl(Node* node) {
#endif // V8_INTL_SUPPORT
Node* EffectControlLinearizer::LowerStringFromSingleCodePoint(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* value = node->InputAt(0);
Node* code = value;
@@ -4370,6 +5544,11 @@ Node* EffectControlLinearizer::LowerStringFromSingleCodePoint(Node* node) {
Node* vfalse1 =
__ Allocate(AllocationType::kYoung,
__ IntPtrConstant(SeqTwoByteString::SizeFor(1)));
+ __ Store(StoreRepresentation(MachineRepresentation::kTaggedSigned,
+ kNoWriteBarrier),
+ vfalse1,
+ SeqTwoByteString::SizeFor(1) - kObjectAlignment - kHeapObjectTag,
+ __ SmiConstant(0));
__ StoreField(AccessBuilder::ForMap(), vfalse1,
__ HeapConstant(factory()->string_map()));
__ StoreField(AccessBuilder::ForNameRawHashField(), vfalse1,
@@ -4410,6 +5589,11 @@ Node* EffectControlLinearizer::LowerStringFromSingleCodePoint(Node* node) {
Node* vfalse0 =
__ Allocate(AllocationType::kYoung,
__ IntPtrConstant(SeqTwoByteString::SizeFor(2)));
+ __ Store(StoreRepresentation(MachineRepresentation::kTaggedSigned,
+ kNoWriteBarrier),
+ vfalse0,
+ SeqTwoByteString::SizeFor(2) - kObjectAlignment - kHeapObjectTag,
+ __ SmiConstant(0));
__ StoreField(AccessBuilder::ForMap(), vfalse0,
__ HeapConstant(factory()->string_map()));
__ StoreField(AccessBuilder::ForNameRawHashField(), vfalse0,
@@ -4429,6 +5613,7 @@ Node* EffectControlLinearizer::LowerStringFromSingleCodePoint(Node* node) {
}
Node* EffectControlLinearizer::LowerStringIndexOf(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* subject = node->InputAt(0);
Node* search_string = node->InputAt(1);
Node* position = node->InputAt(2);
@@ -4440,10 +5625,11 @@ Node* EffectControlLinearizer::LowerStringIndexOf(Node* node) {
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), flags, properties);
return __ Call(call_descriptor, __ HeapConstant(callable.code()), subject,
- search_string, position, __ NoContextConstant());
+ search_string, position);
}
Node* EffectControlLinearizer::LowerStringFromCodePointAt(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* string = node->InputAt(0);
Node* index = node->InputAt(1);
@@ -4455,15 +5641,44 @@ Node* EffectControlLinearizer::LowerStringFromCodePointAt(Node* node) {
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), flags, properties);
return __ Call(call_descriptor, __ HeapConstant(callable.code()), string,
- index, __ NoContextConstant());
+ index);
}
Node* EffectControlLinearizer::LowerStringLength(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* subject = node->InputAt(0);
return __ LoadField(AccessBuilder::ForStringLength(), subject);
}
+Node* EffectControlLinearizer::LowerStringEqual(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
+ Callable callable = Builtins::CallableFor(isolate(), Builtin::kStringEqual);
+ Node* lhs = node->InputAt(0);
+ Node* rhs = node->InputAt(1);
+ Node* lhs_length = __ LoadField(AccessBuilder::ForStringLength(), lhs);
+ Node* rhs_length = __ LoadField(AccessBuilder::ForStringLength(), rhs);
+
+ auto if_length_equal = __ MakeLabel();
+ auto done = __ MakeLabel(MachineRepresentation::kTagged);
+
+ __ GotoIf(__ Word32Equal(lhs_length, rhs_length), &if_length_equal);
+ __ Goto(&done, __ FalseConstant());
+
+ __ Bind(&if_length_equal);
+ Operator::Properties properties = Operator::kEliminatable;
+ CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
+ auto call_descriptor = Linkage::GetStubCallDescriptor(
+ graph()->zone(), callable.descriptor(),
+ callable.descriptor().GetStackParameterCount(), flags, properties);
+ Node* result = __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs,
+ rhs, lhs_length);
+ __ Goto(&done, result);
+
+ __ Bind(&done);
+ return done.PhiAt(0);
+}
+
Node* EffectControlLinearizer::LowerStringComparison(Callable const& callable,
Node* node) {
Node* lhs = node->InputAt(0);
@@ -4474,11 +5689,11 @@ Node* EffectControlLinearizer::LowerStringComparison(Callable const& callable,
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), flags, properties);
- return __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs, rhs,
- __ NoContextConstant());
+ return __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs, rhs);
}
Node* EffectControlLinearizer::LowerStringSubstring(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* receiver = node->InputAt(0);
Node* start = ChangeInt32ToIntPtr(node->InputAt(1));
Node* end = ChangeInt32ToIntPtr(node->InputAt(2));
@@ -4491,36 +5706,25 @@ Node* EffectControlLinearizer::LowerStringSubstring(Node* node) {
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), flags, properties);
return __ Call(call_descriptor, __ HeapConstant(callable.code()), receiver,
- start, end, __ NoContextConstant());
-}
-
-Node* EffectControlLinearizer::LowerStringEqual(Node* node) {
- return LowerStringComparison(
- Builtins::CallableFor(isolate(), Builtin::kStringEqual), node);
+ start, end);
}
Node* EffectControlLinearizer::LowerStringLessThan(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
return LowerStringComparison(
Builtins::CallableFor(isolate(), Builtin::kStringLessThan), node);
}
Node* EffectControlLinearizer::LowerStringLessThanOrEqual(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
return LowerStringComparison(
Builtins::CallableFor(isolate(), Builtin::kStringLessThanOrEqual), node);
}
Node* EffectControlLinearizer::LowerBigIntAdd(Node* node, Node* frame_state) {
- Node* lhs = node->InputAt(0);
- Node* rhs = node->InputAt(1);
-
- Callable const callable =
- Builtins::CallableFor(isolate(), Builtin::kBigIntAddNoThrow);
- auto call_descriptor = Linkage::GetStubCallDescriptor(
- graph()->zone(), callable.descriptor(),
- callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
- Operator::kFoldable | Operator::kNoThrow);
- Node* value = __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs,
- rhs, __ NoContextConstant());
+ DCHECK(!v8_flags.turboshaft);
+ Node* value = CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntAddNoThrow);
// Check for exception sentinel: Smi is returned to signal BigIntTooBig.
__ DeoptimizeIf(DeoptimizeReason::kBigIntTooBig, FeedbackSource{},
@@ -4531,17 +5735,9 @@ Node* EffectControlLinearizer::LowerBigIntAdd(Node* node, Node* frame_state) {
Node* EffectControlLinearizer::LowerBigIntSubtract(Node* node,
Node* frame_state) {
- Node* lhs = node->InputAt(0);
- Node* rhs = node->InputAt(1);
-
- Callable const callable =
- Builtins::CallableFor(isolate(), Builtin::kBigIntSubtractNoThrow);
- auto call_descriptor = Linkage::GetStubCallDescriptor(
- graph()->zone(), callable.descriptor(),
- callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
- Operator::kFoldable | Operator::kNoThrow);
- Node* value = __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs,
- rhs, __ NoContextConstant());
+ DCHECK(!v8_flags.turboshaft);
+ Node* value = CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntSubtractNoThrow);
// Check for exception sentinel: Smi is returned to signal BigIntTooBig.
__ DeoptimizeIf(DeoptimizeReason::kBigIntTooBig, FeedbackSource{},
@@ -4552,17 +5748,9 @@ Node* EffectControlLinearizer::LowerBigIntSubtract(Node* node,
Node* EffectControlLinearizer::LowerBigIntMultiply(Node* node,
Node* frame_state) {
- Node* lhs = node->InputAt(0);
- Node* rhs = node->InputAt(1);
-
- Callable const callable =
- Builtins::CallableFor(isolate(), Builtin::kBigIntMultiplyNoThrow);
- auto call_descriptor = Linkage::GetStubCallDescriptor(
- graph()->zone(), callable.descriptor(),
- callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
- Operator::kFoldable | Operator::kNoThrow);
- Node* value = __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs,
- rhs, __ NoContextConstant());
+ DCHECK(!v8_flags.turboshaft);
+ Node* value = CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntMultiplyNoThrow);
auto if_termreq = __ MakeDeferredLabel();
auto done = __ MakeLabel();
@@ -4596,17 +5784,9 @@ Node* EffectControlLinearizer::LowerBigIntMultiply(Node* node,
Node* EffectControlLinearizer::LowerBigIntDivide(Node* node,
Node* frame_state) {
- Node* lhs = node->InputAt(0);
- Node* rhs = node->InputAt(1);
-
- Callable const callable =
- Builtins::CallableFor(isolate(), Builtin::kBigIntDivideNoThrow);
- auto call_descriptor = Linkage::GetStubCallDescriptor(
- graph()->zone(), callable.descriptor(),
- callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
- Operator::kFoldable | Operator::kNoThrow);
- Node* value = __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs,
- rhs, __ NoContextConstant());
+ DCHECK(!v8_flags.turboshaft);
+ Node* value = CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntDivideNoThrow);
auto if_termreq = __ MakeDeferredLabel();
auto done = __ MakeLabel();
@@ -4640,17 +5820,9 @@ Node* EffectControlLinearizer::LowerBigIntDivide(Node* node,
Node* EffectControlLinearizer::LowerBigIntModulus(Node* node,
Node* frame_state) {
- Node* lhs = node->InputAt(0);
- Node* rhs = node->InputAt(1);
-
- Callable const callable =
- Builtins::CallableFor(isolate(), Builtin::kBigIntModulusNoThrow);
- auto call_descriptor = Linkage::GetStubCallDescriptor(
- graph()->zone(), callable.descriptor(),
- callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
- Operator::kFoldable | Operator::kNoThrow);
- Node* value = __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs,
- rhs, __ NoContextConstant());
+ DCHECK(!v8_flags.turboshaft);
+ Node* value = CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntModulusNoThrow);
auto if_termreq = __ MakeDeferredLabel();
auto done = __ MakeLabel();
@@ -4684,17 +5856,54 @@ Node* EffectControlLinearizer::LowerBigIntModulus(Node* node,
Node* EffectControlLinearizer::LowerBigIntBitwiseAnd(Node* node,
Node* frame_state) {
- Node* lhs = node->InputAt(0);
- Node* rhs = node->InputAt(1);
+ DCHECK(!v8_flags.turboshaft);
+ Node* value = CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntBitwiseAndNoThrow);
- Callable const callable =
- Builtins::CallableFor(isolate(), Builtin::kBigIntBitwiseAndNoThrow);
- auto call_descriptor = Linkage::GetStubCallDescriptor(
- graph()->zone(), callable.descriptor(),
- callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
- Operator::kFoldable | Operator::kNoThrow);
- Node* value = __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs,
- rhs, __ NoContextConstant());
+ // Check for exception sentinel: Smi is returned to signal BigIntTooBig.
+ __ DeoptimizeIf(DeoptimizeReason::kBigIntTooBig, FeedbackSource{},
+ ObjectIsSmi(value), frame_state);
+
+ return value;
+}
+
+Node* EffectControlLinearizer::LowerBigIntBitwiseOr(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
+ return CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntBitwiseOrNoThrow);
+}
+
+Node* EffectControlLinearizer::LowerBigIntBitwiseXor(Node* node,
+ Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
+ Node* value = CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntBitwiseXorNoThrow);
+
+ // Check for exception sentinel: Smi is returned to signal BigIntTooBig.
+ __ DeoptimizeIf(DeoptimizeReason::kBigIntTooBig, FeedbackSource{},
+ ObjectIsSmi(value), frame_state);
+
+ return value;
+}
+
+Node* EffectControlLinearizer::LowerBigIntShiftLeft(Node* node,
+ Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
+ Node* value = CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntShiftLeftNoThrow);
+
+ // Check for exception sentinel: Smi is returned to signal BigIntTooBig.
+ __ DeoptimizeIf(DeoptimizeReason::kBigIntTooBig, FeedbackSource{},
+ ObjectIsSmi(value), frame_state);
+
+ return value;
+}
+
+Node* EffectControlLinearizer::LowerBigIntShiftRight(Node* node,
+ Node* frame_state) {
+ DCHECK(!v8_flags.turboshaft);
+ Node* value = CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntShiftRightNoThrow);
// Check for exception sentinel: Smi is returned to signal BigIntTooBig.
__ DeoptimizeIf(DeoptimizeReason::kBigIntTooBig, FeedbackSource{},
@@ -4703,7 +5912,26 @@ Node* EffectControlLinearizer::LowerBigIntBitwiseAnd(Node* node,
return value;
}
+Node* EffectControlLinearizer::LowerBigIntEqual(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
+ return CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntEqual);
+}
+
+Node* EffectControlLinearizer::LowerBigIntLessThan(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
+ return CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntLessThan);
+}
+
+Node* EffectControlLinearizer::LowerBigIntLessThanOrEqual(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
+ return CallBuiltinForBigIntBinop(node->InputAt(0), node->InputAt(1),
+ Builtin::kBigIntLessThanOrEqual);
+}
+
Node* EffectControlLinearizer::LowerBigIntNegate(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Callable const callable =
Builtins::CallableFor(isolate(), Builtin::kBigIntUnaryMinus);
auto call_descriptor = Linkage::GetStubCallDescriptor(
@@ -4794,10 +6022,7 @@ void EffectControlLinearizer::LowerCheckEqualsInternalizedString(
Node* val_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), val_map);
- // Check for the common case of ThinString first.
- __ GotoIf(__ Word32Equal(val_instance_type,
- __ Int32Constant(THIN_ONE_BYTE_STRING_TYPE)),
- &if_thinstring);
+ // ThinString.
__ Branch(
__ Word32Equal(val_instance_type, __ Int32Constant(THIN_STRING_TYPE)),
&if_thinstring, &if_notthinstring);
@@ -4961,6 +6186,26 @@ Node* EffectControlLinearizer::ObjectIsSmi(Node* value) {
__ Int32Constant(kSmiTag));
}
+Node* EffectControlLinearizer::JSAnyIsNotPrimitiveHeapObject(Node* value,
+ Node* value_map) {
+ if (value_map == nullptr) {
+ value_map = __ LoadField(AccessBuilder::ForMap(), value);
+ }
+#if V8_STATIC_ROOTS_BOOL
+ // Assumes only primitive objects and JS_RECEIVER's are passed here.
+ // All primitive object's maps are allocated at the start of the read only
+ // heap. Thus JS_RECEIVER's must have maps with larger (compressed) addresses.
+ return __ Uint32LessThan(
+ __ Int32Constant(InstanceTypeChecker::kNonJsReceiverMapLimit), value_map);
+#else
+ static_assert(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
+ Node* value_instance_type =
+ __ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
+ return __ Uint32LessThanOrEqual(__ Uint32Constant(FIRST_JS_RECEIVER_TYPE),
+ value_instance_type);
+#endif
+}
+
Node* EffectControlLinearizer::SmiMaxValueConstant() {
return __ Int32Constant(Smi::kMaxValue);
}
@@ -5388,13 +6633,45 @@ Node* EffectControlLinearizer::AdaptFastCallArgument(
Node* stack_slot = __ StackSlot(kSize, kAlign);
__ Store(StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
- stack_slot, 0, node);
+ stack_slot, 0, __ BitcastTaggedToWord(node));
return stack_slot;
}
case CTypeInfo::Type::kFloat32: {
return __ TruncateFloat64ToFloat32(node);
}
+ case CTypeInfo::Type::kPointer: {
+ // Check that the value is a HeapObject.
+ Node* const value_is_smi = ObjectIsSmi(node);
+ __ GotoIf(value_is_smi, if_error);
+
+ auto if_null = __ MakeDeferredLabel();
+ auto done = __ MakeLabel(MachineType::PointerRepresentation());
+
+ // Check if the value is null
+ __ GotoIf(__ TaggedEqual(node, __ NullConstant()), &if_null);
+
+ {
+ // Check that the value is a JSExternalObject.
+ Node* const is_external =
+ __ TaggedEqual(__ LoadField(AccessBuilder::ForMap(), node),
+ __ ExternalObjectMapConstant());
+
+ __ GotoIfNot(is_external, if_error);
+
+ Node* external_pointer =
+ __ LoadField(AccessBuilder::ForJSExternalObjectValue(), node);
+
+ __ Goto(&done, external_pointer);
+ }
+
+ // Value is null, signifying a null pointer.
+ __ Bind(&if_null);
+ { __ Goto(&done, __ IntPtrConstant(0)); }
+
+ __ Bind(&done);
+ return done.PhiAt(0);
+ }
case CTypeInfo::Type::kSeqOneByteString: {
// Check that the value is a HeapObject.
Node* value_is_smi = ObjectIsSmi(node);
@@ -5414,9 +6691,10 @@ Node* EffectControlLinearizer::AdaptFastCallArgument(
Node* length_in_bytes =
__ LoadField(AccessBuilder::ForStringLength(), node);
- Node* data_ptr = __ IntPtrAdd(
- node, __ IntPtrConstant(SeqOneByteString::kHeaderSize -
- kHeapObjectTag));
+ Node* data_ptr =
+ __ IntPtrAdd(__ BitcastTaggedToWord(node),
+ __ IntPtrConstant(SeqOneByteString::kHeaderSize -
+ kHeapObjectTag));
constexpr int kAlign = alignof(FastOneByteString);
constexpr int kSize = sizeof(FastOneByteString);
@@ -5455,7 +6733,7 @@ Node* EffectControlLinearizer::AdaptFastCallArgument(
Node* stack_slot = __ StackSlot(kSize, kAlign);
__ Store(StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
- stack_slot, 0, node);
+ stack_slot, 0, __ BitcastTaggedToWord(node));
// Check that the value is a JSArray.
Node* value_map = __ LoadField(AccessBuilder::ForMap(), node);
@@ -5521,7 +6799,7 @@ EffectControlLinearizer::AdaptOverloadedFastCallArgument(
__ Store(StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
- stack_slot, 0, node);
+ stack_slot, 0, __ BitcastTaggedToWord(node));
Node* target_address = __ ExternalConstant(ExternalReference::Create(
c_functions[func_index].address, ref_type));
@@ -5644,6 +6922,8 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
case CTypeInfo::Type::kFloat64:
return ChangeFloat64ToTagged(
c_call_result, CheckForMinusZeroMode::kCheckForMinusZero);
+ case CTypeInfo::Type::kPointer:
+ return BuildAllocateJSExternalObject(c_call_result);
case CTypeInfo::Type::kSeqOneByteString:
case CTypeInfo::Type::kV8Value:
case CTypeInfo::Type::kApiObject:
@@ -5669,6 +6949,7 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
}
Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
Node* object = node->InputAt(0);
Node* index = node->InputAt(1);
Node* zero = __ IntPtrConstant(0);
@@ -5681,8 +6962,6 @@ Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
auto if_double = __ MakeDeferredLabel();
auto done = __ MakeLabel(MachineRepresentation::kTagged);
- auto loaded_field = __ MakeLabel(MachineRepresentation::kTagged);
- auto done_double = __ MakeLabel(MachineRepresentation::kFloat64);
// Check if field is a mutable double field.
__ GotoIfNot(__ IntPtrEqual(__ WordAnd(index, one), zero), &if_double);
@@ -5699,8 +6978,8 @@ Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
Node* offset =
__ IntAdd(__ WordShl(index, __ IntPtrConstant(kTaggedSizeLog2 - 1)),
__ IntPtrConstant(JSObject::kHeaderSize - kHeapObjectTag));
- Node* field = __ Load(MachineType::AnyTagged(), object, offset);
- __ Goto(&loaded_field, field);
+ Node* result = __ Load(MachineType::AnyTagged(), object, offset);
+ __ Goto(&done, result);
}
// The field is located in the properties backing store of {object}.
@@ -5714,8 +6993,8 @@ Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
__ IntPtrConstant(kTaggedSizeLog2 - 1)),
__ IntPtrConstant((FixedArray::kHeaderSize - kTaggedSize) -
kHeapObjectTag));
- Node* field = __ Load(MachineType::AnyTagged(), properties, offset);
- __ Goto(&loaded_field, field);
+ Node* result = __ Load(MachineType::AnyTagged(), properties, offset);
+ __ Goto(&done, result);
}
}
@@ -5723,6 +7002,9 @@ Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
// architectures, or a mutable HeapNumber.
__ Bind(&if_double);
{
+ auto loaded_field = __ MakeLabel(MachineRepresentation::kTagged);
+ auto done_double = __ MakeLabel(MachineRepresentation::kFloat64);
+
index = __ WordSar(index, one);
// Check if field is in-object or out-of-object.
@@ -5750,27 +7032,27 @@ Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
Node* field = __ Load(MachineType::AnyTagged(), properties, offset);
__ Goto(&loaded_field, field);
}
- }
- __ Bind(&loaded_field);
- {
- Node* field = loaded_field.PhiAt(0);
- // We may have transitioned in-place away from double, so check that
- // this is a HeapNumber -- otherwise the load is fine and we don't need
- // to copy anything anyway.
- __ GotoIf(ObjectIsSmi(field), &done, field);
- Node* field_map = __ LoadField(AccessBuilder::ForMap(), field);
- __ GotoIfNot(__ TaggedEqual(field_map, __ HeapNumberMapConstant()), &done,
- field);
-
- Node* value = __ LoadField(AccessBuilder::ForHeapNumberValue(), field);
- __ Goto(&done_double, value);
- }
+ __ Bind(&loaded_field);
+ {
+ Node* field = loaded_field.PhiAt(0);
+ // We may have transitioned in-place away from double, so check that
+ // this is a HeapNumber -- otherwise the load is fine and we don't need
+ // to copy anything anyway.
+ __ GotoIf(ObjectIsSmi(field), &done, field);
+ Node* field_map = __ LoadField(AccessBuilder::ForMap(), field);
+ __ GotoIfNot(__ TaggedEqual(field_map, __ HeapNumberMapConstant()), &done,
+ field);
+
+ Node* value = __ LoadField(AccessBuilder::ForHeapNumberValue(), field);
+ __ Goto(&done_double, value);
+ }
- __ Bind(&done_double);
- {
- Node* result = AllocateHeapNumberWithValue(done_double.PhiAt(0));
- __ Goto(&done, result);
+ __ Bind(&done_double);
+ {
+ Node* result = AllocateHeapNumberWithValue(done_double.PhiAt(0));
+ __ Goto(&done, result);
+ }
}
__ Bind(&done);
@@ -6194,7 +7476,7 @@ void EffectControlLinearizer::LowerTransitionAndStoreNumberElement(Node* node) {
// loop peeling can break this assumption.
__ GotoIf(__ Word32Equal(kind, __ Int32Constant(HOLEY_DOUBLE_ELEMENTS)),
&do_store);
- __ Unreachable(&do_store);
+ __ Unreachable();
}
__ Bind(&transition_smi_array); // deferred code.
@@ -6392,6 +7674,7 @@ Node* EffectControlLinearizer::LowerFoldConstant(Node* node) {
}
Node* EffectControlLinearizer::LowerDoubleArrayMinMax(Node* node) {
+ DCHECK(!v8_flags.turboshaft);
DCHECK(node->opcode() == IrOpcode::kDoubleArrayMin ||
node->opcode() == IrOpcode::kDoubleArrayMax);
@@ -6448,13 +7731,7 @@ Node* EffectControlLinearizer::LowerConvertReceiver(Node* node) {
// Check if {value} is already a JSReceiver.
__ GotoIf(ObjectIsSmi(value), &convert_to_object);
- static_assert(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
- Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
- Node* value_instance_type =
- __ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
- Node* check = __ Uint32LessThan(
- value_instance_type, __ Uint32Constant(FIRST_JS_RECEIVER_TYPE));
- __ GotoIf(check, &convert_to_object);
+ __ GotoIfNot(JSAnyIsNotPrimitiveHeapObject(value), &convert_to_object);
__ Goto(&done_convert, value);
// Wrap the primitive {value} into a JSPrimitiveWrapper.
@@ -6481,13 +7758,7 @@ Node* EffectControlLinearizer::LowerConvertReceiver(Node* node) {
// Check if {value} is already a JSReceiver, or null/undefined.
__ GotoIf(ObjectIsSmi(value), &convert_to_object);
- static_assert(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
- Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
- Node* value_instance_type =
- __ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
- Node* check = __ Uint32LessThan(
- value_instance_type, __ Uint32Constant(FIRST_JS_RECEIVER_TYPE));
- __ GotoIf(check, &convert_to_object);
+ __ GotoIfNot(JSAnyIsNotPrimitiveHeapObject(value), &convert_to_object);
__ Goto(&done_convert, value);
// Wrap the primitive {value} into a JSPrimitiveWrapper.
@@ -7059,17 +8330,75 @@ Node* EffectControlLinearizer::BuildAllocateBigInt(Node* bitfield,
return result;
}
+Node* EffectControlLinearizer::BuildAllocateJSExternalObject(Node* pointer) {
+ auto if_null = __ MakeDeferredLabel();
+ auto done = __ MakeLabel(MachineRepresentation::kTagged);
+
+ // Check if the pointer is a null pointer
+ __ GotoIf(__ WordEqual(pointer, __ IntPtrConstant(0)), &if_null);
+
+ {
+ Node* external =
+ __ Allocate(AllocationType::kYoung,
+ __ IntPtrConstant(JSExternalObject::kHeaderSize));
+ __ StoreField(AccessBuilder::ForMap(), external,
+ __ ExternalObjectMapConstant());
+ Node* empty_fixed_array = __ HeapConstant(factory()->empty_fixed_array());
+ __ StoreField(AccessBuilder::ForJSObjectPropertiesOrHash(), external,
+ empty_fixed_array);
+ __ StoreField(AccessBuilder::ForJSObjectElements(), external,
+ empty_fixed_array);
+
+#ifdef V8_ENABLE_SANDBOX
+ Node* const isolate_ptr =
+ __ ExternalConstant(ExternalReference::isolate_address(isolate()));
+ MachineSignature::Builder builder(graph()->zone(), 1, 2);
+ builder.AddReturn(MachineType::Uint32());
+ builder.AddParam(MachineType::Pointer());
+ builder.AddParam(MachineType::Pointer());
+ Node* allocate_and_initialize_external_pointer_table_entry =
+ __ ExternalConstant(
+ ExternalReference::
+ allocate_and_initialize_external_pointer_table_entry());
+ auto call_descriptor =
+ Linkage::GetSimplifiedCDescriptor(graph()->zone(), builder.Build());
+ Node* handle = __ Call(common()->Call(call_descriptor),
+ allocate_and_initialize_external_pointer_table_entry,
+ isolate_ptr, pointer);
+
+ __ StoreField(AccessBuilder::ForJSExternalObjectPointerHandle(), external,
+ handle);
+#else
+ __ StoreField(AccessBuilder::ForJSExternalObjectValue(), external, pointer);
+#endif // V8_ENABLE_SANDBOX
+ __ Goto(&done, external);
+ }
+
+ // Pointer is null, convert to a null
+ __ Bind(&if_null);
+ { __ Goto(&done, __ NullConstant()); }
+
+ __ Bind(&done);
+ return done.PhiAt(0);
+}
+
#undef __
void LinearizeEffectControl(JSGraph* graph, Schedule* schedule, Zone* temp_zone,
SourcePositionTable* source_positions,
NodeOriginTable* node_origins,
JSHeapBroker* broker) {
- JSGraphAssembler graph_assembler_(graph, temp_zone,
+ StringBuilderOptimizer string_builder_optimizer(graph, schedule, temp_zone,
+ broker);
+ if (v8_flags.turbo_string_builder && !v8_flags.turboshaft) {
+ string_builder_optimizer.Run();
+ }
+ JSGraphAssembler graph_assembler_(broker, graph, temp_zone,
BranchSemantics::kMachine);
EffectControlLinearizer linearizer(graph, schedule, &graph_assembler_,
temp_zone, source_positions, node_origins,
- MaintainSchedule::kDiscard, broker);
+ MaintainSchedule::kDiscard, broker,
+ &string_builder_optimizer);
linearizer.Run();
}