| // Copyright (c) 2014, 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_REGEXP_ASSEMBLER_IR_H_ |
| #define RUNTIME_VM_REGEXP_ASSEMBLER_IR_H_ |
| |
| #include "vm/compiler/assembler/assembler.h" |
| #include "vm/compiler/backend/il.h" |
| #include "vm/object.h" |
| #include "vm/regexp_assembler.h" |
| |
| namespace dart { |
| |
| class IRRegExpMacroAssembler : public RegExpMacroAssembler { |
| public: |
| // Type of input string to generate code for. |
| enum Mode { ASCII = 1, UC16 = 2 }; |
| |
| // Result of calling generated native RegExp code. |
| // RETRY: Something significant changed during execution, and the matching |
| // should be retried from scratch. |
| // EXCEPTION: Something failed during execution. If no exception has been |
| // thrown, it's an internal out-of-memory, and the caller should |
| // throw the exception. |
| // FAILURE: Matching failed. |
| // SUCCESS: Matching succeeded, and the output array has been filled with |
| // capture positions. |
| enum Result { RETRY = -2, EXCEPTION = -1, FAILURE = 0, SUCCESS = 1 }; |
| |
| IRRegExpMacroAssembler(intptr_t specialization_cid, |
| intptr_t capture_count, |
| const ParsedFunction* parsed_function, |
| const ZoneGrowableArray<const ICData*>& ic_data_array, |
| intptr_t osr_id, |
| Zone* zone); |
| virtual ~IRRegExpMacroAssembler(); |
| |
| virtual bool CanReadUnaligned(); |
| |
| static ArrayPtr Execute(const RegExp& regexp, |
| const String& input, |
| const Smi& start_offset, |
| bool sticky, |
| Zone* zone); |
| |
| virtual bool IsClosed() const { return (current_instruction_ == NULL); } |
| |
| virtual intptr_t stack_limit_slack(); |
| virtual void AdvanceCurrentPosition(intptr_t by); |
| virtual void AdvanceRegister(intptr_t reg, intptr_t by); |
| virtual void Backtrack(); |
| virtual void BindBlock(BlockLabel* label); |
| virtual void CheckAtStart(BlockLabel* on_at_start); |
| virtual void CheckCharacter(uint32_t c, BlockLabel* on_equal); |
| virtual void CheckCharacterAfterAnd(uint32_t c, |
| uint32_t mask, |
| BlockLabel* on_equal); |
| virtual void CheckCharacterGT(uint16_t limit, BlockLabel* on_greater); |
| virtual void CheckCharacterLT(uint16_t limit, BlockLabel* on_less); |
| // A "greedy loop" is a loop that is both greedy and with a simple |
| // body. It has a particularly simple implementation. |
| virtual void CheckGreedyLoop(BlockLabel* on_tos_equals_current_position); |
| virtual void CheckNotAtStart(intptr_t cp_offset, BlockLabel* on_not_at_start); |
| virtual void CheckNotBackReference(intptr_t start_reg, |
| bool read_backward, |
| BlockLabel* on_no_match); |
| virtual void CheckNotBackReferenceIgnoreCase(intptr_t start_reg, |
| bool read_backward, |
| bool unicode, |
| BlockLabel* on_no_match); |
| virtual void CheckNotCharacter(uint32_t c, BlockLabel* on_not_equal); |
| virtual void CheckNotCharacterAfterAnd(uint32_t c, |
| uint32_t mask, |
| BlockLabel* on_not_equal); |
| virtual void CheckNotCharacterAfterMinusAnd(uint16_t c, |
| uint16_t minus, |
| uint16_t mask, |
| BlockLabel* on_not_equal); |
| virtual void CheckCharacterInRange(uint16_t from, |
| uint16_t to, |
| BlockLabel* on_in_range); |
| virtual void CheckCharacterNotInRange(uint16_t from, |
| uint16_t to, |
| BlockLabel* on_not_in_range); |
| virtual void CheckBitInTable(const TypedData& table, BlockLabel* on_bit_set); |
| |
| // Checks whether the given offset from the current position is before |
| // the end of the string. |
| virtual void CheckPosition(intptr_t cp_offset, BlockLabel* on_outside_input); |
| virtual bool CheckSpecialCharacterClass(uint16_t type, |
| BlockLabel* on_no_match); |
| virtual void Fail(); |
| virtual void IfRegisterGE(intptr_t reg, |
| intptr_t comparand, |
| BlockLabel* if_ge); |
| virtual void IfRegisterLT(intptr_t reg, |
| intptr_t comparand, |
| BlockLabel* if_lt); |
| virtual void IfRegisterEqPos(intptr_t reg, BlockLabel* if_eq); |
| virtual IrregexpImplementation Implementation(); |
| virtual void GoTo(BlockLabel* to); |
| virtual void LoadCurrentCharacter(intptr_t cp_offset, |
| BlockLabel* on_end_of_input, |
| bool check_bounds = true, |
| intptr_t characters = 1); |
| virtual void PopCurrentPosition(); |
| virtual void PopRegister(intptr_t register_index); |
| virtual void Print(const char* str); |
| virtual void PushBacktrack(BlockLabel* label); |
| virtual void PushCurrentPosition(); |
| virtual void PushRegister(intptr_t register_index); |
| virtual void ReadCurrentPositionFromRegister(intptr_t reg); |
| virtual void ReadStackPointerFromRegister(intptr_t reg); |
| virtual void SetCurrentPositionFromEnd(intptr_t by); |
| virtual void SetRegister(intptr_t register_index, intptr_t to); |
| virtual bool Succeed(); |
| virtual void WriteCurrentPositionToRegister(intptr_t reg, intptr_t cp_offset); |
| virtual void ClearRegisters(intptr_t reg_from, intptr_t reg_to); |
| virtual void WriteStackPointerToRegister(intptr_t reg); |
| |
| virtual void PrintBlocks(); |
| |
| IndirectGotoInstr* backtrack_goto() const { return backtrack_goto_; } |
| GraphEntryInstr* graph_entry() const { return entry_block_; } |
| |
| intptr_t num_stack_locals() const { return local_id_.Count(); } |
| intptr_t num_blocks() const { return block_id_.Count(); } |
| |
| // Generate a dispatch block implementing backtracking. Must be done after |
| // graph construction. |
| void GenerateBacktrackBlock(); |
| |
| // Allocate the actual registers array once its size is known. Must be done |
| // after graph construction. |
| void FinalizeRegistersArray(); |
| |
| private: |
| intptr_t GetNextDeoptId() const { |
| return thread_->compiler_state().GetNextDeoptId(); |
| } |
| |
| // Generate the contents of preset blocks. The entry block is the entry point |
| // of the generated code. |
| void GenerateEntryBlock(); |
| // Copies capture indices into the result area and returns true. |
| void GenerateSuccessBlock(); |
| // Returns false. |
| void GenerateExitBlock(); |
| |
| enum ComparisonKind { |
| kEQ, |
| kNE, |
| kLT, |
| kGT, |
| kLTE, |
| kGTE, |
| }; |
| |
| struct InstanceCallDescriptor { |
| // Standard (i.e. most non-Smi) functions. |
| explicit InstanceCallDescriptor(const String& name) |
| : name(name), token_kind(Token::kILLEGAL), checked_argument_count(1) {} |
| |
| InstanceCallDescriptor(const String& name, |
| Token::Kind token_kind, |
| intptr_t checked_argument_count) |
| : name(name), |
| token_kind(token_kind), |
| checked_argument_count(checked_argument_count) {} |
| |
| // Special cases for Smi and indexing functions. |
| static InstanceCallDescriptor FromToken(Token::Kind token_kind) { |
| switch (token_kind) { |
| case Token::kEQ: |
| return InstanceCallDescriptor(Symbols::EqualOperator(), token_kind, |
| 2); |
| case Token::kADD: |
| return InstanceCallDescriptor(Symbols::Plus(), token_kind, 2); |
| case Token::kSUB: |
| return InstanceCallDescriptor(Symbols::Minus(), token_kind, 2); |
| case Token::kBIT_OR: |
| return InstanceCallDescriptor(Symbols::BitOr(), token_kind, 2); |
| case Token::kBIT_AND: |
| return InstanceCallDescriptor(Symbols::BitAnd(), token_kind, 2); |
| case Token::kLT: |
| return InstanceCallDescriptor(Symbols::LAngleBracket(), token_kind, |
| 2); |
| case Token::kLTE: |
| return InstanceCallDescriptor(Symbols::LessEqualOperator(), |
| token_kind, 2); |
| case Token::kGT: |
| return InstanceCallDescriptor(Symbols::RAngleBracket(), token_kind, |
| 2); |
| case Token::kGTE: |
| return InstanceCallDescriptor(Symbols::GreaterEqualOperator(), |
| token_kind, 2); |
| case Token::kNEGATE: |
| return InstanceCallDescriptor(Symbols::UnaryMinus(), token_kind, 1); |
| case Token::kINDEX: |
| return InstanceCallDescriptor(Symbols::IndexToken(), token_kind, 2); |
| case Token::kASSIGN_INDEX: |
| return InstanceCallDescriptor(Symbols::AssignIndexToken(), token_kind, |
| 2); |
| default: |
| UNREACHABLE(); |
| } |
| UNREACHABLE(); |
| return InstanceCallDescriptor(Symbols::Empty()); |
| } |
| |
| const String& name; |
| Token::Kind token_kind; |
| intptr_t checked_argument_count; |
| }; |
| |
| LocalVariable* Local(const String& name); |
| LocalVariable* Parameter(const String& name, intptr_t index) const; |
| |
| ConstantInstr* Int64Constant(int64_t value) const; |
| ConstantInstr* Uint64Constant(uint64_t value) const; |
| ConstantInstr* BoolConstant(bool value) const; |
| ConstantInstr* StringConstant(const char* value) const; |
| |
| // The word character map static member of the RegExp class. |
| // Byte map of one byte characters with a 0xff if the character is a word |
| // character (digit, letter or underscore) and 0x00 otherwise. |
| // Used by generated RegExp code. |
| ConstantInstr* WordCharacterMapConstant() const; |
| |
| ComparisonInstr* Comparison(ComparisonKind kind, Value* lhs, Value* rhs); |
| ComparisonInstr* Comparison(ComparisonKind kind, |
| Definition* lhs, |
| Definition* rhs); |
| |
| InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc, |
| Value* arg1) const; |
| InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc, |
| Value* arg1, |
| Value* arg2) const; |
| InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc, |
| Value* arg1, |
| Value* arg2, |
| Value* arg3) const; |
| InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc, |
| InputsArray* arguments) const; |
| |
| StaticCallInstr* StaticCall(const Function& function, |
| ICData::RebindRule rebind_rule) const; |
| StaticCallInstr* StaticCall(const Function& function, |
| Value* arg1, |
| ICData::RebindRule rebind_rule) const; |
| StaticCallInstr* StaticCall(const Function& function, |
| Value* arg1, |
| Value* arg2, |
| ICData::RebindRule rebind_rule) const; |
| StaticCallInstr* StaticCall(const Function& function, |
| InputsArray* arguments, |
| ICData::RebindRule rebind_rule) const; |
| |
| // Creates a new block consisting simply of a goto to dst. |
| TargetEntryInstr* TargetWithJoinGoto(JoinEntryInstr* dst); |
| IndirectEntryInstr* IndirectWithJoinGoto(JoinEntryInstr* dst); |
| |
| // Adds, respectively subtracts lhs and rhs and returns the result. |
| Definition* Add(Value* lhs, Value* rhs); |
| Definition* Sub(Value* lhs, Value* rhs); |
| |
| LoadLocalInstr* LoadLocal(LocalVariable* local) const; |
| void StoreLocal(LocalVariable* local, Value* value); |
| |
| Value* PushLocal(LocalVariable* local); |
| |
| Value* PushRegisterIndex(intptr_t reg); |
| Value* LoadRegister(intptr_t reg); |
| void StoreRegister(intptr_t reg, intptr_t value); |
| void StoreRegister(Value* registers, Value* index, Value* value); |
| |
| // Load a number of characters at the given offset from the |
| // current position, into the current-character register. |
| void LoadCurrentCharacterUnchecked(intptr_t cp_offset, |
| intptr_t character_count); |
| |
| // Returns the character within the passed string at the specified index. |
| Value* CharacterAt(LocalVariable* index); |
| |
| // Load a number of characters starting from index in the pattern string. |
| Value* LoadCodeUnitsAt(LocalVariable* index, intptr_t character_count); |
| |
| // Check whether preemption has been requested. Also serves as an OSR entry. |
| void CheckPreemption(bool is_backtrack); |
| |
| // Byte size of chars in the string to match (decided by the Mode argument) |
| inline intptr_t char_size() { return static_cast<int>(mode_); } |
| |
| // Equivalent to a conditional branch to the label, unless the label |
| // is NULL, in which case it is a conditional Backtrack. |
| void BranchOrBacktrack(ComparisonInstr* comparison, |
| BlockLabel* true_successor); |
| |
| // Set up all local variables and parameters. |
| void InitializeLocals(); |
| |
| // Allocates a new local, and returns the appropriate id for placing it |
| // on the stack. |
| intptr_t GetNextLocalIndex(); |
| |
| // We never have any copied parameters. |
| intptr_t num_copied_params() const { return 0; } |
| |
| // Return the position register at the specified index, creating it if |
| // necessary. Note that the number of such registers can exceed the amount |
| // required by the number of output captures. |
| LocalVariable* position_register(intptr_t index); |
| |
| void set_current_instruction(Instruction* instruction); |
| |
| // The following functions are responsible for appending instructions |
| // to the current instruction in various ways. The most simple one |
| // is AppendInstruction, which simply appends an instruction and performs |
| // bookkeeping. |
| void AppendInstruction(Instruction* instruction); |
| // Similar to AppendInstruction, but closes the current block by |
| // setting current_instruction_ to NULL. |
| void CloseBlockWith(Instruction* instruction); |
| // Appends definition and allocates a temp index for the result. |
| Value* Bind(Definition* definition); |
| // Loads and binds a local variable. |
| Value* BindLoadLocal(const LocalVariable& local); |
| |
| // Appends the definition. |
| void Do(Definition* definition); |
| // Closes the current block with a jump to the specified block. |
| void GoTo(JoinEntryInstr* to); |
| |
| // Accessors for our local stack_. |
| void PushStack(Definition* definition); |
| Definition* PopStack(); |
| Definition* PeekStack(); |
| void CheckStackLimit(); |
| void GrowStack(); |
| |
| // Prints the specified argument. Used for debugging. |
| void Print(Value* argument); |
| |
| // A utility class tracking ids of various objects such as blocks, temps, etc. |
| class IdAllocator : public ValueObject { |
| public: |
| explicit IdAllocator(intptr_t first_id = 0) : next_id(first_id) {} |
| |
| intptr_t Count() const { return next_id; } |
| intptr_t Alloc(intptr_t count = 1) { |
| ASSERT(count >= 0); |
| intptr_t current_id = next_id; |
| next_id += count; |
| return current_id; |
| } |
| void Dealloc(intptr_t count = 1) { |
| ASSERT(count <= next_id); |
| next_id -= count; |
| } |
| |
| private: |
| intptr_t next_id; |
| }; |
| |
| Thread* thread_; |
| |
| // Which mode to generate code for (ASCII or UC16). |
| Mode mode_; |
| |
| // Which specific string class to generate code for. |
| intptr_t specialization_cid_; |
| |
| // Block entries used internally. |
| GraphEntryInstr* entry_block_; |
| JoinEntryInstr* start_block_; |
| JoinEntryInstr* success_block_; |
| JoinEntryInstr* exit_block_; |
| |
| // Shared backtracking block. |
| JoinEntryInstr* backtrack_block_; |
| // Single indirect goto instruction which performs all backtracking. |
| IndirectGotoInstr* backtrack_goto_; |
| |
| const ParsedFunction* parsed_function_; |
| const ZoneGrowableArray<const ICData*>& ic_data_array_; |
| |
| // All created blocks are contained within this set. Used for printing |
| // the generated code. |
| GrowableArray<BlockEntryInstr*> blocks_; |
| |
| // The current instruction to link to when new code is emitted. |
| Instruction* current_instruction_; |
| |
| // A list, acting as the runtime stack for both backtrack locations and |
| // stored positions within the string. |
| LocalVariable* stack_; |
| LocalVariable* stack_pointer_; |
| |
| // Stores the current character within the string. |
| LocalVariable* current_character_; |
| |
| // Stores the current location within the string as a negative offset |
| // from the end of the string. |
| LocalVariable* current_position_; |
| |
| // The string being processed, passed as a function parameter. |
| LocalVariable* string_param_; |
| |
| // Stores the length of string_param_. |
| LocalVariable* string_param_length_; |
| |
| // The start index within the string, passed as a function parameter. |
| LocalVariable* start_index_param_; |
| |
| // An assortment of utility variables. |
| LocalVariable* capture_length_; |
| LocalVariable* match_start_index_; |
| LocalVariable* capture_start_index_; |
| LocalVariable* match_end_index_; |
| LocalVariable* char_in_capture_; |
| LocalVariable* char_in_match_; |
| LocalVariable* index_temp_; |
| |
| LocalVariable* result_; |
| |
| // Stored positions containing group bounds. Generated as needed. |
| LocalVariable* registers_; |
| intptr_t registers_count_; |
| const intptr_t saved_registers_count_; |
| |
| // The actual array objects used for the stack and registers. |
| Array& stack_array_cell_; |
| TypedData& registers_array_; |
| |
| IdAllocator block_id_; |
| IdAllocator temp_id_; |
| IdAllocator local_id_; |
| IdAllocator indirect_id_; |
| }; |
| |
| } // namespace dart |
| |
| #endif // RUNTIME_VM_REGEXP_ASSEMBLER_IR_H_ |