Files
sdk/runtime/vm/compiler/frontend/kernel_to_il.h
T
Daco Harkes 5380fa5d40 [vm/ffi] Introduce CCallInstr and replace handle instructions
The CCallInstr takes unboxed word-sized integers and does a call to C
without any VM transitions.

This can be used for the following.
1) Leaf runtime entries. This CL removes the EnterHandleScope,
   ExitHandleScope, and AllocateHandle instructions.
2) Compilation of isolate independent code where all definitions in IL
   are unboxed integers.

Currently, the CCallInstr expects all it's arguments and the return type
to be word-sized.

This CL also:
- Adds loadImmediate and CCall with a register for each assembler.
- Adds a CSE pass to force-optimized so that LoadThreadInstr gets de-
  duplicated.

TEST=tests/ffi(_2)/*

Change-Id: I67c6aaa1b7d6aa7d60e274477686a54e5a331216
Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-debug-simriscv64-try,vm-kernel-precomp-linux-debug-simriscv64-try,vm-kernel-nnbd-mac-debug-arm64-try,vm-kernel-nnbd-mac-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/246240
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
2022-05-30 20:58:03 +00:00

951 lines
35 KiB
C++

// Copyright (c) 2016, 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_COMPILER_FRONTEND_KERNEL_TO_IL_H_
#define RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TO_IL_H_
#if defined(DART_PRECOMPILED_RUNTIME)
#error "AOT runtime should not use compiler sources (including header files)"
#endif // defined(DART_PRECOMPILED_RUNTIME)
#include "vm/growable_array.h"
#include "vm/hash_map.h"
#include "vm/compiler/backend/flow_graph.h"
#include "vm/compiler/backend/il.h"
#include "vm/compiler/ffi/marshaller.h"
#include "vm/compiler/ffi/native_type.h"
#include "vm/compiler/frontend/base_flow_graph_builder.h"
#include "vm/compiler/frontend/kernel_translation_helper.h"
#include "vm/compiler/frontend/scope_builder.h"
namespace dart {
class InlineExitCollector;
namespace kernel {
class StreamingFlowGraphBuilder;
struct InferredTypeMetadata;
class BreakableBlock;
class CatchBlock;
class FlowGraphBuilder;
class SwitchBlock;
class TryCatchBlock;
class TryFinallyBlock;
struct YieldContinuation {
Instruction* entry;
intptr_t try_index;
YieldContinuation(Instruction* entry, intptr_t try_index)
: entry(entry), try_index(try_index) {}
YieldContinuation() : entry(NULL), try_index(kInvalidTryIndex) {}
};
enum class TypeChecksToBuild {
kCheckAllTypeParameterBounds,
kCheckNonCovariantTypeParameterBounds,
kCheckCovariantTypeParameterBounds,
};
class FlowGraphBuilder : public BaseFlowGraphBuilder {
public:
FlowGraphBuilder(ParsedFunction* parsed_function,
ZoneGrowableArray<const ICData*>* ic_data_array,
ZoneGrowableArray<intptr_t>* context_level_array,
InlineExitCollector* exit_collector,
bool optimizing,
intptr_t osr_id,
intptr_t first_block_id = 1,
bool inlining_unchecked_entry = false);
virtual ~FlowGraphBuilder();
FlowGraph* BuildGraph();
// Returns true if given [function] is recognized for flow
// graph building and its body is expressed in a custom-built IL.
static bool IsRecognizedMethodForFlowGraph(const Function& function);
private:
BlockEntryInstr* BuildPrologue(BlockEntryInstr* normal_entry,
PrologueInfo* prologue_info);
// Return names of optional named parameters of [function].
ArrayPtr GetOptionalParameterNames(const Function& function);
// Generate fragment which pushes all explicit parameters of [function].
Fragment PushExplicitParameters(
const Function& function,
const Function& target = Function::null_function());
FlowGraph* BuildGraphOfMethodExtractor(const Function& method);
FlowGraph* BuildGraphOfNoSuchMethodDispatcher(const Function& function);
struct ClosureCallInfo;
// Tests whether the closure function is generic and branches to the
// appropriate fragment.
Fragment TestClosureFunctionGeneric(const ClosureCallInfo& info,
Fragment generic,
Fragment not_generic);
// Tests whether the function parameter at the given index is required and
// branches to the appropriate fragment. Loads the parameter index to
// check from info.vars->current_param_index.
Fragment TestClosureFunctionNamedParameterRequired(
const ClosureCallInfo& info,
Fragment set,
Fragment not_set);
// Builds a fragment that, if there are no provided function type arguments,
// calculates the appropriate TAV to use instead. Stores either the provided
// or calculated function type arguments in vars->function_type_args.
Fragment BuildClosureCallDefaultTypeHandling(const ClosureCallInfo& info);
// The BuildClosureCall...Check methods differs from the checks built in the
// PrologueBuilder in that they are built for invoke field dispatchers,
// where the ArgumentsDescriptor is known at compile time but the specific
// closure function is retrieved at runtime.
// Builds checks that the given named arguments have valid argument names
// and, in the case of null safe code, that all required named parameters
// are provided.
Fragment BuildClosureCallNamedArgumentsCheck(const ClosureCallInfo& info);
// Builds checks for checking the arguments of a call are valid for the
// function retrieved at runtime from the closure.
Fragment BuildClosureCallArgumentsValidCheck(const ClosureCallInfo& info);
// Builds checks that the type arguments of a call are consistent with the
// bounds of the closure function type parameters. Assumes that the closure
// function is generic.
Fragment BuildClosureCallTypeArgumentsTypeCheck(const ClosureCallInfo& info);
// Builds checks for type checking a given argument of the closure call using
// parameter information from the closure function retrieved at runtime.
//
// For named arguments, arg_name is a compile-time constant retrieved from
// the saved arguments descriptor. For positional arguments, null is passed.
Fragment BuildClosureCallArgumentTypeCheck(const ClosureCallInfo& info,
LocalVariable* param_index,
intptr_t arg_index,
const String& arg_name);
// Builds checks for type checking the arguments of a call using parameter
// information for the function retrieved at runtime from the closure.
Fragment BuildClosureCallArgumentTypeChecks(const ClosureCallInfo& info);
// Main entry point for building checks.
Fragment BuildDynamicClosureCallChecks(LocalVariable* closure);
FlowGraph* BuildGraphOfInvokeFieldDispatcher(const Function& function);
FlowGraph* BuildGraphOfFfiTrampoline(const Function& function);
FlowGraph* BuildGraphOfFfiCallback(const Function& function);
FlowGraph* BuildGraphOfFfiNative(const Function& function);
Fragment NativeFunctionBody(const Function& function,
LocalVariable* first_parameter);
FlowGraph* BuildGraphOfRecognizedMethod(const Function& function);
Fragment BuildTypedDataViewFactoryConstructor(const Function& function,
classid_t cid);
Fragment BuildTypedDataFactoryConstructor(const Function& function,
classid_t cid);
Fragment EnterScope(intptr_t kernel_offset,
const LocalScope** scope = nullptr);
Fragment ExitScope(intptr_t kernel_offset);
Fragment AdjustContextTo(int depth);
Fragment PushContext(const LocalScope* scope);
Fragment PopContext();
Fragment LoadInstantiatorTypeArguments();
Fragment LoadFunctionTypeArguments();
Fragment TranslateInstantiatedTypeArguments(
const TypeArguments& type_arguments);
Fragment CatchBlockEntry(const Array& handler_types,
intptr_t handler_index,
bool needs_stacktrace,
bool is_synthesized);
Fragment TryCatch(int try_handler_index);
Fragment CheckStackOverflowInPrologue(TokenPosition position);
Fragment CloneContext(const ZoneGrowableArray<const Slot*>& context_slots);
Fragment InstanceCall(
TokenPosition position,
const String& name,
Token::Kind kind,
intptr_t type_args_len,
intptr_t argument_count,
const Array& argument_names,
intptr_t checked_argument_count,
const Function& interface_target = Function::null_function(),
const Function& tearoff_interface_target = Function::null_function(),
const InferredTypeMetadata* result_type = nullptr,
bool use_unchecked_entry = false,
const CallSiteAttributesMetadata* call_site_attrs = nullptr,
bool receiver_is_not_smi = false,
bool is_call_on_this = false);
Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller);
Fragment CCall(
const compiler::ffi::NativeCallingConvention& native_calling_convention);
Fragment CCall(intptr_t num_arguments,
Representation representation = kUnboxedFfiIntPtr);
Fragment RethrowException(TokenPosition position, int catch_try_index);
Fragment LoadLocal(LocalVariable* variable);
Fragment IndirectGoto(intptr_t target_count);
Fragment StoreLateField(const Field& field,
LocalVariable* instance,
LocalVariable* setter_value);
Fragment NativeCall(const String* name, const Function* function);
Fragment Return(
TokenPosition position,
bool omit_result_type_check = false,
intptr_t yield_index = UntaggedPcDescriptors::kInvalidYieldIndex);
void SetResultTypeForStaticCall(StaticCallInstr* call,
const Function& target,
intptr_t argument_count,
const InferredTypeMetadata* result_type);
Fragment StaticCall(TokenPosition position,
const Function& target,
intptr_t argument_count,
ICData::RebindRule rebind_rule);
Fragment StaticCall(TokenPosition position,
const Function& target,
intptr_t argument_count,
const Array& argument_names,
ICData::RebindRule rebind_rule,
const InferredTypeMetadata* result_type = NULL,
intptr_t type_args_len = 0,
bool use_unchecked_entry = false);
Fragment StringInterpolateSingle(TokenPosition position);
Fragment StringInterpolate(TokenPosition position);
Fragment ThrowTypeError();
// [incompatible_arguments] should be true if the NSM is due to a mismatch
// between the provided arguments and the function signature.
Fragment ThrowNoSuchMethodError(const Function& target,
bool incompatible_arguments);
Fragment ThrowLateInitializationError(TokenPosition position,
const char* throw_method_name,
const String& name);
Fragment BuildImplicitClosureCreation(const Function& target);
Fragment EvaluateAssertion();
Fragment CheckVariableTypeInCheckedMode(const AbstractType& dst_type,
const String& name_symbol);
Fragment CheckBoolean(TokenPosition position);
Fragment CheckAssignable(
const AbstractType& dst_type,
const String& dst_name,
AssertAssignableInstr::Kind kind = AssertAssignableInstr::kUnknown,
TokenPosition token_pos = TokenPosition::kNoSource);
Fragment AssertAssignableLoadTypeArguments(
TokenPosition position,
const AbstractType& dst_type,
const String& dst_name,
AssertAssignableInstr::Kind kind = AssertAssignableInstr::kUnknown);
Fragment AssertSubtype(TokenPosition position,
const AbstractType& sub_type,
const AbstractType& super_type,
const String& dst_name);
// Assumes destination name, supertype, and subtype are the top of the stack.
Fragment AssertSubtype(TokenPosition position);
bool NeedsDebugStepCheck(const Function& function, TokenPosition position);
bool NeedsDebugStepCheck(Value* value, TokenPosition position);
// Deals with StoreIndexed not working with kUnboxedFloat.
// TODO(dartbug.com/43448): Remove this workaround.
Fragment StoreIndexedTypedDataUnboxed(Representation unboxed_representation,
intptr_t index_scale,
bool index_unboxed);
// Deals with LoadIndexed not working with kUnboxedFloat.
// TODO(dartbug.com/43448): Remove this workaround.
Fragment LoadIndexedTypedDataUnboxed(Representation unboxed_representation,
intptr_t index_scale,
bool index_unboxed);
// Truncates (instead of deoptimizing) if the origin does not fit into the
// target representation.
Fragment UnboxTruncate(Representation to);
// Loads the (untagged) thread address.
Fragment LoadThread();
// Loads the (untagged) isolate address.
Fragment LoadIsolate();
// Loads the (untagged) service extension stream address.
Fragment LoadServiceExtensionStream();
// Converts a true to 1 and false to 0.
Fragment BoolToInt();
// Converts 0 to false and the rest to true.
Fragment IntToBool();
// Creates an ffi.Pointer holding a given address (TOS).
Fragment FfiPointerFromAddress(const Type& result_type);
// Pushes an (unboxed) bogus value returned when a native -> Dart callback
// throws an exception.
Fragment FfiExceptionalReturnValue(const AbstractType& result_type,
const Representation target);
// Pops a Dart object and push the unboxed native version, according to the
// semantics of FFI argument translation.
//
// Works for FFI call arguments, and FFI callback return values.
Fragment FfiConvertPrimitiveToNative(
const compiler::ffi::BaseMarshaller& marshaller,
intptr_t arg_index);
// Pops an unboxed native value, and pushes a Dart object, according to the
// semantics of FFI argument translation.
//
// Works for FFI call return values, and FFI callback arguments.
Fragment FfiConvertPrimitiveToDart(
const compiler::ffi::BaseMarshaller& marshaller,
intptr_t arg_index);
// We pass in `variable` instead of on top of the stack so that we can have
// multiple consecutive calls that keep only compound parts on the stack with
// no compound parts in between.
Fragment FfiCallConvertCompoundArgumentToNative(
LocalVariable* variable,
const compiler::ffi::BaseMarshaller& marshaller,
intptr_t arg_index);
Fragment FfiCallConvertCompoundReturnToDart(
const compiler::ffi::BaseMarshaller& marshaller,
intptr_t arg_index);
// We pass in multiple `definitions`, which are also expected to be the top
// of the stack. This eases storing each definition in the resulting struct
// or union.
Fragment FfiCallbackConvertCompoundArgumentToDart(
const compiler::ffi::BaseMarshaller& marshaller,
intptr_t arg_index,
ZoneGrowableArray<LocalVariable*>* definitions);
Fragment FfiCallbackConvertCompoundReturnToNative(
const compiler::ffi::CallbackMarshaller& marshaller,
intptr_t arg_index);
// Wraps a TypedDataBase from the stack and wraps it in a subclass of
// _Compound.
Fragment WrapTypedDataBaseInCompound(const AbstractType& compound_type);
// Loads the _typedDataBase field from a subclass of _Compound.
Fragment LoadTypedDataBaseFromCompound();
// Breaks up a subclass of _Compound in multiple definitions and puts them on
// the stack.
//
// Takes in the _Compound as a local `variable` so that can be anywhere on
// the stack and this function can be called multiple times to leave only the
// results of this function on the stack without any _Compounds in between.
//
// The compound contents are heterogeneous, so pass in
// `representations` to know what representation to load.
Fragment CopyFromCompoundToStack(
LocalVariable* variable,
const GrowableArray<Representation>& representations);
// Copy `definitions` into TypedData.
//
// Expects the TypedData on top of the stack and `definitions` right under it.
//
// Leaves TypedData on stack.
//
// The compound contents are heterogeneous, so pass in `representations` to
// know what representation to load.
Fragment PopFromStackToTypedDataBase(
ZoneGrowableArray<LocalVariable*>* definitions,
const GrowableArray<Representation>& representations);
// Copies bytes from a TypedDataBase to the address of an kUnboxedFfiIntPtr.
Fragment CopyFromTypedDataBaseToUnboxedAddress(intptr_t length_in_bytes);
// Copies bytes from the address of an kUnboxedFfiIntPtr to a TypedDataBase.
Fragment CopyFromUnboxedAddressToTypedDataBase(intptr_t length_in_bytes);
// Generates a call to `Thread::EnterApiScope`.
Fragment EnterHandleScope();
// Generates a load of `Thread::api_top_scope`.
Fragment GetTopHandleScope();
// Generates a call to `Thread::ExitApiScope`.
Fragment ExitHandleScope();
// Leaves a `LocalHandle` on the stack.
Fragment AllocateHandle();
// Loads a tagged value from an untagged base + offset from outside the heap.
Fragment RawLoadField(int32_t offset);
// Populates the untagged base + offset outside the heap with a tagged value.
//
// The store must be outside of the heap, does not emit a store barrier.
Fragment RawStoreField(int32_t offset);
// Wraps an `Object` from the stack and leaves a `LocalHandle` on the stack.
Fragment WrapHandle();
// Unwraps a `LocalHandle` from the stack and leaves the object on the stack.
Fragment UnwrapHandle();
// Wrap the current exception and stacktrace in an unhandled exception.
Fragment UnhandledException();
// Return from a native -> Dart callback. Can only be used in conjunction with
// NativeEntry and NativeParameter are used.
Fragment NativeReturn(const compiler::ffi::CallbackMarshaller& marshaller);
// Bit-wise cast between representations.
// Pops the input and pushes the converted result.
// Currently only works with equal sizes and floating point <-> integer.
Fragment BitCast(Representation from, Representation to);
// Generates Call1ArgStub instruction.
Fragment Call1ArgStub(TokenPosition position,
Call1ArgStubInstr::StubId stub_id);
LocalVariable* LookupVariable(intptr_t kernel_offset);
// Build type argument type checks for the current function.
// ParsedFunction should have the following information:
// - is_forwarding_stub()
// - forwarding_stub_super_target()
// Scope should be populated with parameter variables including
// - needs_type_check()
// - is_explicit_covariant_parameter()
void BuildTypeArgumentTypeChecks(TypeChecksToBuild mode,
Fragment* implicit_checks);
// Build argument type checks for the current function.
// ParsedFunction should have the following information:
// - is_forwarding_stub()
// - forwarding_stub_super_target()
// Scope should be populated with parameter variables including
// - needs_type_check()
// - is_explicit_covariant_parameter()
void BuildArgumentTypeChecks(Fragment* explicit_checks,
Fragment* implicit_checks,
Fragment* implicit_redefinitions);
// Returns true if null assertion is needed for
// a parameter of given type.
bool NeedsNullAssertion(const AbstractType& type);
// Builds null assertion for the given parameter.
Fragment NullAssertion(LocalVariable* variable);
// Builds null assertions for all parameters (if needed).
Fragment BuildNullAssertions();
// Builds flow graph for noSuchMethod forwarder.
//
// If throw_no_such_method_error is set to true, an
// instance of NoSuchMethodError is thrown. Otherwise, the instance
// noSuchMethod is called.
//
// ParsedFunction should have the following information:
// - default_parameter_values()
// - is_forwarding_stub()
// - forwarding_stub_super_target()
//
// Scope should be populated with parameter variables including
// - needs_type_check()
// - is_explicit_covariant_parameter()
//
FlowGraph* BuildGraphOfNoSuchMethodForwarder(
const Function& function,
bool is_implicit_closure_function,
bool throw_no_such_method_error);
// If no type arguments are passed to a generic function, we need to fill the
// type arguments in with the default types stored on the TypeParameter nodes
// in Kernel.
//
// ParsedFunction should have the following information:
// - DefaultFunctionTypeArguments()
// - function_type_arguments()
Fragment BuildDefaultTypeHandling(const Function& function);
FunctionEntryInstr* BuildSharedUncheckedEntryPoint(
Fragment prologue_from_normal_entry,
Fragment skippable_checks,
Fragment redefinitions_if_skipped,
Fragment body);
FunctionEntryInstr* BuildSeparateUncheckedEntryPoint(
BlockEntryInstr* normal_entry,
Fragment normal_prologue,
Fragment extra_prologue,
Fragment shared_prologue,
Fragment body);
// Builds flow graph for implicit closure function (tear-off).
//
// ParsedFunction should have the following information:
// - DefaultFunctionTypeArguments()
// - function_type_arguments()
// - default_parameter_values()
// - is_forwarding_stub()
// - forwarding_stub_super_target()
//
// Scope should be populated with parameter variables including
// - needs_type_check()
// - is_explicit_covariant_parameter()
//
FlowGraph* BuildGraphOfImplicitClosureFunction(const Function& function);
// Builds flow graph of implicit field getter, setter, or a
// dynamic invocation forwarder to a field setter.
//
// If field is const, its value should be evaluated and stored in
// - StaticValue()
//
// Scope should be populated with parameter variables including
// - needs_type_check()
//
FlowGraph* BuildGraphOfFieldAccessor(const Function& function);
// Builds flow graph of dynamic invocation forwarder.
//
// ParsedFunction should have the following information:
// - DefaultFunctionTypeArguments()
// - function_type_arguments()
// - default_parameter_values()
// - is_forwarding_stub()
// - forwarding_stub_super_target()
//
// Scope should be populated with parameter variables including
// - needs_type_check()
// - is_explicit_covariant_parameter()
//
FlowGraph* BuildGraphOfDynamicInvocationForwarder(const Function& function);
void SetConstantRangeOfCurrentDefinition(const Fragment& fragment,
int64_t min,
int64_t max);
// Extracts a packed field out of the unboxed value with representation [rep
// on the top of the stack. Picks a sequence that keeps unboxed values on the
// expression stack only as needed, switching to Smis as soon as possible.
template <typename T>
Fragment BuildExtractUnboxedSlotBitFieldIntoSmi(const Slot& slot) {
ASSERT(RepresentationUtils::IsUnboxedInteger(slot.representation()));
Fragment instructions;
if (!Boxing::RequiresAllocation(slot.representation())) {
// We don't need to allocate to box this value, so it already fits in
// a Smi (and thus the mask must also).
instructions += LoadNativeField(slot);
instructions += Box(slot.representation());
instructions += IntConstant(T::mask_in_place());
instructions += SmiBinaryOp(Token::kBIT_AND);
} else {
// Since kBIT_AND never throws or deoptimizes, we require that the result
// of masking the field in place fits into a Smi, so we can use Smi
// operations for the shift.
static_assert(T::mask_in_place() <= compiler::target::kSmiMax,
"Cannot fit results of masking in place into a Smi");
instructions += LoadNativeField(slot);
instructions +=
UnboxedIntConstant(T::mask_in_place(), slot.representation());
instructions += BinaryIntegerOp(Token::kBIT_AND, slot.representation());
// Set the range of the definition that will be used as the value in the
// box so that ValueFitsSmi() returns true even in unoptimized code.
SetConstantRangeOfCurrentDefinition(instructions, 0, T::mask_in_place());
instructions += Box(slot.representation());
}
if (T::shift() != 0) {
// Only add the shift operation if it's necessary.
instructions += IntConstant(T::shift());
instructions += SmiBinaryOp(Token::kSHR);
}
return instructions;
}
TranslationHelper translation_helper_;
Thread* thread_;
Zone* zone_;
ParsedFunction* parsed_function_;
const bool optimizing_;
ZoneGrowableArray<const ICData*>& ic_data_array_;
intptr_t next_function_id_;
intptr_t AllocateFunctionId() { return next_function_id_++; }
intptr_t loop_depth_;
intptr_t try_depth_;
intptr_t catch_depth_;
intptr_t for_in_depth_;
intptr_t block_expression_depth_;
GraphEntryInstr* graph_entry_;
ScopeBuildingResult* scopes_;
GrowableArray<YieldContinuation> yield_continuations_;
LocalVariable* CurrentException() {
return scopes_->exception_variables[catch_depth_ - 1];
}
LocalVariable* CurrentStackTrace() {
return scopes_->stack_trace_variables[catch_depth_ - 1];
}
LocalVariable* CurrentRawException() {
return scopes_->raw_exception_variables[catch_depth_ - 1];
}
LocalVariable* CurrentRawStackTrace() {
return scopes_->raw_stack_trace_variables[catch_depth_ - 1];
}
LocalVariable* CurrentCatchContext() {
return scopes_->catch_context_variables[try_depth_];
}
TryCatchBlock* CurrentTryCatchBlock() const { return try_catch_block_; }
void SetCurrentTryCatchBlock(TryCatchBlock* try_catch_block);
// A chained list of breakable blocks. Chaining and lookup is done by the
// [BreakableBlock] class.
BreakableBlock* breakable_block_;
// A chained list of switch blocks. Chaining and lookup is done by the
// [SwitchBlock] class.
SwitchBlock* switch_block_;
// A chained list of try-catch blocks. Chaining and lookup is done by the
// [TryCatchBlock] class.
TryCatchBlock* try_catch_block_;
// A chained list of try-finally blocks. Chaining and lookup is done by the
// [TryFinallyBlock] class.
TryFinallyBlock* try_finally_block_;
// A chained list of catch blocks. Chaining and lookup is done by the
// [CatchBlock] class.
CatchBlock* catch_block_;
ActiveClass active_class_;
// Cached _PrependTypeArguments.
Function& prepend_type_arguments_;
// Returns the function _PrependTypeArguments from dart:_internal. If the
// cached version is null, retrieves it and updates the cache.
const Function& PrependTypeArgumentsFunction();
// Cached _AssertionError._throwNewNullAssertion.
Function& throw_new_null_assertion_;
// Returns the function _AssertionError._throwNewNullAssertion. If the
// cached version is null, retrieves it and updates the cache.
const Function& ThrowNewNullAssertionFunction();
friend class BreakableBlock;
friend class CatchBlock;
friend class ProgramState;
friend class StreamingFlowGraphBuilder;
friend class SwitchBlock;
friend class TryCatchBlock;
friend class TryFinallyBlock;
DISALLOW_COPY_AND_ASSIGN(FlowGraphBuilder);
};
// Convenience class to save/restore program state.
// This snapshot denotes a partial state of the flow
// grap builder that is needed when recursing into
// the statements and expressions of a finalizer block.
class ProgramState {
public:
ProgramState(BreakableBlock* breakable_block,
SwitchBlock* switch_block,
intptr_t loop_depth,
intptr_t for_in_depth,
intptr_t try_depth,
intptr_t catch_depth,
intptr_t block_expression_depth)
: breakable_block_(breakable_block),
switch_block_(switch_block),
loop_depth_(loop_depth),
for_in_depth_(for_in_depth),
try_depth_(try_depth),
catch_depth_(catch_depth),
block_expression_depth_(block_expression_depth) {}
void assignTo(FlowGraphBuilder* builder) const {
builder->breakable_block_ = breakable_block_;
builder->switch_block_ = switch_block_;
builder->loop_depth_ = loop_depth_;
builder->for_in_depth_ = for_in_depth_;
builder->try_depth_ = try_depth_;
builder->catch_depth_ = catch_depth_;
builder->block_expression_depth_ = block_expression_depth_;
}
private:
BreakableBlock* const breakable_block_;
SwitchBlock* const switch_block_;
const intptr_t loop_depth_;
const intptr_t for_in_depth_;
const intptr_t try_depth_;
const intptr_t catch_depth_;
const intptr_t block_expression_depth_;
};
class SwitchBlock {
public:
SwitchBlock(FlowGraphBuilder* builder, intptr_t case_count)
: builder_(builder),
outer_(builder->switch_block_),
outer_finally_(builder->try_finally_block_),
case_count_(case_count),
context_depth_(builder->context_depth_),
try_index_(builder->CurrentTryIndex()) {
builder_->switch_block_ = this;
if (outer_ != NULL) {
depth_ = outer_->depth_ + outer_->case_count_;
} else {
depth_ = 0;
}
}
~SwitchBlock() { builder_->switch_block_ = outer_; }
bool HadJumper(intptr_t case_num) {
return destinations_.Lookup(case_num) != NULL;
}
// Get destination via absolute target number (i.e. the correct destination
// is not necessarily in this block).
JoinEntryInstr* Destination(intptr_t target_index,
TryFinallyBlock** outer_finally = NULL,
intptr_t* context_depth = NULL) {
// Verify consistency of program state.
ASSERT(builder_->switch_block_ == this);
// Find corresponding destination.
SwitchBlock* block = this;
while (block->depth_ > target_index) {
block = block->outer_;
ASSERT(block != nullptr);
}
// Set the outer finally block.
if (outer_finally != NULL) {
*outer_finally = block->outer_finally_;
*context_depth = block->context_depth_;
}
// Ensure there's [JoinEntryInstr] for that [SwitchCase].
return block->EnsureDestination(target_index - block->depth_);
}
// Get destination via relative target number (i.e. relative to this block,
// 0 is first case in this block etc).
JoinEntryInstr* DestinationDirect(intptr_t case_num,
TryFinallyBlock** outer_finally = NULL,
intptr_t* context_depth = NULL) {
// Set the outer finally block.
if (outer_finally != NULL) {
*outer_finally = outer_finally_;
*context_depth = context_depth_;
}
// Ensure there's [JoinEntryInstr] for that [SwitchCase].
return EnsureDestination(case_num);
}
private:
JoinEntryInstr* EnsureDestination(intptr_t case_num) {
JoinEntryInstr* cached_inst = destinations_.Lookup(case_num);
if (cached_inst == NULL) {
JoinEntryInstr* inst = builder_->BuildJoinEntry(try_index_);
destinations_.Insert(case_num, inst);
return inst;
}
return cached_inst;
}
FlowGraphBuilder* builder_;
SwitchBlock* outer_;
IntMap<JoinEntryInstr*> destinations_;
TryFinallyBlock* outer_finally_;
intptr_t case_count_;
intptr_t depth_;
intptr_t context_depth_;
intptr_t try_index_;
};
class TryCatchBlock {
public:
explicit TryCatchBlock(FlowGraphBuilder* builder,
intptr_t try_handler_index = -1)
: builder_(builder),
outer_(builder->CurrentTryCatchBlock()),
try_index_(try_handler_index == -1 ? builder->AllocateTryIndex()
: try_handler_index) {
builder->SetCurrentTryCatchBlock(this);
}
~TryCatchBlock() { builder_->SetCurrentTryCatchBlock(outer_); }
intptr_t try_index() { return try_index_; }
TryCatchBlock* outer() const { return outer_; }
private:
FlowGraphBuilder* const builder_;
TryCatchBlock* const outer_;
intptr_t const try_index_;
DISALLOW_COPY_AND_ASSIGN(TryCatchBlock);
};
class TryFinallyBlock {
public:
TryFinallyBlock(FlowGraphBuilder* builder, intptr_t finalizer_kernel_offset)
: builder_(builder),
outer_(builder->try_finally_block_),
finalizer_kernel_offset_(finalizer_kernel_offset),
context_depth_(builder->context_depth_),
try_index_(builder_->CurrentTryIndex()),
// Finalizers are executed outside of the try block hence
// try depth of finalizers are one less than current try
// depth. For others, program state is snapshot of current.
state_(builder_->breakable_block_,
builder_->switch_block_,
builder_->loop_depth_,
builder_->for_in_depth_,
builder_->try_depth_ - 1,
builder_->catch_depth_,
builder_->block_expression_depth_) {
builder_->try_finally_block_ = this;
}
~TryFinallyBlock() { builder_->try_finally_block_ = outer_; }
TryFinallyBlock* outer() const { return outer_; }
intptr_t finalizer_kernel_offset() const { return finalizer_kernel_offset_; }
intptr_t context_depth() const { return context_depth_; }
intptr_t try_index() const { return try_index_; }
const ProgramState& state() const { return state_; }
private:
FlowGraphBuilder* const builder_;
TryFinallyBlock* const outer_;
const intptr_t finalizer_kernel_offset_;
const intptr_t context_depth_;
const intptr_t try_index_;
const ProgramState state_;
DISALLOW_COPY_AND_ASSIGN(TryFinallyBlock);
};
class BreakableBlock {
public:
explicit BreakableBlock(FlowGraphBuilder* builder)
: builder_(builder),
outer_(builder->breakable_block_),
destination_(NULL),
outer_finally_(builder->try_finally_block_),
context_depth_(builder->context_depth_),
try_index_(builder->CurrentTryIndex()) {
if (builder_->breakable_block_ == NULL) {
index_ = 0;
} else {
index_ = builder_->breakable_block_->index_ + 1;
}
builder_->breakable_block_ = this;
}
~BreakableBlock() { builder_->breakable_block_ = outer_; }
bool HadJumper() { return destination_ != NULL; }
JoinEntryInstr* destination() { return destination_; }
JoinEntryInstr* BreakDestination(intptr_t label_index,
TryFinallyBlock** outer_finally,
intptr_t* context_depth) {
// Verify consistency of program state.
ASSERT(builder_->breakable_block_ == this);
// Find corresponding destination.
BreakableBlock* block = this;
while (block->index_ != label_index) {
block = block->outer_;
ASSERT(block != nullptr);
}
*outer_finally = block->outer_finally_;
*context_depth = block->context_depth_;
return block->EnsureDestination();
}
private:
JoinEntryInstr* EnsureDestination() {
if (destination_ == NULL) {
destination_ = builder_->BuildJoinEntry(try_index_);
}
return destination_;
}
FlowGraphBuilder* builder_;
intptr_t index_;
BreakableBlock* outer_;
JoinEntryInstr* destination_;
TryFinallyBlock* outer_finally_;
intptr_t context_depth_;
intptr_t try_index_;
DISALLOW_COPY_AND_ASSIGN(BreakableBlock);
};
class CatchBlock {
public:
CatchBlock(FlowGraphBuilder* builder,
LocalVariable* exception_var,
LocalVariable* stack_trace_var,
intptr_t catch_try_index)
: builder_(builder),
outer_(builder->catch_block_),
exception_var_(exception_var),
stack_trace_var_(stack_trace_var),
catch_try_index_(catch_try_index) {
builder_->catch_block_ = this;
}
~CatchBlock() { builder_->catch_block_ = outer_; }
LocalVariable* exception_var() { return exception_var_; }
LocalVariable* stack_trace_var() { return stack_trace_var_; }
intptr_t catch_try_index() { return catch_try_index_; }
private:
FlowGraphBuilder* builder_;
CatchBlock* outer_;
LocalVariable* exception_var_;
LocalVariable* stack_trace_var_;
intptr_t catch_try_index_;
DISALLOW_COPY_AND_ASSIGN(CatchBlock);
};
} // namespace kernel
} // namespace dart
#endif // RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TO_IL_H_