4c6f2e96f1
... to precisely express which guarantees does this operator provide. Drive-by-fix: use it for other tag-checking predicates in CSA. Bug: v8:9396 Change-Id: Ifee22922ac02ec8866038be1a97625a32638d521 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1795504 Commit-Queue: Igor Sheludko <ishell@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#63681}
482 lines
18 KiB
C++
482 lines
18 KiB
C++
// Copyright 2016 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef V8_COMPILER_GRAPH_ASSEMBLER_H_
|
|
#define V8_COMPILER_GRAPH_ASSEMBLER_H_
|
|
|
|
#include "src/compiler/feedback-source.h"
|
|
#include "src/compiler/js-graph.h"
|
|
#include "src/compiler/node.h"
|
|
#include "src/compiler/simplified-operator.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
class JSGraph;
|
|
class Graph;
|
|
|
|
namespace compiler {
|
|
|
|
#define PURE_ASSEMBLER_MACH_UNOP_LIST(V) \
|
|
V(ChangeInt32ToInt64) \
|
|
V(ChangeInt32ToFloat64) \
|
|
V(ChangeInt64ToFloat64) \
|
|
V(ChangeUint32ToFloat64) \
|
|
V(ChangeUint32ToUint64) \
|
|
V(ChangeFloat64ToInt32) \
|
|
V(ChangeFloat64ToInt64) \
|
|
V(ChangeFloat64ToUint32) \
|
|
V(TruncateInt64ToInt32) \
|
|
V(RoundFloat64ToInt32) \
|
|
V(TruncateFloat64ToInt64) \
|
|
V(TruncateFloat64ToWord32) \
|
|
V(Float64ExtractLowWord32) \
|
|
V(Float64ExtractHighWord32) \
|
|
V(BitcastInt32ToFloat32) \
|
|
V(BitcastInt64ToFloat64) \
|
|
V(BitcastFloat32ToInt32) \
|
|
V(BitcastFloat64ToInt64) \
|
|
V(Float64Abs) \
|
|
V(Word32ReverseBytes) \
|
|
V(Word64ReverseBytes) \
|
|
V(Float64SilenceNaN) \
|
|
V(ChangeCompressedToTagged) \
|
|
V(ChangeTaggedToCompressed) \
|
|
V(ChangeTaggedSignedToCompressedSigned) \
|
|
V(ChangeCompressedSignedToTaggedSigned) \
|
|
V(ChangeTaggedPointerToCompressedPointer) \
|
|
V(ChangeCompressedPointerToTaggedPointer)
|
|
#define PURE_ASSEMBLER_MACH_BINOP_LIST(V) \
|
|
V(WordShl) \
|
|
V(WordSar) \
|
|
V(WordAnd) \
|
|
V(Word32Or) \
|
|
V(Word32And) \
|
|
V(Word32Xor) \
|
|
V(Word32Shr) \
|
|
V(Word32Shl) \
|
|
V(Word32Sar) \
|
|
V(Word64And) \
|
|
V(IntAdd) \
|
|
V(IntSub) \
|
|
V(IntMul) \
|
|
V(IntLessThan) \
|
|
V(UintLessThan) \
|
|
V(Int32Add) \
|
|
V(Int32Sub) \
|
|
V(Int32Mul) \
|
|
V(Int32LessThanOrEqual) \
|
|
V(Uint32LessThan) \
|
|
V(Uint32LessThanOrEqual) \
|
|
V(Uint64LessThan) \
|
|
V(Uint64LessThanOrEqual) \
|
|
V(Int32LessThan) \
|
|
V(Int64Sub) \
|
|
V(Float64Add) \
|
|
V(Float64Sub) \
|
|
V(Float64Div) \
|
|
V(Float64Mod) \
|
|
V(Float64Equal) \
|
|
V(Float64LessThan) \
|
|
V(Float64LessThanOrEqual) \
|
|
V(Float64InsertLowWord32) \
|
|
V(Float64InsertHighWord32) \
|
|
V(Word32Equal) \
|
|
V(Word64Equal) \
|
|
V(WordEqual)
|
|
|
|
#define CHECKED_ASSEMBLER_MACH_BINOP_LIST(V) \
|
|
V(Int32AddWithOverflow) \
|
|
V(Int32SubWithOverflow) \
|
|
V(Int32MulWithOverflow) \
|
|
V(Int32Mod) \
|
|
V(Int32Div) \
|
|
V(Uint32Mod) \
|
|
V(Uint32Div)
|
|
|
|
#define JSGRAPH_SINGLETON_CONSTANT_LIST(V) \
|
|
V(TrueConstant) \
|
|
V(FalseConstant) \
|
|
V(NullConstant) \
|
|
V(BigIntMapConstant) \
|
|
V(BooleanMapConstant) \
|
|
V(HeapNumberMapConstant) \
|
|
V(NoContextConstant) \
|
|
V(EmptyStringConstant) \
|
|
V(UndefinedConstant) \
|
|
V(TheHoleConstant) \
|
|
V(FixedArrayMapConstant) \
|
|
V(FixedDoubleArrayMapConstant) \
|
|
V(ToNumberBuiltinConstant) \
|
|
V(AllocateInYoungGenerationStubConstant) \
|
|
V(AllocateRegularInYoungGenerationStubConstant) \
|
|
V(AllocateInOldGenerationStubConstant) \
|
|
V(AllocateRegularInOldGenerationStubConstant)
|
|
|
|
class GraphAssembler;
|
|
|
|
enum class GraphAssemblerLabelType { kDeferred, kNonDeferred, kLoop };
|
|
|
|
// Label with statically known count of incoming branches and phis.
|
|
template <size_t VarCount>
|
|
class GraphAssemblerLabel {
|
|
public:
|
|
Node* PhiAt(size_t index);
|
|
|
|
template <typename... Reps>
|
|
explicit GraphAssemblerLabel(GraphAssemblerLabelType type, Reps... reps)
|
|
: type_(type) {
|
|
STATIC_ASSERT(VarCount == sizeof...(reps));
|
|
MachineRepresentation reps_array[] = {MachineRepresentation::kNone,
|
|
reps...};
|
|
for (size_t i = 0; i < VarCount; i++) {
|
|
representations_[i] = reps_array[i + 1];
|
|
}
|
|
}
|
|
|
|
~GraphAssemblerLabel() { DCHECK(IsBound() || merged_count_ == 0); }
|
|
|
|
private:
|
|
friend class GraphAssembler;
|
|
|
|
void SetBound() {
|
|
DCHECK(!IsBound());
|
|
is_bound_ = true;
|
|
}
|
|
bool IsBound() const { return is_bound_; }
|
|
bool IsDeferred() const {
|
|
return type_ == GraphAssemblerLabelType::kDeferred;
|
|
}
|
|
bool IsLoop() const { return type_ == GraphAssemblerLabelType::kLoop; }
|
|
|
|
bool is_bound_ = false;
|
|
GraphAssemblerLabelType const type_;
|
|
size_t merged_count_ = 0;
|
|
Node* effect_;
|
|
Node* control_;
|
|
Node* bindings_[VarCount + 1];
|
|
MachineRepresentation representations_[VarCount + 1];
|
|
};
|
|
|
|
class GraphAssembler {
|
|
public:
|
|
GraphAssembler(JSGraph* jsgraph, Node* effect, Node* control, Zone* zone);
|
|
|
|
void Reset(Node* effect, Node* control);
|
|
|
|
// Create label.
|
|
template <typename... Reps>
|
|
static GraphAssemblerLabel<sizeof...(Reps)> MakeLabelFor(
|
|
GraphAssemblerLabelType type, Reps... reps) {
|
|
return GraphAssemblerLabel<sizeof...(Reps)>(type, reps...);
|
|
}
|
|
|
|
// Convenience wrapper for creating non-deferred labels.
|
|
template <typename... Reps>
|
|
static GraphAssemblerLabel<sizeof...(Reps)> MakeLabel(Reps... reps) {
|
|
return MakeLabelFor(GraphAssemblerLabelType::kNonDeferred, reps...);
|
|
}
|
|
|
|
// Convenience wrapper for creating loop labels.
|
|
template <typename... Reps>
|
|
static GraphAssemblerLabel<sizeof...(Reps)> MakeLoopLabel(Reps... reps) {
|
|
return MakeLabelFor(GraphAssemblerLabelType::kLoop, reps...);
|
|
}
|
|
|
|
// Convenience wrapper for creating deferred labels.
|
|
template <typename... Reps>
|
|
static GraphAssemblerLabel<sizeof...(Reps)> MakeDeferredLabel(Reps... reps) {
|
|
return MakeLabelFor(GraphAssemblerLabelType::kDeferred, reps...);
|
|
}
|
|
|
|
// Value creation.
|
|
Node* IntPtrConstant(intptr_t value);
|
|
Node* Uint32Constant(int32_t value);
|
|
Node* Int32Constant(int32_t value);
|
|
Node* Int64Constant(int64_t value);
|
|
Node* UniqueIntPtrConstant(intptr_t value);
|
|
Node* SmiConstant(int32_t value);
|
|
Node* Float64Constant(double value);
|
|
Node* Projection(int index, Node* value);
|
|
Node* HeapConstant(Handle<HeapObject> object);
|
|
Node* NumberConstant(double value);
|
|
Node* CEntryStubConstant(int result_size);
|
|
Node* ExternalConstant(ExternalReference ref);
|
|
|
|
Node* LoadFramePointer();
|
|
|
|
#define SINGLETON_CONST_DECL(Name) Node* Name();
|
|
JSGRAPH_SINGLETON_CONSTANT_LIST(SINGLETON_CONST_DECL)
|
|
#undef SINGLETON_CONST_DECL
|
|
|
|
#define PURE_UNOP_DECL(Name) Node* Name(Node* input);
|
|
PURE_ASSEMBLER_MACH_UNOP_LIST(PURE_UNOP_DECL)
|
|
#undef PURE_UNOP_DECL
|
|
|
|
#define BINOP_DECL(Name) Node* Name(Node* left, Node* right);
|
|
PURE_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL)
|
|
CHECKED_ASSEMBLER_MACH_BINOP_LIST(BINOP_DECL)
|
|
#undef BINOP_DECL
|
|
|
|
// Debugging
|
|
Node* DebugBreak();
|
|
|
|
Node* Unreachable();
|
|
|
|
Node* IntPtrEqual(Node* left, Node* right);
|
|
Node* TaggedEqual(Node* left, Node* right);
|
|
|
|
Node* Float64RoundDown(Node* value);
|
|
Node* Float64RoundTruncate(Node* value);
|
|
|
|
Node* ToNumber(Node* value);
|
|
Node* BitcastWordToTagged(Node* value);
|
|
Node* BitcastTaggedToWord(Node* value);
|
|
Node* BitcastTaggedToWordForTagAndSmiBits(Node* value);
|
|
Node* Allocate(AllocationType allocation, Node* size);
|
|
Node* LoadField(FieldAccess const&, Node* object);
|
|
Node* LoadElement(ElementAccess const&, Node* object, Node* index);
|
|
Node* StoreField(FieldAccess const&, Node* object, Node* value);
|
|
Node* StoreElement(ElementAccess const&, Node* object, Node* index,
|
|
Node* value);
|
|
|
|
Node* Store(StoreRepresentation rep, Node* object, Node* offset, Node* value);
|
|
Node* Load(MachineType type, Node* object, Node* offset);
|
|
|
|
Node* StoreUnaligned(MachineRepresentation rep, Node* object, Node* offset,
|
|
Node* value);
|
|
Node* LoadUnaligned(MachineType type, Node* object, Node* offset);
|
|
|
|
Node* Retain(Node* buffer);
|
|
Node* UnsafePointerAdd(Node* base, Node* external);
|
|
|
|
Node* Word32PoisonOnSpeculation(Node* value);
|
|
|
|
Node* DeoptimizeIf(
|
|
DeoptimizeReason reason, FeedbackSource const& feedback, Node* condition,
|
|
Node* frame_state,
|
|
IsSafetyCheck is_safety_check = IsSafetyCheck::kSafetyCheck);
|
|
Node* DeoptimizeIfNot(
|
|
DeoptimizeReason reason, FeedbackSource const& feedback, Node* condition,
|
|
Node* frame_state,
|
|
IsSafetyCheck is_safety_check = IsSafetyCheck::kSafetyCheck);
|
|
template <typename... Args>
|
|
Node* Call(const CallDescriptor* call_descriptor, Args... args);
|
|
template <typename... Args>
|
|
Node* Call(const Operator* op, Args... args);
|
|
|
|
// Basic control operations.
|
|
template <size_t VarCount>
|
|
void Bind(GraphAssemblerLabel<VarCount>* label);
|
|
|
|
template <typename... Vars>
|
|
void Goto(GraphAssemblerLabel<sizeof...(Vars)>* label, Vars...);
|
|
|
|
void Branch(Node* condition, GraphAssemblerLabel<0u>* if_true,
|
|
GraphAssemblerLabel<0u>* if_false,
|
|
IsSafetyCheck is_safety_check = IsSafetyCheck::kNoSafetyCheck);
|
|
|
|
// Control helpers.
|
|
// {GotoIf(c, l)} is equivalent to {Branch(c, l, templ);Bind(templ)}.
|
|
template <typename... Vars>
|
|
void GotoIf(Node* condition, GraphAssemblerLabel<sizeof...(Vars)>* label,
|
|
Vars...);
|
|
|
|
// {GotoIfNot(c, l)} is equivalent to {Branch(c, templ, l);Bind(templ)}.
|
|
template <typename... Vars>
|
|
void GotoIfNot(Node* condition, GraphAssemblerLabel<sizeof...(Vars)>* label,
|
|
Vars...);
|
|
|
|
// Extractors (should be only used when destructing/resetting the assembler).
|
|
Node* ExtractCurrentControl();
|
|
Node* ExtractCurrentEffect();
|
|
|
|
private:
|
|
// Adds a decompression node if pointer compression is enabled and the
|
|
// representation loaded is a compressed one. To be used after loads.
|
|
Node* InsertDecompressionIfNeeded(MachineRepresentation rep, Node* value);
|
|
// Adds a compression node if pointer compression is enabled and the
|
|
// representation to be stored is a compressed one. To be used before stores.
|
|
Node* InsertCompressionIfNeeded(MachineRepresentation rep, Node* value);
|
|
template <typename... Vars>
|
|
void MergeState(GraphAssemblerLabel<sizeof...(Vars)>* label, Vars... vars);
|
|
|
|
Operator const* ToNumberOperator();
|
|
|
|
JSGraph* jsgraph() const { return jsgraph_; }
|
|
Isolate* isolate() const { return jsgraph_->isolate(); }
|
|
Graph* graph() const { return jsgraph_->graph(); }
|
|
Zone* temp_zone() const { return temp_zone_; }
|
|
CommonOperatorBuilder* common() const { return jsgraph()->common(); }
|
|
MachineOperatorBuilder* machine() const { return jsgraph()->machine(); }
|
|
SimplifiedOperatorBuilder* simplified() const {
|
|
return jsgraph()->simplified();
|
|
}
|
|
|
|
SetOncePointer<Operator const> to_number_operator_;
|
|
Zone* temp_zone_;
|
|
JSGraph* jsgraph_;
|
|
Node* current_effect_;
|
|
Node* current_control_;
|
|
};
|
|
|
|
template <size_t VarCount>
|
|
Node* GraphAssemblerLabel<VarCount>::PhiAt(size_t index) {
|
|
DCHECK(IsBound());
|
|
DCHECK_LT(index, VarCount);
|
|
return bindings_[index];
|
|
}
|
|
|
|
template <typename... Vars>
|
|
void GraphAssembler::MergeState(GraphAssemblerLabel<sizeof...(Vars)>* label,
|
|
Vars... vars) {
|
|
int merged_count = static_cast<int>(label->merged_count_);
|
|
Node* var_array[] = {nullptr, vars...};
|
|
if (label->IsLoop()) {
|
|
if (merged_count == 0) {
|
|
DCHECK(!label->IsBound());
|
|
label->control_ = graph()->NewNode(common()->Loop(2), current_control_,
|
|
current_control_);
|
|
label->effect_ = graph()->NewNode(common()->EffectPhi(2), current_effect_,
|
|
current_effect_, label->control_);
|
|
Node* terminate = graph()->NewNode(common()->Terminate(), label->effect_,
|
|
label->control_);
|
|
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
|
|
for (size_t i = 0; i < sizeof...(vars); i++) {
|
|
label->bindings_[i] = graph()->NewNode(
|
|
common()->Phi(label->representations_[i], 2), var_array[i + 1],
|
|
var_array[i + 1], label->control_);
|
|
}
|
|
} else {
|
|
DCHECK(label->IsBound());
|
|
DCHECK_EQ(1, merged_count);
|
|
label->control_->ReplaceInput(1, current_control_);
|
|
label->effect_->ReplaceInput(1, current_effect_);
|
|
for (size_t i = 0; i < sizeof...(vars); i++) {
|
|
label->bindings_[i]->ReplaceInput(1, var_array[i + 1]);
|
|
}
|
|
}
|
|
} else {
|
|
DCHECK(!label->IsBound());
|
|
if (merged_count == 0) {
|
|
// Just set the control, effect and variables directly.
|
|
DCHECK(!label->IsBound());
|
|
label->control_ = current_control_;
|
|
label->effect_ = current_effect_;
|
|
for (size_t i = 0; i < sizeof...(vars); i++) {
|
|
label->bindings_[i] = var_array[i + 1];
|
|
}
|
|
} else if (merged_count == 1) {
|
|
// Create merge, effect phi and a phi for each variable.
|
|
label->control_ = graph()->NewNode(common()->Merge(2), label->control_,
|
|
current_control_);
|
|
label->effect_ = graph()->NewNode(common()->EffectPhi(2), label->effect_,
|
|
current_effect_, label->control_);
|
|
for (size_t i = 0; i < sizeof...(vars); i++) {
|
|
label->bindings_[i] = graph()->NewNode(
|
|
common()->Phi(label->representations_[i], 2), label->bindings_[i],
|
|
var_array[i + 1], label->control_);
|
|
}
|
|
} else {
|
|
// Append to the merge, effect phi and phis.
|
|
DCHECK_EQ(IrOpcode::kMerge, label->control_->opcode());
|
|
label->control_->AppendInput(graph()->zone(), current_control_);
|
|
NodeProperties::ChangeOp(label->control_,
|
|
common()->Merge(merged_count + 1));
|
|
|
|
DCHECK_EQ(IrOpcode::kEffectPhi, label->effect_->opcode());
|
|
label->effect_->ReplaceInput(merged_count, current_effect_);
|
|
label->effect_->AppendInput(graph()->zone(), label->control_);
|
|
NodeProperties::ChangeOp(label->effect_,
|
|
common()->EffectPhi(merged_count + 1));
|
|
|
|
for (size_t i = 0; i < sizeof...(vars); i++) {
|
|
DCHECK_EQ(IrOpcode::kPhi, label->bindings_[i]->opcode());
|
|
label->bindings_[i]->ReplaceInput(merged_count, var_array[i + 1]);
|
|
label->bindings_[i]->AppendInput(graph()->zone(), label->control_);
|
|
NodeProperties::ChangeOp(
|
|
label->bindings_[i],
|
|
common()->Phi(label->representations_[i], merged_count + 1));
|
|
}
|
|
}
|
|
}
|
|
label->merged_count_++;
|
|
}
|
|
|
|
template <size_t VarCount>
|
|
void GraphAssembler::Bind(GraphAssemblerLabel<VarCount>* label) {
|
|
DCHECK_NULL(current_control_);
|
|
DCHECK_NULL(current_effect_);
|
|
DCHECK_LT(0, label->merged_count_);
|
|
|
|
current_control_ = label->control_;
|
|
current_effect_ = label->effect_;
|
|
|
|
label->SetBound();
|
|
}
|
|
|
|
template <typename... Vars>
|
|
void GraphAssembler::Goto(GraphAssemblerLabel<sizeof...(Vars)>* label,
|
|
Vars... vars) {
|
|
DCHECK_NOT_NULL(current_control_);
|
|
DCHECK_NOT_NULL(current_effect_);
|
|
MergeState(label, vars...);
|
|
current_control_ = nullptr;
|
|
current_effect_ = nullptr;
|
|
}
|
|
|
|
template <typename... Vars>
|
|
void GraphAssembler::GotoIf(Node* condition,
|
|
GraphAssemblerLabel<sizeof...(Vars)>* label,
|
|
Vars... vars) {
|
|
BranchHint hint =
|
|
label->IsDeferred() ? BranchHint::kFalse : BranchHint::kNone;
|
|
Node* branch =
|
|
graph()->NewNode(common()->Branch(hint), condition, current_control_);
|
|
|
|
current_control_ = graph()->NewNode(common()->IfTrue(), branch);
|
|
MergeState(label, vars...);
|
|
|
|
current_control_ = graph()->NewNode(common()->IfFalse(), branch);
|
|
}
|
|
|
|
template <typename... Vars>
|
|
void GraphAssembler::GotoIfNot(Node* condition,
|
|
GraphAssemblerLabel<sizeof...(Vars)>* label,
|
|
Vars... vars) {
|
|
BranchHint hint = label->IsDeferred() ? BranchHint::kTrue : BranchHint::kNone;
|
|
Node* branch =
|
|
graph()->NewNode(common()->Branch(hint), condition, current_control_);
|
|
|
|
current_control_ = graph()->NewNode(common()->IfFalse(), branch);
|
|
MergeState(label, vars...);
|
|
|
|
current_control_ = graph()->NewNode(common()->IfTrue(), branch);
|
|
}
|
|
|
|
template <typename... Args>
|
|
Node* GraphAssembler::Call(const CallDescriptor* call_descriptor,
|
|
Args... args) {
|
|
const Operator* op = common()->Call(call_descriptor);
|
|
return Call(op, args...);
|
|
}
|
|
|
|
template <typename... Args>
|
|
Node* GraphAssembler::Call(const Operator* op, Args... args) {
|
|
DCHECK_EQ(IrOpcode::kCall, op->opcode());
|
|
Node* args_array[] = {args..., current_effect_, current_control_};
|
|
int size = static_cast<int>(sizeof...(args)) + op->EffectInputCount() +
|
|
op->ControlInputCount();
|
|
Node* call = graph()->NewNode(op, size, args_array);
|
|
DCHECK_EQ(0, op->ControlOutputCount());
|
|
current_effect_ = call;
|
|
return call;
|
|
}
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|
|
|
|
#endif // V8_COMPILER_GRAPH_ASSEMBLER_H_
|