void BytesTrieTest::TestTruncatingIteratorFromLinearMatchLong() { static const StringAndValue data[]={ { "abcdef", 10 }, { "abcdepq", 200 }, { "abcdeyz", 3000 } }; LocalPointer<BytesTrie> trie(buildTrie(data, UPRV_LENGTHOF(data), USTRINGTRIE_BUILD_FAST)); if(trie.isNull()) { return; // buildTrie() reported an error } // Go into a linear-match node. trie->next('a'); trie->next('b'); trie->next('c'); IcuTestErrorCode errorCode(*this, "TestTruncatingIteratorFromLinearMatchLong()"); // Truncate after the linear-match node. BytesTrie::Iterator iter(*trie, 3, errorCode); if(errorCode.logIfFailureAndReset("BytesTrie::Iterator(trie) constructor")) { return; } static const StringAndValue expected[]={ { "def", 10 }, { "dep", -1 }, { "dey", -1 } }; checkIterator(iter, expected, UPRV_LENGTHOF(expected)); // Reset, and we should get the same result. logln("after iter.reset()"); checkIterator(iter.reset(), expected, UPRV_LENGTHOF(expected)); }
void BytesTrieTest::TestIteratorFromLinearMatch() { LocalPointer<BytesTrie> trie(buildMonthsTrie(USTRINGTRIE_BUILD_SMALL)); if(trie.isNull()) { return; // buildTrie() reported an error } // Go into a linear-match node. trie->next('j'); trie->next('a'); trie->next('n'); trie->next('u'); trie->next('a'); IcuTestErrorCode errorCode(*this, "TestIteratorFromLinearMatch()"); BytesTrie::Iterator iter(*trie, 0, errorCode); if(errorCode.logIfFailureAndReset("BytesTrie::Iterator(trie) constructor")) { return; } // Expected data: Same as in buildMonthsTrie(), except only the suffixes // following "janua". static const StringAndValue data[]={ { "r", 1 }, { "ry", 1 } }; checkIterator(iter, data, UPRV_LENGTHOF(data)); // Reset, and we should get the same result. logln("after iter.reset()"); checkIterator(iter.reset(), data, UPRV_LENGTHOF(data)); }
void BytesTrieTest::TestIteratorFromBranch() { LocalPointer<BytesTrie> trie(buildMonthsTrie(USTRINGTRIE_BUILD_FAST)); if(trie.isNull()) { return; // buildTrie() reported an error } // Go to a branch node. trie->next('j'); trie->next('a'); trie->next('n'); IcuTestErrorCode errorCode(*this, "TestIteratorFromBranch()"); BytesTrie::Iterator iter(*trie, 0, errorCode); if(errorCode.logIfFailureAndReset("BytesTrie::Iterator(trie) constructor")) { return; } // Expected data: Same as in buildMonthsTrie(), except only the suffixes // following "jan". static const StringAndValue data[]={ { "", 1 }, { ".", 1 }, { "a", 1 }, { "bb", 1 }, { "c", 1 }, { "ddd", 1 }, { "ee", 1 }, { "ef", 1 }, { "f", 1 }, { "gg", 1 }, { "h", 1 }, { "iiii", 1 }, { "j", 1 }, { "kk", 1 }, { "kl", 1 }, { "kmm", 1 }, { "l", 1 }, { "m", 1 }, { "nnnnnnnnnnnnnnnnnnnnnnnnnnnn", 1 }, { "o", 1 }, { "pp", 1 }, { "qqq", 1 }, { "r", 1 }, { "uar", 1 }, { "uary", 1 } }; checkIterator(iter, data, UPRV_LENGTHOF(data)); // Reset, and we should get the same result. logln("after iter.reset()"); checkIterator(iter.reset(), data, UPRV_LENGTHOF(data)); }
void BytesTrieTest::TestTruncatingIteratorFromRoot() { LocalPointer<BytesTrie> trie(buildMonthsTrie(USTRINGTRIE_BUILD_FAST)); if(trie.isNull()) { return; // buildTrie() reported an error } IcuTestErrorCode errorCode(*this, "TestTruncatingIteratorFromRoot()"); BytesTrie::Iterator iter(*trie, 4, errorCode); if(errorCode.logIfFailureAndReset("BytesTrie::Iterator(trie) constructor")) { return; } // Expected data: Same as in buildMonthsTrie(), except only the first 4 characters // of each string, and no string duplicates from the truncation. static const StringAndValue data[]={ { "augu", -1 }, { "jan", 1 }, { "jan.", 1 }, { "jana", 1 }, { "janb", -1 }, { "janc", 1 }, { "jand", -1 }, { "jane", -1 }, { "janf", 1 }, { "jang", -1 }, { "janh", 1 }, { "jani", -1 }, { "janj", 1 }, { "jank", -1 }, { "janl", 1 }, { "janm", 1 }, { "jann", -1 }, { "jano", 1 }, { "janp", -1 }, { "janq", -1 }, { "janr", 1 }, { "janu", -1 }, { "july", 7 }, { "jun", 6 }, { "jun.", 6 }, { "june", 6 } }; checkIterator(iter, data, UPRV_LENGTHOF(data)); // Reset, and we should get the same result. logln("after iter.reset()"); checkIterator(iter.reset(), data, UPRV_LENGTHOF(data)); }
void BytesTrieTest::checkIterator(const BytesTrie &trie, const StringAndValue data[], int32_t dataLength) { IcuTestErrorCode errorCode(*this, "checkIterator()"); BytesTrie::Iterator iter(trie, 0, errorCode); if(errorCode.logIfFailureAndReset("BytesTrie::Iterator(trie) constructor")) { return; } checkIterator(iter, data, dataLength); }
void BytesTrieTest::checkData(const StringAndValue data[], int32_t dataLength, UStringTrieBuildOption buildOption) { LocalPointer<BytesTrie> trie(buildTrie(data, dataLength, buildOption)); if(trie.isNull()) { return; // buildTrie() reported an error } checkFirst(*trie, data, dataLength); checkNext(*trie, data, dataLength); checkNextWithState(*trie, data, dataLength); checkNextString(*trie, data, dataLength); checkIterator(*trie, data, dataLength); }
void BytesTrieTest::TestIteratorFromBytes() { static const StringAndValue data[]={ { "mm", 3 }, { "mmm", 33 }, { "mmnop", 333 } }; builder_->clear(); IcuTestErrorCode errorCode(*this, "TestIteratorFromBytes()"); for(int32_t i=0; i<UPRV_LENGTHOF(data); ++i) { builder_->add(data[i].s, data[i].value, errorCode); } StringPiece trieBytes=builder_->buildStringPiece(USTRINGTRIE_BUILD_FAST, errorCode); BytesTrie::Iterator iter(trieBytes.data(), 0, errorCode); checkIterator(iter, data, UPRV_LENGTHOF(data)); }
void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck) { // === Stage 1 - Function header code generation === // // This code currently matches the old JIT. In the function header we need to // pop the return address (since we do not allow any recursion on the machine // stack), and perform a fast register file check. // This is the main entry point, without performing an arity check. // FIXME: https://bugs.webkit.org/show_bug.cgi?id=56292 // We'll need to convert the remaining cti_ style calls (specifically the register file // check) which will be dependent on stack layout. (We'd need to account for this in // both normal return code and when jumping to an exception handler). preserveReturnAddressAfterCall(GPRInfo::regT2); emitPutToCallFrameHeader(GPRInfo::regT2, RegisterFile::ReturnPC); // If we needed to perform an arity check we will already have moved the return address, // so enter after this. Label fromArityCheck(this); // Setup a pointer to the codeblock in the CallFrameHeader. emitPutImmediateToCallFrameHeader(m_codeBlock, RegisterFile::CodeBlock); // Plant a check that sufficient space is available in the RegisterFile. // FIXME: https://bugs.webkit.org/show_bug.cgi?id=56291 addPtr(Imm32(m_codeBlock->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1); Jump registerFileCheck = branchPtr(Below, AbsoluteAddress(m_globalData->interpreter->registerFile().addressOfEnd()), GPRInfo::regT1); // Return here after register file check. Label fromRegisterFileCheck = label(); // === Stage 2 - Function body code generation === // // We generate the speculative code path, followed by the non-speculative // code for the function. Next we need to link the two together, making // bail-outs from the speculative path jump to the corresponding point on // the non-speculative one (and generating any code necessary to juggle // register values around, rebox values, and ensure spilled, to match the // non-speculative path's requirements). #if DFG_JIT_BREAK_ON_EVERY_FUNCTION // Handy debug tool! breakpoint(); #endif // First generate the speculative path. Label speculativePathBegin = label(); SpeculativeJIT speculative(*this); #if !DFG_DEBUG_LOCAL_DISBALE_SPECULATIVE bool compiledSpeculative = speculative.compile(); #else bool compiledSpeculative = false; #endif // Next, generate the non-speculative path. We pass this a SpeculationCheckIndexIterator // to allow it to check which nodes in the graph may bail out, and may need to reenter the // non-speculative path. if (compiledSpeculative) { SpeculationCheckIndexIterator checkIterator(speculative.speculationChecks()); NonSpeculativeJIT nonSpeculative(*this); nonSpeculative.compile(checkIterator); // Link the bail-outs from the speculative path to the corresponding entry points into the non-speculative one. linkSpeculationChecks(speculative, nonSpeculative); } else { // If compilation through the SpeculativeJIT failed, throw away the code we generated. m_calls.clear(); rewindToLabel(speculativePathBegin); SpeculationCheckVector noChecks; SpeculationCheckIndexIterator checkIterator(noChecks); NonSpeculativeJIT nonSpeculative(*this); nonSpeculative.compile(checkIterator); } // === Stage 3 - Function footer code generation === // // Generate code to lookup and jump to exception handlers, to perform the slow // register file check (if the fast one in the function header fails), and // generate the entry point with arity check. // Iterate over the m_calls vector, checking for exception checks, // and linking them to here. unsigned exceptionCheckCount = 0; for (unsigned i = 0; i < m_calls.size(); ++i) { Jump& exceptionCheck = m_calls[i].m_exceptionCheck; if (exceptionCheck.isSet()) { exceptionCheck.link(this); ++exceptionCheckCount; } } // If any exception checks were linked, generate code to lookup a handler. if (exceptionCheckCount) { // lookupExceptionHandler is passed two arguments, exec (the CallFrame*), and // an identifier for the operation that threw the exception, which we can use // to look up handler information. The identifier we use is the return address // of the call out from JIT code that threw the exception; this is still // available on the stack, just below the stack pointer! move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); peek(GPRInfo::argumentGPR1, -1); m_calls.append(CallRecord(call(), lookupExceptionHandler)); // lookupExceptionHandler leaves the handler CallFrame* in the returnValueGPR, // and the address of the handler in returnValueGPR2. jump(GPRInfo::returnValueGPR2); } // Generate the register file check; if the fast check in the function head fails, // we need to call out to a helper function to check whether more space is available. // FIXME: change this from a cti call to a DFG style operation (normal C calling conventions). registerFileCheck.link(this); move(stackPointerRegister, GPRInfo::argumentGPR0); poke(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*)); Call callRegisterFileCheck = call(); jump(fromRegisterFileCheck); // The fast entry point into a function does not check the correct number of arguments // have been passed to the call (we only use the fast entry point where we can statically // determine the correct number of arguments have been passed, or have already checked). // In cases where an arity check is necessary, we enter here. // FIXME: change this from a cti call to a DFG style operation (normal C calling conventions). Label arityCheck = label(); preserveReturnAddressAfterCall(GPRInfo::regT2); emitPutToCallFrameHeader(GPRInfo::regT2, RegisterFile::ReturnPC); branch32(Equal, GPRInfo::regT1, Imm32(m_codeBlock->m_numParameters)).linkTo(fromArityCheck, this); move(stackPointerRegister, GPRInfo::argumentGPR0); poke(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*)); Call callArityCheck = call(); move(GPRInfo::regT0, GPRInfo::callFrameRegister); jump(fromArityCheck); // === Stage 4 - Link === // // Link the code, populate data in CodeBlock data structures. LinkBuffer linkBuffer(*m_globalData, this, m_globalData->executableAllocator); #if DFG_DEBUG_VERBOSE fprintf(stderr, "JIT code start at %p\n", linkBuffer.debugAddress()); #endif // Link all calls out from the JIT code to their respective functions. for (unsigned i = 0; i < m_calls.size(); ++i) linkBuffer.link(m_calls[i].m_call, m_calls[i].m_function); if (m_codeBlock->needsCallReturnIndices()) { m_codeBlock->callReturnIndexVector().reserveCapacity(exceptionCheckCount); for (unsigned i = 0; i < m_calls.size(); ++i) { if (m_calls[i].m_exceptionCheck.isSet()) { unsigned returnAddressOffset = linkBuffer.returnAddressOffset(m_calls[i].m_call); unsigned exceptionInfo = m_calls[i].m_exceptionInfo; m_codeBlock->callReturnIndexVector().append(CallReturnOffsetToBytecodeOffset(returnAddressOffset, exceptionInfo)); } } } // FIXME: switch the register file check & arity check over to DFGOpertaion style calls, not JIT stubs. linkBuffer.link(callRegisterFileCheck, cti_register_file_check); linkBuffer.link(callArityCheck, m_codeBlock->m_isConstructor ? cti_op_construct_arityCheck : cti_op_call_arityCheck); entryWithArityCheck = linkBuffer.locationOf(arityCheck); entry = linkBuffer.finalizeCode(); }