mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 14:32:24 +00:00
e005326c86
C++20 will automatically generate an operator== with reversed operand order, which is ambiguous with the written operator== when one argument is marked const and the other isn't. TEST=ci Change-Id: Ie4f7b293c1e68462f08335f4d4e9a8146dcc0e15 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/329105 Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com> Auto-Submit: Ivan Inozemtsev <iinozemtsev@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
481 lines
18 KiB
C++
481 lines
18 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_SCOPES_H_
|
|
#define RUNTIME_VM_SCOPES_H_
|
|
|
|
#include <limits>
|
|
|
|
#include "platform/assert.h"
|
|
#include "platform/globals.h"
|
|
#include "vm/allocation.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/object.h"
|
|
#include "vm/raw_object.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/token.h"
|
|
|
|
namespace dart {
|
|
|
|
class CompileType;
|
|
class LocalScope;
|
|
class Slot;
|
|
|
|
// Indices of [LocalVariable]s are abstract and have little todo with the
|
|
// actual frame layout!
|
|
//
|
|
// There are generally 4 different kinds of [LocalVariable]s:
|
|
//
|
|
// a) [LocalVariable]s referring to a parameter: The indices for those
|
|
// variables are assigned by the flow graph builder. Parameter n gets
|
|
// assigned the index (function.num_parameters - n - 1). I.e. the last
|
|
// parameter has index 1.
|
|
//
|
|
// b) [LocalVariable]s referring to actual variables in the body of a
|
|
// function (either from Dart code or specially injected ones. The
|
|
// indices of those variables are assigned by the scope builder
|
|
// from 0, -1, ... -(M-1) for M local variables.
|
|
//
|
|
// -> These variables participate in full SSA renaming and can therefore
|
|
// be used with [StoreLocalInstr]s (in addition to [LoadLocal]s).
|
|
//
|
|
// c) [LocalVariable]s referring to values on the expression stack. Those are
|
|
// assigned by the flow graph builder. The indices of those variables are
|
|
// assigned by the flow graph builder (it simulates the expression stack
|
|
// height), they go from -NumVariables - ExpressionHeight.
|
|
//
|
|
// -> These variables participate only partially in SSA renaming and can
|
|
// therefore only be used with [LoadLocalInstr]s and with
|
|
// [StoreLocalInstr]s **where no phis are necessary**.
|
|
//
|
|
// b) [LocalVariable]s referring to captured variables. Those are never
|
|
// loaded/stored directly. Their only purpose is to tell the flow graph
|
|
// builder how many parent links to follow and into which context index to
|
|
// store. The indices of those variables are assigned by the scope
|
|
// builder and they refer to indices into context objects.
|
|
class VariableIndex {
|
|
public:
|
|
static constexpr int kInvalidIndex = std::numeric_limits<int>::min();
|
|
|
|
explicit VariableIndex(int value = kInvalidIndex) : value_(value) {}
|
|
|
|
bool operator==(const VariableIndex& other) const {
|
|
return value_ == other.value_;
|
|
}
|
|
|
|
bool IsValid() const { return value_ != kInvalidIndex; }
|
|
|
|
int value() const { return value_; }
|
|
|
|
private:
|
|
int value_;
|
|
};
|
|
|
|
class LocalVariable : public ZoneAllocated {
|
|
public:
|
|
static constexpr intptr_t kNoKernelOffset = -1;
|
|
|
|
LocalVariable(TokenPosition declaration_pos,
|
|
TokenPosition token_pos,
|
|
const String& name,
|
|
const AbstractType& static_type,
|
|
intptr_t kernel_offset = kNoKernelOffset);
|
|
|
|
LocalVariable(TokenPosition declaration_pos,
|
|
TokenPosition token_pos,
|
|
const String& name,
|
|
const AbstractType& static_type,
|
|
intptr_t kernel_offset,
|
|
CompileType* inferred_type,
|
|
CompileType* inferred_arg_type = nullptr,
|
|
const Object* inferred_arg_value = nullptr)
|
|
: declaration_pos_(declaration_pos),
|
|
token_pos_(token_pos),
|
|
name_(name),
|
|
kernel_offset_(kernel_offset),
|
|
annotations_offset_(kNoKernelOffset),
|
|
owner_(nullptr),
|
|
static_type_(static_type),
|
|
inferred_type_(inferred_type),
|
|
inferred_arg_type_(inferred_arg_type),
|
|
inferred_arg_value_(inferred_arg_value),
|
|
covariance_mode_(kNotCovariant),
|
|
late_init_offset_(0),
|
|
type_check_mode_(kDoTypeCheck),
|
|
index_(),
|
|
is_awaiter_link_(IsAwaiterLink::kNotLink) {
|
|
DEBUG_ASSERT(static_type.IsNotTemporaryScopedHandle());
|
|
ASSERT(static_type.IsFinalized());
|
|
ASSERT(inferred_type != nullptr);
|
|
ASSERT(name.IsSymbol());
|
|
if (IsFilteredIdentifier(name)) {
|
|
set_invisible(true);
|
|
}
|
|
}
|
|
|
|
TokenPosition token_pos() const { return token_pos_; }
|
|
TokenPosition declaration_token_pos() const { return declaration_pos_; }
|
|
const String& name() const { return name_; }
|
|
intptr_t kernel_offset() const { return kernel_offset_; }
|
|
intptr_t annotations_offset() const { return annotations_offset_; }
|
|
LocalScope* owner() const { return owner_; }
|
|
void set_owner(LocalScope* owner) {
|
|
ASSERT(owner_ == nullptr);
|
|
owner_ = owner;
|
|
}
|
|
|
|
void set_annotations_offset(intptr_t offset) {
|
|
annotations_offset_ = offset;
|
|
is_awaiter_link_ = (offset == kNoKernelOffset) ? IsAwaiterLink::kNotLink
|
|
: IsAwaiterLink::kUnknown;
|
|
}
|
|
|
|
const AbstractType& static_type() const { return static_type_; }
|
|
|
|
CompileType* inferred_type() const { return inferred_type_; }
|
|
CompileType* inferred_arg_type() const { return inferred_arg_type_; }
|
|
const Object* inferred_arg_value() const { return inferred_arg_value_; }
|
|
|
|
bool is_final() const { return IsFinalBit::decode(bitfield_); }
|
|
void set_is_final() { bitfield_ = IsFinalBit::update(true, bitfield_); }
|
|
|
|
bool is_captured() const { return IsCapturedBit::decode(bitfield_); }
|
|
void set_is_captured() { bitfield_ = IsCapturedBit::update(true, bitfield_); }
|
|
|
|
bool ComputeIfIsAwaiterLink(const Library& library);
|
|
void set_is_awaiter_link(bool value) {
|
|
is_awaiter_link_ = value ? IsAwaiterLink::kLink : IsAwaiterLink::kNotLink;
|
|
}
|
|
|
|
// Variables marked as forced to stack are skipped and not captured by
|
|
// CaptureLocalVariables - which iterates scope chain between two scopes
|
|
// and indiscriminately marks all variables as captured.
|
|
// TODO(27590) remove the hardcoded list of names from CaptureLocalVariables
|
|
bool is_forced_stack() const { return IsForcedStackBit::decode(bitfield_); }
|
|
void set_is_forced_stack() {
|
|
bitfield_ = IsForcedStackBit::update(true, bitfield_);
|
|
}
|
|
|
|
bool is_late() const { return IsLateBit::decode(bitfield_); }
|
|
void set_is_late() { bitfield_ = IsLateBit::update(true, bitfield_); }
|
|
|
|
intptr_t late_init_offset() const { return late_init_offset_; }
|
|
void set_late_init_offset(intptr_t late_init_offset) {
|
|
late_init_offset_ = late_init_offset;
|
|
}
|
|
|
|
bool is_explicit_covariant_parameter() const {
|
|
return covariance_mode_ == kExplicit;
|
|
}
|
|
void set_is_explicit_covariant_parameter() { covariance_mode_ = kExplicit; }
|
|
|
|
bool needs_covariant_check_in_method() const {
|
|
return covariance_mode_ != kNotCovariant;
|
|
}
|
|
void set_needs_covariant_check_in_method() {
|
|
if (covariance_mode_ == kNotCovariant) {
|
|
covariance_mode_ = kImplicit;
|
|
}
|
|
}
|
|
|
|
enum TypeCheckMode {
|
|
kDoTypeCheck,
|
|
kSkipTypeCheck,
|
|
kTypeCheckedByCaller,
|
|
};
|
|
|
|
// Returns true if this local variable represents a parameter that needs type
|
|
// check when we enter the function.
|
|
bool needs_type_check() const { return (type_check_mode_ == kDoTypeCheck); }
|
|
|
|
// Returns true if this local variable represents a parameter which type is
|
|
// guaranteed by the caller.
|
|
bool was_type_checked_by_caller() const {
|
|
return type_check_mode_ == kTypeCheckedByCaller;
|
|
}
|
|
|
|
TypeCheckMode type_check_mode() const { return type_check_mode_; }
|
|
void set_type_check_mode(TypeCheckMode mode) { type_check_mode_ = mode; }
|
|
|
|
bool HasIndex() const { return index_.IsValid(); }
|
|
VariableIndex index() const {
|
|
ASSERT(HasIndex());
|
|
return index_;
|
|
}
|
|
|
|
// Assign an index to a local.
|
|
void set_index(VariableIndex index) {
|
|
ASSERT(index.IsValid());
|
|
index_ = index;
|
|
}
|
|
|
|
// Invisible variables are not included into LocalVarDescriptors
|
|
// and not displayed in the debugger.
|
|
bool is_invisible() const { return IsInvisibleBit::decode(bitfield_); }
|
|
void set_invisible(bool value) {
|
|
bitfield_ = IsInvisibleBit::update(value, bitfield_);
|
|
}
|
|
|
|
bool is_captured_parameter() const {
|
|
return IsCapturedParameterBit::decode(bitfield_);
|
|
}
|
|
void set_is_captured_parameter(bool value) {
|
|
bitfield_ = IsCapturedParameterBit::update(value, bitfield_);
|
|
}
|
|
|
|
bool Equals(const LocalVariable& other) const;
|
|
|
|
private:
|
|
// If true, this variable is readonly.
|
|
using IsFinalBit = BitField<uint32_t, bool, 0, 1>;
|
|
// If true, this variable lives in the context, otherwise
|
|
// in the stack frame.
|
|
using IsCapturedBit = BitField<uint32_t, bool, IsFinalBit::kNextBit, 1>;
|
|
using IsInvisibleBit = BitField<uint32_t, bool, IsCapturedBit::kNextBit, 1>;
|
|
using IsCapturedParameterBit =
|
|
BitField<uint32_t, bool, IsInvisibleBit::kNextBit, 1>;
|
|
using IsForcedStackBit =
|
|
BitField<uint32_t, bool, IsCapturedParameterBit::kNextBit, 1>;
|
|
using IsLateBit = BitField<uint32_t, bool, IsForcedStackBit ::kNextBit, 1>;
|
|
|
|
enum CovarianceMode {
|
|
kNotCovariant,
|
|
kImplicit,
|
|
kExplicit,
|
|
};
|
|
|
|
static constexpr int kUninitializedIndex = INT_MIN;
|
|
|
|
static bool IsFilteredIdentifier(const String& name);
|
|
|
|
const TokenPosition declaration_pos_;
|
|
const TokenPosition token_pos_;
|
|
const String& name_;
|
|
const intptr_t kernel_offset_;
|
|
intptr_t annotations_offset_;
|
|
LocalScope* owner_; // Local scope declaring this variable.
|
|
|
|
const AbstractType& static_type_; // Declaration type of local variable.
|
|
|
|
// Inferred variable type.
|
|
CompileType* const inferred_type_;
|
|
// nullptr or inferred type of incoming argument.
|
|
CompileType* const inferred_arg_type_;
|
|
// nullptr or inferred value of incoming argument.
|
|
const Object* const inferred_arg_value_;
|
|
|
|
uint32_t bitfield_ = 0;
|
|
CovarianceMode covariance_mode_;
|
|
intptr_t late_init_offset_;
|
|
TypeCheckMode type_check_mode_;
|
|
VariableIndex index_;
|
|
|
|
enum class IsAwaiterLink {
|
|
kUnknown,
|
|
kNotLink,
|
|
kLink,
|
|
};
|
|
IsAwaiterLink is_awaiter_link_;
|
|
|
|
friend class LocalScope;
|
|
DISALLOW_COPY_AND_ASSIGN(LocalVariable);
|
|
};
|
|
|
|
// Accumulates local variable descriptors while building
|
|
// LocalVarDescriptors object.
|
|
class LocalVarDescriptorsBuilder : public ValueObject {
|
|
public:
|
|
struct VarDesc {
|
|
const String* name;
|
|
UntaggedLocalVarDescriptors::VarInfo info;
|
|
};
|
|
|
|
LocalVarDescriptorsBuilder() : vars_(8) {}
|
|
|
|
// Add variable descriptor.
|
|
void Add(const VarDesc& var_desc) { vars_.Add(var_desc); }
|
|
|
|
// Add all variable descriptors from given [LocalVarDescriptors] object.
|
|
void AddAll(Zone* zone, const LocalVarDescriptors& var_descs);
|
|
|
|
// Record deopt-id -> context-level mappings, using ranges of deopt-ids with
|
|
// the same context-level. [context_level_array] contains (deopt_id,
|
|
// context_level) tuples.
|
|
void AddDeoptIdToContextLevelMappings(
|
|
ZoneGrowableArray<intptr_t>* context_level_array);
|
|
|
|
// Finish building LocalVarDescriptor object.
|
|
LocalVarDescriptorsPtr Done();
|
|
|
|
private:
|
|
GrowableArray<VarDesc> vars_;
|
|
};
|
|
|
|
class LocalScope : public ZoneAllocated {
|
|
public:
|
|
LocalScope(LocalScope* parent, int function_level, int loop_level);
|
|
|
|
LocalScope* parent() const { return parent_; }
|
|
LocalScope* child() const { return child_; }
|
|
LocalScope* sibling() const { return sibling_; }
|
|
int function_level() const { return function_level_; }
|
|
int loop_level() const { return loop_level_; }
|
|
|
|
// Check if this scope is nested within the passed in scope.
|
|
bool IsNestedWithin(LocalScope* scope) const;
|
|
|
|
// The context level is only set in a scope that is either the owner scope of
|
|
// a captured variable or that is the owner scope of a context.
|
|
bool HasContextLevel() const {
|
|
return context_level_ != kUninitializedContextLevel;
|
|
}
|
|
int context_level() const {
|
|
ASSERT(HasContextLevel());
|
|
return context_level_;
|
|
}
|
|
void set_context_level(int context_level) {
|
|
ASSERT(!HasContextLevel());
|
|
ASSERT(context_level != kUninitializedContextLevel);
|
|
context_level_ = context_level;
|
|
}
|
|
|
|
TokenPosition begin_token_pos() const { return begin_token_pos_; }
|
|
void set_begin_token_pos(TokenPosition value) { begin_token_pos_ = value; }
|
|
|
|
TokenPosition end_token_pos() const { return end_token_pos_; }
|
|
void set_end_token_pos(TokenPosition value) { end_token_pos_ = value; }
|
|
|
|
// Return the list of variables allocated in the context and belonging to this
|
|
// scope and to its children at the same loop level.
|
|
const GrowableArray<LocalVariable*>& context_variables() const {
|
|
return context_variables_;
|
|
}
|
|
|
|
const ZoneGrowableArray<const Slot*>& context_slots() const {
|
|
return *context_slots_;
|
|
}
|
|
|
|
// The number of variables allocated in the context and belonging to this
|
|
// scope and to its children at the same loop level.
|
|
int num_context_variables() const { return context_variables().length(); }
|
|
|
|
// Add a variable to the scope. Returns false if a variable with the
|
|
// same name and kernel offset is already present.
|
|
bool AddVariable(LocalVariable* variable);
|
|
|
|
// Add a variable to the scope as a context allocated variable and assigns
|
|
// it an index within the context. Does not check if the scope already
|
|
// contains this variable or a variable with the same name.
|
|
void AddContextVariable(LocalVariable* var);
|
|
|
|
// Insert a formal parameter variable to the scope at the given position,
|
|
// possibly in front of aliases already added with AddVariable.
|
|
// Returns false if a variable with the same name is already present.
|
|
bool InsertParameterAt(intptr_t pos, LocalVariable* parameter);
|
|
|
|
// Lookup a variable in this scope only.
|
|
LocalVariable* LocalLookupVariable(const String& name,
|
|
intptr_t kernel_offset) const;
|
|
|
|
// Lookup a variable in this scope and its parents. If the variable
|
|
// is found in a parent scope and 'test_only' is not true, we insert
|
|
// aliases of the variable in the current and intermediate scopes up to
|
|
// the declaration scope in order to detect "used before declared" errors.
|
|
// We mark a variable as 'captured' when applicable.
|
|
LocalVariable* LookupVariable(const String& name,
|
|
intptr_t kernel_offset,
|
|
bool test_only);
|
|
|
|
// Lookup a variable in this scope and its parents by name.
|
|
LocalVariable* LookupVariableByName(const String& name);
|
|
|
|
// Mark this variable as captured by this scope.
|
|
void CaptureVariable(LocalVariable* variable);
|
|
|
|
// Accessing the variables in the scope.
|
|
intptr_t num_variables() const { return variables_.length(); }
|
|
LocalVariable* VariableAt(intptr_t index) const {
|
|
ASSERT((index >= 0) && (index < variables_.length()));
|
|
return variables_[index];
|
|
}
|
|
|
|
// Count the captured variables belonging to outer scopes and referenced in
|
|
// this local scope.
|
|
int NumCapturedVariables() const;
|
|
|
|
// Allocate both captured and non-captured variables declared in this scope
|
|
// and in its children scopes of the same function level. Allocating means
|
|
// assigning a frame slot index or a context slot index.
|
|
// Parameters to be allocated in the frame must all appear in the top scope
|
|
// and not in its children (we do not yet handle register parameters).
|
|
// Locals must be listed after parameters in top scope and in its children.
|
|
// Two locals in different sibling scopes may share the same frame slot.
|
|
//
|
|
// Return the index of the next available frame slot.
|
|
VariableIndex AllocateVariables(const Function& function,
|
|
VariableIndex first_parameter_index,
|
|
int num_parameters,
|
|
VariableIndex first_local_index,
|
|
LocalScope* context_owner,
|
|
bool* found_captured_variables);
|
|
|
|
// Creates variable info for the scope and all its nested scopes.
|
|
// Must be called after AllocateVariables() has been called.
|
|
LocalVarDescriptorsPtr GetVarDescriptors(
|
|
const Function& func,
|
|
ZoneGrowableArray<intptr_t>* context_level_array);
|
|
|
|
// Create a ContextScope object describing all captured variables referenced
|
|
// from this scope and belonging to outer scopes.
|
|
ContextScopePtr PreserveOuterScope(const Function& function,
|
|
intptr_t current_context_level) const;
|
|
|
|
// Mark all local variables that are accessible from this scope up to
|
|
// top_scope (included) as captured unless they are marked as forced to stack.
|
|
void CaptureLocalVariables(LocalScope* top_scope);
|
|
|
|
// Creates a LocalScope representing the outer scope of a local function to be
|
|
// compiled. This outer scope contains the variables captured by the function
|
|
// as specified by the given ContextScope, which was created during the
|
|
// compilation of the enclosing function.
|
|
static LocalScope* RestoreOuterScope(const ContextScope& context_scope);
|
|
|
|
// Create a ContextScope object which will capture "this" for an implicit
|
|
// closure object.
|
|
static ContextScopePtr CreateImplicitClosureScope(const Function& func);
|
|
|
|
private:
|
|
// Allocate the variable in the current context, possibly updating the current
|
|
// context owner scope, if the variable is the first one to be allocated at
|
|
// this loop level.
|
|
// The variable may belong to this scope or to any of its children, but at the
|
|
// same loop level.
|
|
void AllocateContextVariable(LocalVariable* variable,
|
|
LocalScope** context_owner);
|
|
|
|
void CollectLocalVariables(LocalVarDescriptorsBuilder* vars,
|
|
int16_t* scope_id);
|
|
|
|
static constexpr int kUninitializedContextLevel = INT_MIN;
|
|
LocalScope* parent_;
|
|
LocalScope* child_;
|
|
LocalScope* sibling_;
|
|
int function_level_; // Reflects the nesting level of local functions.
|
|
int loop_level_; // Reflects the loop nesting level.
|
|
int context_level_; // Reflects the level of the runtime context.
|
|
TokenPosition begin_token_pos_; // Token index of beginning of scope.
|
|
TokenPosition end_token_pos_; // Token index of end of scope.
|
|
GrowableArray<LocalVariable*> variables_;
|
|
|
|
// List of variables allocated into the context which is owned by this scope,
|
|
// and their corresponding Slots.
|
|
GrowableArray<LocalVariable*> context_variables_;
|
|
ZoneGrowableArray<const Slot*>* context_slots_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(LocalScope);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_SCOPES_H_
|