// 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& 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(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& ic_data_array_; // All created blocks are contained within this set. Used for printing // the generated code. GrowableArray 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_