RegexOptimizer.cpp 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418
  1. /*
  2. * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Debug.h>
  7. #include <AK/Function.h>
  8. #include <AK/Queue.h>
  9. #include <AK/QuickSort.h>
  10. #include <AK/RedBlackTree.h>
  11. #include <AK/Stack.h>
  12. #include <AK/Trie.h>
  13. #include <LibRegex/Regex.h>
  14. #include <LibRegex/RegexBytecodeStreamOptimizer.h>
  15. #include <LibUnicode/CharacterTypes.h>
  16. #if REGEX_DEBUG
  17. # include <AK/ScopeGuard.h>
  18. # include <AK/ScopeLogger.h>
  19. #endif
  20. namespace regex {
  21. using Detail::Block;
  22. template<typename Parser>
  23. void Regex<Parser>::run_optimization_passes()
  24. {
  25. parser_result.bytecode.flatten();
  26. auto blocks = split_basic_blocks(parser_result.bytecode);
  27. if (attempt_rewrite_entire_match_as_substring_search(blocks))
  28. return;
  29. // Rewrite fork loops as atomic groups
  30. // e.g. a*b -> (ATOMIC a*)b
  31. attempt_rewrite_loops_as_atomic_groups(blocks);
  32. // FIXME: "There are a few more conditions this can be true in (e.g. within an arbitrarily nested capture group)"
  33. MatchState state;
  34. auto& opcode = parser_result.bytecode.get_opcode(state);
  35. if (opcode.opcode_id() == OpCodeId::CheckBegin)
  36. parser_result.optimization_data.only_start_of_line = true;
  37. parser_result.bytecode.flatten();
  38. }
  39. template<typename Parser>
  40. typename Regex<Parser>::BasicBlockList Regex<Parser>::split_basic_blocks(ByteCode const& bytecode)
  41. {
  42. BasicBlockList block_boundaries;
  43. size_t end_of_last_block = 0;
  44. auto bytecode_size = bytecode.size();
  45. MatchState state;
  46. state.instruction_position = 0;
  47. auto check_jump = [&]<typename T>(OpCode const& opcode) {
  48. auto& op = static_cast<T const&>(opcode);
  49. ssize_t jump_offset = op.size() + op.offset();
  50. if (jump_offset >= 0) {
  51. block_boundaries.append({ end_of_last_block, state.instruction_position });
  52. end_of_last_block = state.instruction_position + opcode.size();
  53. } else {
  54. // This op jumps back, see if that's within this "block".
  55. if (jump_offset + state.instruction_position > end_of_last_block) {
  56. // Split the block!
  57. block_boundaries.append({ end_of_last_block, jump_offset + state.instruction_position });
  58. block_boundaries.append({ jump_offset + state.instruction_position, state.instruction_position });
  59. end_of_last_block = state.instruction_position + opcode.size();
  60. } else {
  61. // Nope, it's just a jump to another block
  62. block_boundaries.append({ end_of_last_block, state.instruction_position });
  63. end_of_last_block = state.instruction_position + opcode.size();
  64. }
  65. }
  66. };
  67. for (;;) {
  68. auto& opcode = bytecode.get_opcode(state);
  69. switch (opcode.opcode_id()) {
  70. case OpCodeId::Jump:
  71. check_jump.template operator()<OpCode_Jump>(opcode);
  72. break;
  73. case OpCodeId::JumpNonEmpty:
  74. check_jump.template operator()<OpCode_JumpNonEmpty>(opcode);
  75. break;
  76. case OpCodeId::ForkJump:
  77. check_jump.template operator()<OpCode_ForkJump>(opcode);
  78. break;
  79. case OpCodeId::ForkStay:
  80. check_jump.template operator()<OpCode_ForkStay>(opcode);
  81. break;
  82. case OpCodeId::FailForks:
  83. block_boundaries.append({ end_of_last_block, state.instruction_position });
  84. end_of_last_block = state.instruction_position + opcode.size();
  85. break;
  86. case OpCodeId::Repeat: {
  87. // Repeat produces two blocks, one containing its repeated expr, and one after that.
  88. auto repeat_start = state.instruction_position - static_cast<OpCode_Repeat const&>(opcode).offset();
  89. if (repeat_start > end_of_last_block)
  90. block_boundaries.append({ end_of_last_block, repeat_start });
  91. block_boundaries.append({ repeat_start, state.instruction_position });
  92. end_of_last_block = state.instruction_position + opcode.size();
  93. break;
  94. }
  95. default:
  96. break;
  97. }
  98. auto next_ip = state.instruction_position + opcode.size();
  99. if (next_ip < bytecode_size)
  100. state.instruction_position = next_ip;
  101. else
  102. break;
  103. }
  104. if (end_of_last_block < bytecode_size)
  105. block_boundaries.append({ end_of_last_block, bytecode_size });
  106. quick_sort(block_boundaries, [](auto& a, auto& b) { return a.start < b.start; });
  107. return block_boundaries;
  108. }
  109. static bool has_overlap(Vector<CompareTypeAndValuePair> const& lhs, Vector<CompareTypeAndValuePair> const& rhs)
  110. {
  111. // We have to fully interpret the two sequences to determine if they overlap (that is, keep track of inversion state and what ranges they cover).
  112. bool inverse { false };
  113. bool temporary_inverse { false };
  114. bool reset_temporary_inverse { false };
  115. auto current_lhs_inversion_state = [&]() -> bool { return temporary_inverse ^ inverse; };
  116. RedBlackTree<u32, u32> lhs_ranges;
  117. RedBlackTree<u32, u32> lhs_negated_ranges;
  118. HashTable<CharClass> lhs_char_classes;
  119. HashTable<CharClass> lhs_negated_char_classes;
  120. auto has_any_unicode_property = false;
  121. HashTable<Unicode::GeneralCategory> lhs_unicode_general_categories;
  122. HashTable<Unicode::Property> lhs_unicode_properties;
  123. HashTable<Unicode::Script> lhs_unicode_scripts;
  124. HashTable<Unicode::Script> lhs_unicode_script_extensions;
  125. HashTable<Unicode::GeneralCategory> lhs_negated_unicode_general_categories;
  126. HashTable<Unicode::Property> lhs_negated_unicode_properties;
  127. HashTable<Unicode::Script> lhs_negated_unicode_scripts;
  128. HashTable<Unicode::Script> lhs_negated_unicode_script_extensions;
  129. auto any_unicode_property_matches = [&](u32 code_point) {
  130. if (any_of(lhs_negated_unicode_general_categories, [code_point](auto category) { return Unicode::code_point_has_general_category(code_point, category); }))
  131. return false;
  132. if (any_of(lhs_negated_unicode_properties, [code_point](auto property) { return Unicode::code_point_has_property(code_point, property); }))
  133. return false;
  134. if (any_of(lhs_negated_unicode_scripts, [code_point](auto script) { return Unicode::code_point_has_script(code_point, script); }))
  135. return false;
  136. if (any_of(lhs_negated_unicode_script_extensions, [code_point](auto script) { return Unicode::code_point_has_script_extension(code_point, script); }))
  137. return false;
  138. if (any_of(lhs_unicode_general_categories, [code_point](auto category) { return Unicode::code_point_has_general_category(code_point, category); }))
  139. return true;
  140. if (any_of(lhs_unicode_properties, [code_point](auto property) { return Unicode::code_point_has_property(code_point, property); }))
  141. return true;
  142. if (any_of(lhs_unicode_scripts, [code_point](auto script) { return Unicode::code_point_has_script(code_point, script); }))
  143. return true;
  144. if (any_of(lhs_unicode_script_extensions, [code_point](auto script) { return Unicode::code_point_has_script_extension(code_point, script); }))
  145. return true;
  146. return false;
  147. };
  148. auto range_contains = [&]<typename T>(T& value) -> bool {
  149. u32 start;
  150. u32 end;
  151. if constexpr (IsSame<T, CharRange>) {
  152. start = value.from;
  153. end = value.to;
  154. } else {
  155. start = value;
  156. end = value;
  157. }
  158. if (has_any_unicode_property) {
  159. // We have some properties, and a range is present
  160. // Instead of checking every single code point in the range, assume it's a match.
  161. return start != end || any_unicode_property_matches(start);
  162. }
  163. auto* max = lhs_ranges.find_smallest_not_below(start);
  164. return max && *max <= end;
  165. };
  166. auto char_class_contains = [&](CharClass const& value) -> bool {
  167. if (lhs_char_classes.contains(value))
  168. return true;
  169. if (lhs_negated_char_classes.contains(value))
  170. return false;
  171. if (lhs_ranges.is_empty())
  172. return false;
  173. for (auto it = lhs_ranges.begin(); it != lhs_ranges.end(); ++it) {
  174. auto start = it.key();
  175. auto end = *it;
  176. for (u32 ch = start; ch <= end; ++ch) {
  177. if (OpCode_Compare::matches_character_class(value, ch, false))
  178. return true;
  179. }
  180. }
  181. return false;
  182. };
  183. for (auto const& pair : lhs) {
  184. if (reset_temporary_inverse) {
  185. reset_temporary_inverse = false;
  186. temporary_inverse = false;
  187. } else {
  188. reset_temporary_inverse = true;
  189. }
  190. switch (pair.type) {
  191. case CharacterCompareType::Inverse:
  192. inverse = !inverse;
  193. break;
  194. case CharacterCompareType::TemporaryInverse:
  195. temporary_inverse = true;
  196. reset_temporary_inverse = true;
  197. break;
  198. case CharacterCompareType::AnyChar:
  199. // Special case: if not inverted, AnyChar is always in the range.
  200. if (!current_lhs_inversion_state())
  201. return true;
  202. break;
  203. case CharacterCompareType::Char:
  204. if (!current_lhs_inversion_state())
  205. lhs_ranges.insert(pair.value, pair.value);
  206. else
  207. lhs_negated_ranges.insert(pair.value, pair.value);
  208. break;
  209. case CharacterCompareType::String:
  210. // FIXME: We just need to look at the last character of this string, but we only have the first character here.
  211. // Just bail out to avoid false positives.
  212. return true;
  213. case CharacterCompareType::CharClass:
  214. if (!current_lhs_inversion_state())
  215. lhs_char_classes.set(static_cast<CharClass>(pair.value));
  216. else
  217. lhs_negated_char_classes.set(static_cast<CharClass>(pair.value));
  218. break;
  219. case CharacterCompareType::CharRange: {
  220. auto range = CharRange(pair.value);
  221. if (!current_lhs_inversion_state())
  222. lhs_ranges.insert(range.from, range.to);
  223. else
  224. lhs_negated_ranges.insert(range.from, range.to);
  225. break;
  226. }
  227. case CharacterCompareType::LookupTable:
  228. // We've transformed this into a series of ranges in flat_compares(), so bail out if we see it.
  229. return true;
  230. case CharacterCompareType::Reference:
  231. // We've handled this before coming here.
  232. break;
  233. case CharacterCompareType::Property:
  234. has_any_unicode_property = true;
  235. if (!current_lhs_inversion_state())
  236. lhs_unicode_properties.set(static_cast<Unicode::Property>(pair.value));
  237. else
  238. lhs_negated_unicode_properties.set(static_cast<Unicode::Property>(pair.value));
  239. break;
  240. case CharacterCompareType::GeneralCategory:
  241. has_any_unicode_property = true;
  242. if (!current_lhs_inversion_state())
  243. lhs_unicode_general_categories.set(static_cast<Unicode::GeneralCategory>(pair.value));
  244. else
  245. lhs_negated_unicode_general_categories.set(static_cast<Unicode::GeneralCategory>(pair.value));
  246. break;
  247. case CharacterCompareType::Script:
  248. has_any_unicode_property = true;
  249. if (!current_lhs_inversion_state())
  250. lhs_unicode_scripts.set(static_cast<Unicode::Script>(pair.value));
  251. else
  252. lhs_negated_unicode_scripts.set(static_cast<Unicode::Script>(pair.value));
  253. break;
  254. case CharacterCompareType::ScriptExtension:
  255. has_any_unicode_property = true;
  256. if (!current_lhs_inversion_state())
  257. lhs_unicode_script_extensions.set(static_cast<Unicode::Script>(pair.value));
  258. else
  259. lhs_negated_unicode_script_extensions.set(static_cast<Unicode::Script>(pair.value));
  260. break;
  261. case CharacterCompareType::And:
  262. case CharacterCompareType::Or:
  263. case CharacterCompareType::EndAndOr:
  264. // FIXME: These are too difficult to handle, so bail out.
  265. return true;
  266. case CharacterCompareType::Undefined:
  267. case CharacterCompareType::RangeExpressionDummy:
  268. // These do not occur in valid bytecode.
  269. VERIFY_NOT_REACHED();
  270. }
  271. }
  272. if constexpr (REGEX_DEBUG) {
  273. dbgln("lhs ranges:");
  274. for (auto it = lhs_ranges.begin(); it != lhs_ranges.end(); ++it)
  275. dbgln(" {}..{}", it.key(), *it);
  276. dbgln("lhs negated ranges:");
  277. for (auto it = lhs_negated_ranges.begin(); it != lhs_negated_ranges.end(); ++it)
  278. dbgln(" {}..{}", it.key(), *it);
  279. }
  280. temporary_inverse = false;
  281. reset_temporary_inverse = false;
  282. inverse = false;
  283. for (auto const& pair : rhs) {
  284. if (reset_temporary_inverse) {
  285. reset_temporary_inverse = false;
  286. temporary_inverse = false;
  287. } else {
  288. reset_temporary_inverse = true;
  289. }
  290. dbgln_if(REGEX_DEBUG, "check {} ({}) [inverted? {}]...", character_compare_type_name(pair.type), pair.value, current_lhs_inversion_state());
  291. switch (pair.type) {
  292. case CharacterCompareType::Inverse:
  293. inverse = !inverse;
  294. break;
  295. case CharacterCompareType::TemporaryInverse:
  296. temporary_inverse = true;
  297. reset_temporary_inverse = true;
  298. break;
  299. case CharacterCompareType::AnyChar:
  300. // Special case: if not inverted, AnyChar is always in the range.
  301. if (!current_lhs_inversion_state())
  302. return true;
  303. break;
  304. case CharacterCompareType::Char:
  305. if (current_lhs_inversion_state() ^ range_contains(pair.value))
  306. return true;
  307. break;
  308. case CharacterCompareType::String:
  309. // FIXME: We just need to look at the last character of this string, but we only have the first character here.
  310. // Just bail out to avoid false positives.
  311. return true;
  312. case CharacterCompareType::CharClass:
  313. if (current_lhs_inversion_state() ^ char_class_contains(static_cast<CharClass>(pair.value)))
  314. return true;
  315. break;
  316. case CharacterCompareType::CharRange: {
  317. auto range = CharRange(pair.value);
  318. if (current_lhs_inversion_state() ^ range_contains(range))
  319. return true;
  320. break;
  321. }
  322. case CharacterCompareType::LookupTable:
  323. // We've transformed this into a series of ranges in flat_compares(), so bail out if we see it.
  324. return true;
  325. case CharacterCompareType::Reference:
  326. // We've handled this before coming here.
  327. break;
  328. case CharacterCompareType::Property:
  329. // The only reasonable scenario where we can check these properties without spending too much time is if:
  330. // - the ranges are empty
  331. // - the char classes are empty
  332. // - the unicode properties are empty or contain only this property
  333. if (!lhs_ranges.is_empty() || !lhs_negated_ranges.is_empty() || !lhs_char_classes.is_empty() || !lhs_negated_char_classes.is_empty())
  334. return true;
  335. if (has_any_unicode_property && !lhs_unicode_properties.is_empty() && !lhs_negated_unicode_properties.is_empty()) {
  336. if (current_lhs_inversion_state() ^ lhs_unicode_properties.contains(static_cast<Unicode::Property>(pair.value)))
  337. return true;
  338. if (false == (current_lhs_inversion_state() ^ lhs_negated_unicode_properties.contains(static_cast<Unicode::Property>(pair.value))))
  339. return true;
  340. }
  341. break;
  342. case CharacterCompareType::GeneralCategory:
  343. if (!lhs_ranges.is_empty() || !lhs_negated_ranges.is_empty() || !lhs_char_classes.is_empty() || !lhs_negated_char_classes.is_empty())
  344. return true;
  345. if (has_any_unicode_property && !lhs_unicode_general_categories.is_empty() && !lhs_negated_unicode_general_categories.is_empty()) {
  346. if (current_lhs_inversion_state() ^ lhs_unicode_general_categories.contains(static_cast<Unicode::GeneralCategory>(pair.value)))
  347. return true;
  348. if (false == (current_lhs_inversion_state() ^ lhs_negated_unicode_general_categories.contains(static_cast<Unicode::GeneralCategory>(pair.value))))
  349. return true;
  350. }
  351. break;
  352. case CharacterCompareType::Script:
  353. if (!lhs_ranges.is_empty() || !lhs_negated_ranges.is_empty() || !lhs_char_classes.is_empty() || !lhs_negated_char_classes.is_empty())
  354. return true;
  355. if (has_any_unicode_property && !lhs_unicode_scripts.is_empty() && !lhs_negated_unicode_scripts.is_empty()) {
  356. if (current_lhs_inversion_state() ^ lhs_unicode_scripts.contains(static_cast<Unicode::Script>(pair.value)))
  357. return true;
  358. if (false == (current_lhs_inversion_state() ^ lhs_negated_unicode_scripts.contains(static_cast<Unicode::Script>(pair.value))))
  359. return true;
  360. }
  361. break;
  362. case CharacterCompareType::ScriptExtension:
  363. if (!lhs_ranges.is_empty() || !lhs_negated_ranges.is_empty() || !lhs_char_classes.is_empty() || !lhs_negated_char_classes.is_empty())
  364. return true;
  365. if (has_any_unicode_property && !lhs_unicode_script_extensions.is_empty() && !lhs_negated_unicode_script_extensions.is_empty()) {
  366. if (current_lhs_inversion_state() ^ lhs_unicode_script_extensions.contains(static_cast<Unicode::Script>(pair.value)))
  367. return true;
  368. if (false == (current_lhs_inversion_state() ^ lhs_negated_unicode_script_extensions.contains(static_cast<Unicode::Script>(pair.value))))
  369. return true;
  370. }
  371. break;
  372. case CharacterCompareType::And:
  373. case CharacterCompareType::Or:
  374. case CharacterCompareType::EndAndOr:
  375. // FIXME: These are too difficult to handle, so bail out.
  376. return true;
  377. case CharacterCompareType::Undefined:
  378. case CharacterCompareType::RangeExpressionDummy:
  379. // These do not occur in valid bytecode.
  380. VERIFY_NOT_REACHED();
  381. }
  382. }
  383. return false;
  384. }
  385. enum class AtomicRewritePreconditionResult {
  386. SatisfiedWithProperHeader,
  387. SatisfiedWithEmptyHeader,
  388. NotSatisfied,
  389. };
  390. static AtomicRewritePreconditionResult block_satisfies_atomic_rewrite_precondition(ByteCode const& bytecode, Block const& repeated_block, Block const& following_block)
  391. {
  392. Vector<Vector<CompareTypeAndValuePair>> repeated_values;
  393. HashTable<size_t> active_capture_groups;
  394. MatchState state;
  395. auto has_seen_actionable_opcode = false;
  396. for (state.instruction_position = repeated_block.start; state.instruction_position < repeated_block.end;) {
  397. auto& opcode = bytecode.get_opcode(state);
  398. switch (opcode.opcode_id()) {
  399. case OpCodeId::Compare: {
  400. has_seen_actionable_opcode = true;
  401. auto compares = static_cast<OpCode_Compare const&>(opcode).flat_compares();
  402. if (repeated_values.is_empty() && any_of(compares, [](auto& compare) { return compare.type == CharacterCompareType::AnyChar; }))
  403. return AtomicRewritePreconditionResult::NotSatisfied;
  404. repeated_values.append(move(compares));
  405. break;
  406. }
  407. case OpCodeId::CheckBegin:
  408. case OpCodeId::CheckEnd:
  409. has_seen_actionable_opcode = true;
  410. if (repeated_values.is_empty())
  411. return AtomicRewritePreconditionResult::SatisfiedWithProperHeader;
  412. break;
  413. case OpCodeId::CheckBoundary:
  414. // FIXME: What should we do with these? for now, let's fail.
  415. return AtomicRewritePreconditionResult::NotSatisfied;
  416. case OpCodeId::Restore:
  417. case OpCodeId::GoBack:
  418. return AtomicRewritePreconditionResult::NotSatisfied;
  419. case OpCodeId::SaveRightCaptureGroup:
  420. active_capture_groups.set(static_cast<OpCode_SaveRightCaptureGroup const&>(opcode).id());
  421. break;
  422. case OpCodeId::SaveLeftCaptureGroup:
  423. active_capture_groups.set(static_cast<OpCode_SaveLeftCaptureGroup const&>(opcode).id());
  424. break;
  425. case OpCodeId::ForkJump:
  426. case OpCodeId::ForkReplaceJump:
  427. case OpCodeId::JumpNonEmpty:
  428. // We could attempt to recursively resolve the follow set, but pretending that this just goes nowhere is faster.
  429. if (!has_seen_actionable_opcode)
  430. return AtomicRewritePreconditionResult::NotSatisfied;
  431. break;
  432. default:
  433. break;
  434. }
  435. state.instruction_position += opcode.size();
  436. }
  437. dbgln_if(REGEX_DEBUG, "Found {} entries in reference", repeated_values.size());
  438. dbgln_if(REGEX_DEBUG, "Found {} active capture groups", active_capture_groups.size());
  439. bool following_block_has_at_least_one_compare = false;
  440. // Find the first compare in the following block, it must NOT match any of the values in `repeated_values'.
  441. auto final_instruction = following_block.start;
  442. for (state.instruction_position = following_block.start; state.instruction_position < following_block.end;) {
  443. final_instruction = state.instruction_position;
  444. auto& opcode = bytecode.get_opcode(state);
  445. switch (opcode.opcode_id()) {
  446. // Note: These have to exist since we're effectively repeating the following block as well
  447. case OpCodeId::SaveRightCaptureGroup:
  448. active_capture_groups.set(static_cast<OpCode_SaveRightCaptureGroup const&>(opcode).id());
  449. break;
  450. case OpCodeId::SaveLeftCaptureGroup:
  451. active_capture_groups.set(static_cast<OpCode_SaveLeftCaptureGroup const&>(opcode).id());
  452. break;
  453. case OpCodeId::Compare: {
  454. following_block_has_at_least_one_compare = true;
  455. // We found a compare, let's see what it has.
  456. auto compares = static_cast<OpCode_Compare const&>(opcode).flat_compares();
  457. if (compares.is_empty())
  458. break;
  459. if (any_of(compares, [&](auto& compare) {
  460. return compare.type == CharacterCompareType::AnyChar
  461. || (compare.type == CharacterCompareType::Reference && active_capture_groups.contains(compare.value));
  462. }))
  463. return AtomicRewritePreconditionResult::NotSatisfied;
  464. if (any_of(repeated_values, [&](auto& repeated_value) { return has_overlap(compares, repeated_value); }))
  465. return AtomicRewritePreconditionResult::NotSatisfied;
  466. return AtomicRewritePreconditionResult::SatisfiedWithProperHeader;
  467. }
  468. case OpCodeId::CheckBegin:
  469. case OpCodeId::CheckEnd:
  470. return AtomicRewritePreconditionResult::SatisfiedWithProperHeader; // Nothing can match the end!
  471. case OpCodeId::CheckBoundary:
  472. // FIXME: What should we do with these? For now, consider them a failure.
  473. return AtomicRewritePreconditionResult::NotSatisfied;
  474. case OpCodeId::ForkJump:
  475. case OpCodeId::ForkReplaceJump:
  476. case OpCodeId::JumpNonEmpty:
  477. // See note in the previous switch, same cases.
  478. if (!following_block_has_at_least_one_compare)
  479. return AtomicRewritePreconditionResult::NotSatisfied;
  480. break;
  481. default:
  482. break;
  483. }
  484. state.instruction_position += opcode.size();
  485. }
  486. // If the following block falls through, we can't rewrite it.
  487. state.instruction_position = final_instruction;
  488. switch (bytecode.get_opcode(state).opcode_id()) {
  489. case OpCodeId::Jump:
  490. case OpCodeId::JumpNonEmpty:
  491. case OpCodeId::ForkJump:
  492. case OpCodeId::ForkReplaceJump:
  493. break;
  494. default:
  495. return AtomicRewritePreconditionResult::NotSatisfied;
  496. }
  497. if (following_block_has_at_least_one_compare)
  498. return AtomicRewritePreconditionResult::SatisfiedWithProperHeader;
  499. return AtomicRewritePreconditionResult::SatisfiedWithEmptyHeader;
  500. }
  501. template<typename Parser>
  502. bool Regex<Parser>::attempt_rewrite_entire_match_as_substring_search(BasicBlockList const& basic_blocks)
  503. {
  504. // If there's no jumps, we can probably rewrite this as a substring search (Compare { string = str }).
  505. if (basic_blocks.size() > 1)
  506. return false;
  507. if (basic_blocks.is_empty()) {
  508. parser_result.optimization_data.pure_substring_search = ""sv;
  509. return true; // Empty regex, sure.
  510. }
  511. auto& bytecode = parser_result.bytecode;
  512. auto is_unicode = parser_result.options.has_flag_set(AllFlags::Unicode);
  513. // We have a single basic block, let's see if it's a series of character or string compares.
  514. StringBuilder final_string;
  515. MatchState state;
  516. while (state.instruction_position < bytecode.size()) {
  517. auto& opcode = bytecode.get_opcode(state);
  518. switch (opcode.opcode_id()) {
  519. case OpCodeId::Compare: {
  520. auto& compare = static_cast<OpCode_Compare const&>(opcode);
  521. for (auto& flat_compare : compare.flat_compares()) {
  522. if (flat_compare.type != CharacterCompareType::Char)
  523. return false;
  524. if (is_unicode || flat_compare.value <= 0x7f)
  525. final_string.append_code_point(flat_compare.value);
  526. else
  527. final_string.append(bit_cast<char>(static_cast<u8>(flat_compare.value)));
  528. }
  529. break;
  530. }
  531. default:
  532. return false;
  533. }
  534. state.instruction_position += opcode.size();
  535. }
  536. parser_result.optimization_data.pure_substring_search = final_string.to_byte_string();
  537. return true;
  538. }
  539. template<typename Parser>
  540. void Regex<Parser>::attempt_rewrite_loops_as_atomic_groups(BasicBlockList const& basic_blocks)
  541. {
  542. auto& bytecode = parser_result.bytecode;
  543. if constexpr (REGEX_DEBUG) {
  544. RegexDebug dbg;
  545. dbg.print_bytecode(*this);
  546. for (auto const& block : basic_blocks)
  547. dbgln("block from {} to {}", block.start, block.end);
  548. }
  549. // A pattern such as:
  550. // bb0 | RE0
  551. // | ForkX bb0
  552. // -------------------------
  553. // bb1 | RE1
  554. // can be rewritten as:
  555. // -------------------------
  556. // bb0 | RE0
  557. // | ForkReplaceX bb0
  558. // -------------------------
  559. // bb1 | RE1
  560. // provided that first(RE1) not-in end(RE0), which is to say
  561. // that RE1 cannot start with whatever RE0 has matched (ever).
  562. //
  563. // Alternatively, a second form of this pattern can also occur:
  564. // bb0 | *
  565. // | ForkX bb2
  566. // ------------------------
  567. // bb1 | RE0
  568. // | Jump bb0
  569. // ------------------------
  570. // bb2 | RE1
  571. // which can be transformed (with the same preconditions) to:
  572. // bb0 | *
  573. // | ForkReplaceX bb2
  574. // ------------------------
  575. // bb1 | RE0
  576. // | Jump bb0
  577. // ------------------------
  578. // bb2 | RE1
  579. enum class AlternateForm {
  580. DirectLoopWithoutHeader, // loop without proper header, a block forking to itself. i.e. the first form.
  581. DirectLoopWithoutHeaderAndEmptyFollow, // loop without proper header, a block forking to itself. i.e. the first form but with RE1 being empty.
  582. DirectLoopWithHeader, // loop with proper header, i.e. the second form.
  583. };
  584. struct CandidateBlock {
  585. Block forking_block;
  586. Optional<Block> new_target_block;
  587. AlternateForm form;
  588. };
  589. Vector<CandidateBlock> candidate_blocks;
  590. auto is_an_eligible_jump = [](OpCode const& opcode, size_t ip, size_t block_start, AlternateForm alternate_form) {
  591. switch (opcode.opcode_id()) {
  592. case OpCodeId::JumpNonEmpty: {
  593. auto const& op = static_cast<OpCode_JumpNonEmpty const&>(opcode);
  594. auto form = op.form();
  595. if (form != OpCodeId::Jump && alternate_form == AlternateForm::DirectLoopWithHeader)
  596. return false;
  597. if (form != OpCodeId::ForkJump && form != OpCodeId::ForkStay && alternate_form == AlternateForm::DirectLoopWithoutHeader)
  598. return false;
  599. return op.offset() + ip + opcode.size() == block_start;
  600. }
  601. case OpCodeId::ForkJump:
  602. if (alternate_form == AlternateForm::DirectLoopWithHeader)
  603. return false;
  604. return static_cast<OpCode_ForkJump const&>(opcode).offset() + ip + opcode.size() == block_start;
  605. case OpCodeId::ForkStay:
  606. if (alternate_form == AlternateForm::DirectLoopWithHeader)
  607. return false;
  608. return static_cast<OpCode_ForkStay const&>(opcode).offset() + ip + opcode.size() == block_start;
  609. case OpCodeId::Jump:
  610. // Infinite loop does *not* produce forks.
  611. if (alternate_form == AlternateForm::DirectLoopWithoutHeader)
  612. return false;
  613. if (alternate_form == AlternateForm::DirectLoopWithHeader)
  614. return static_cast<OpCode_Jump const&>(opcode).offset() + ip + opcode.size() == block_start;
  615. VERIFY_NOT_REACHED();
  616. default:
  617. return false;
  618. }
  619. };
  620. for (size_t i = 0; i < basic_blocks.size(); ++i) {
  621. auto forking_block = basic_blocks[i];
  622. Optional<Block> fork_fallback_block;
  623. if (i + 1 < basic_blocks.size())
  624. fork_fallback_block = basic_blocks[i + 1];
  625. MatchState state;
  626. // Check if the last instruction in this block is a jump to the block itself:
  627. {
  628. state.instruction_position = forking_block.end;
  629. auto& opcode = bytecode.get_opcode(state);
  630. if (is_an_eligible_jump(opcode, state.instruction_position, forking_block.start, AlternateForm::DirectLoopWithoutHeader)) {
  631. // We've found RE0 (and RE1 is just the following block, if any), let's see if the precondition applies.
  632. // if RE1 is empty, there's no first(RE1), so this is an automatic pass.
  633. if (!fork_fallback_block.has_value()
  634. || (fork_fallback_block->end == fork_fallback_block->start && block_satisfies_atomic_rewrite_precondition(bytecode, forking_block, *fork_fallback_block) != AtomicRewritePreconditionResult::NotSatisfied)) {
  635. candidate_blocks.append({ forking_block, fork_fallback_block, AlternateForm::DirectLoopWithoutHeader });
  636. break;
  637. }
  638. auto precondition = block_satisfies_atomic_rewrite_precondition(bytecode, forking_block, *fork_fallback_block);
  639. if (precondition == AtomicRewritePreconditionResult::SatisfiedWithProperHeader) {
  640. candidate_blocks.append({ forking_block, fork_fallback_block, AlternateForm::DirectLoopWithoutHeader });
  641. break;
  642. }
  643. if (precondition == AtomicRewritePreconditionResult::SatisfiedWithEmptyHeader) {
  644. candidate_blocks.append({ forking_block, fork_fallback_block, AlternateForm::DirectLoopWithoutHeaderAndEmptyFollow });
  645. break;
  646. }
  647. }
  648. }
  649. // Check if the last instruction in the last block is a direct jump to this block
  650. if (fork_fallback_block.has_value()) {
  651. state.instruction_position = fork_fallback_block->end;
  652. auto& opcode = bytecode.get_opcode(state);
  653. if (is_an_eligible_jump(opcode, state.instruction_position, forking_block.start, AlternateForm::DirectLoopWithHeader)) {
  654. // We've found bb1 and bb0, let's just make sure that bb0 forks to bb2.
  655. state.instruction_position = forking_block.end;
  656. auto& opcode = bytecode.get_opcode(state);
  657. if (opcode.opcode_id() == OpCodeId::ForkJump || opcode.opcode_id() == OpCodeId::ForkStay) {
  658. Optional<Block> block_following_fork_fallback;
  659. if (i + 2 < basic_blocks.size())
  660. block_following_fork_fallback = basic_blocks[i + 2];
  661. if (!block_following_fork_fallback.has_value()
  662. || block_satisfies_atomic_rewrite_precondition(bytecode, *fork_fallback_block, *block_following_fork_fallback) != AtomicRewritePreconditionResult::NotSatisfied) {
  663. candidate_blocks.append({ forking_block, {}, AlternateForm::DirectLoopWithHeader });
  664. break;
  665. }
  666. }
  667. }
  668. }
  669. }
  670. dbgln_if(REGEX_DEBUG, "Found {} candidate blocks", candidate_blocks.size());
  671. if (candidate_blocks.is_empty()) {
  672. dbgln_if(REGEX_DEBUG, "Failed to find anything for {}", pattern_value);
  673. return;
  674. }
  675. RedBlackTree<size_t, size_t> needed_patches;
  676. // Reverse the blocks, so we can patch the bytecode without messing with the latter patches.
  677. quick_sort(candidate_blocks, [](auto& a, auto& b) { return b.forking_block.start > a.forking_block.start; });
  678. for (auto& candidate : candidate_blocks) {
  679. // Note that both forms share a ForkReplace patch in forking_block.
  680. // Patch the ForkX in forking_block to be a ForkReplaceX instead.
  681. auto& opcode_id = bytecode[candidate.forking_block.end];
  682. if (opcode_id == (ByteCodeValueType)OpCodeId::ForkStay) {
  683. opcode_id = (ByteCodeValueType)OpCodeId::ForkReplaceStay;
  684. } else if (opcode_id == (ByteCodeValueType)OpCodeId::ForkJump) {
  685. opcode_id = (ByteCodeValueType)OpCodeId::ForkReplaceJump;
  686. } else if (opcode_id == (ByteCodeValueType)OpCodeId::JumpNonEmpty) {
  687. auto& jump_opcode_id = bytecode[candidate.forking_block.end + 3];
  688. if (jump_opcode_id == (ByteCodeValueType)OpCodeId::ForkStay)
  689. jump_opcode_id = (ByteCodeValueType)OpCodeId::ForkReplaceStay;
  690. else if (jump_opcode_id == (ByteCodeValueType)OpCodeId::ForkJump)
  691. jump_opcode_id = (ByteCodeValueType)OpCodeId::ForkReplaceJump;
  692. else
  693. VERIFY_NOT_REACHED();
  694. } else {
  695. VERIFY_NOT_REACHED();
  696. }
  697. }
  698. if (!needed_patches.is_empty()) {
  699. MatchState state;
  700. auto bytecode_size = bytecode.size();
  701. state.instruction_position = 0;
  702. struct Patch {
  703. ssize_t value;
  704. size_t offset;
  705. bool should_negate { false };
  706. };
  707. for (;;) {
  708. if (state.instruction_position >= bytecode_size)
  709. break;
  710. auto& opcode = bytecode.get_opcode(state);
  711. Stack<Patch, 2> patch_points;
  712. switch (opcode.opcode_id()) {
  713. case OpCodeId::Jump:
  714. patch_points.push({ static_cast<OpCode_Jump const&>(opcode).offset(), state.instruction_position + 1 });
  715. break;
  716. case OpCodeId::JumpNonEmpty:
  717. patch_points.push({ static_cast<OpCode_JumpNonEmpty const&>(opcode).offset(), state.instruction_position + 1 });
  718. patch_points.push({ static_cast<OpCode_JumpNonEmpty const&>(opcode).checkpoint(), state.instruction_position + 2 });
  719. break;
  720. case OpCodeId::ForkJump:
  721. patch_points.push({ static_cast<OpCode_ForkJump const&>(opcode).offset(), state.instruction_position + 1 });
  722. break;
  723. case OpCodeId::ForkStay:
  724. patch_points.push({ static_cast<OpCode_ForkStay const&>(opcode).offset(), state.instruction_position + 1 });
  725. break;
  726. case OpCodeId::Repeat:
  727. patch_points.push({ -(ssize_t) static_cast<OpCode_Repeat const&>(opcode).offset(), state.instruction_position + 1, true });
  728. break;
  729. default:
  730. break;
  731. }
  732. while (!patch_points.is_empty()) {
  733. auto& patch_point = patch_points.top();
  734. auto target_offset = patch_point.value + state.instruction_position + opcode.size();
  735. constexpr auto do_patch = [](auto& patch_it, auto& patch_point, auto& target_offset, auto& bytecode, auto ip) {
  736. if (patch_it.key() == ip)
  737. return;
  738. if (patch_point.value < 0 && target_offset <= patch_it.key() && ip > patch_it.key())
  739. bytecode[patch_point.offset] += (patch_point.should_negate ? 1 : -1) * (*patch_it);
  740. else if (patch_point.value > 0 && target_offset >= patch_it.key() && ip < patch_it.key())
  741. bytecode[patch_point.offset] += (patch_point.should_negate ? -1 : 1) * (*patch_it);
  742. };
  743. if (auto patch_it = needed_patches.find_largest_not_above_iterator(target_offset); !patch_it.is_end())
  744. do_patch(patch_it, patch_point, target_offset, bytecode, state.instruction_position);
  745. else if (auto patch_it = needed_patches.find_largest_not_above_iterator(state.instruction_position); !patch_it.is_end())
  746. do_patch(patch_it, patch_point, target_offset, bytecode, state.instruction_position);
  747. patch_points.pop();
  748. }
  749. state.instruction_position += opcode.size();
  750. }
  751. }
  752. if constexpr (REGEX_DEBUG) {
  753. warnln("Transformed to:");
  754. RegexDebug dbg;
  755. dbg.print_bytecode(*this);
  756. }
  757. }
  758. void Optimizer::append_alternation(ByteCode& target, ByteCode&& left, ByteCode&& right)
  759. {
  760. Array<ByteCode, 2> alternatives;
  761. alternatives[0] = move(left);
  762. alternatives[1] = move(right);
  763. append_alternation(target, alternatives);
  764. }
  765. template<typename K, typename V, typename KTraits>
  766. using OrderedHashMapForTrie = OrderedHashMap<K, V, KTraits>;
  767. void Optimizer::append_alternation(ByteCode& target, Span<ByteCode> alternatives)
  768. {
  769. if (alternatives.size() == 0)
  770. return;
  771. if (alternatives.size() == 1)
  772. return target.extend(move(alternatives[0]));
  773. if (all_of(alternatives, [](auto& x) { return x.is_empty(); }))
  774. return;
  775. for (auto& entry : alternatives)
  776. entry.flatten();
  777. #if REGEX_DEBUG
  778. ScopeLogger<true> log;
  779. warnln("Alternations:");
  780. RegexDebug dbg;
  781. for (auto& entry : alternatives) {
  782. warnln("----------");
  783. dbg.print_bytecode(entry);
  784. }
  785. ScopeGuard print_at_end {
  786. [&] {
  787. warnln("======================");
  788. RegexDebug dbg;
  789. dbg.print_bytecode(target);
  790. }
  791. };
  792. #endif
  793. // First, find incoming jump edges.
  794. // We need them for two reasons:
  795. // - We need to distinguish between insn-A-jumped-to-by-insn-B and insn-A-jumped-to-by-insn-C (as otherwise we'd break trie invariants)
  796. // - We need to know which jumps to patch when we're done
  797. struct JumpEdge {
  798. Span<ByteCodeValueType const> jump_insn;
  799. };
  800. Vector<HashMap<size_t, Vector<JumpEdge>>> incoming_jump_edges_for_each_alternative;
  801. incoming_jump_edges_for_each_alternative.resize(alternatives.size());
  802. auto has_any_backwards_jump = false;
  803. MatchState state;
  804. for (size_t i = 0; i < alternatives.size(); ++i) {
  805. auto& alternative = alternatives[i];
  806. // Add a jump to the "end" of the block; this is implicit in the bytecode, but we need it to be explicit in the trie.
  807. // Jump{offset=0}
  808. alternative.append(static_cast<ByteCodeValueType>(OpCodeId::Jump));
  809. alternative.append(0);
  810. auto& incoming_jump_edges = incoming_jump_edges_for_each_alternative[i];
  811. auto alternative_bytes = alternative.spans<1>().singular_span();
  812. for (state.instruction_position = 0; state.instruction_position < alternative.size();) {
  813. auto& opcode = alternative.get_opcode(state);
  814. auto opcode_bytes = alternative_bytes.slice(state.instruction_position, opcode.size());
  815. switch (opcode.opcode_id()) {
  816. case OpCodeId::Jump:
  817. incoming_jump_edges.ensure(static_cast<OpCode_Jump const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  818. has_any_backwards_jump |= static_cast<OpCode_Jump const&>(opcode).offset() < 0;
  819. break;
  820. case OpCodeId::JumpNonEmpty:
  821. incoming_jump_edges.ensure(static_cast<OpCode_JumpNonEmpty const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  822. has_any_backwards_jump |= static_cast<OpCode_JumpNonEmpty const&>(opcode).offset() < 0;
  823. break;
  824. case OpCodeId::ForkJump:
  825. incoming_jump_edges.ensure(static_cast<OpCode_ForkJump const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  826. has_any_backwards_jump |= static_cast<OpCode_ForkJump const&>(opcode).offset() < 0;
  827. break;
  828. case OpCodeId::ForkStay:
  829. incoming_jump_edges.ensure(static_cast<OpCode_ForkStay const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  830. has_any_backwards_jump |= static_cast<OpCode_ForkStay const&>(opcode).offset() < 0;
  831. break;
  832. case OpCodeId::ForkReplaceJump:
  833. incoming_jump_edges.ensure(static_cast<OpCode_ForkReplaceJump const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  834. has_any_backwards_jump |= static_cast<OpCode_ForkReplaceJump const&>(opcode).offset() < 0;
  835. break;
  836. case OpCodeId::ForkReplaceStay:
  837. incoming_jump_edges.ensure(static_cast<OpCode_ForkReplaceStay const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  838. has_any_backwards_jump |= static_cast<OpCode_ForkReplaceStay const&>(opcode).offset() < 0;
  839. break;
  840. case OpCodeId::Repeat:
  841. incoming_jump_edges.ensure(state.instruction_position - static_cast<OpCode_Repeat const&>(opcode).offset()).append({ opcode_bytes });
  842. has_any_backwards_jump = true;
  843. break;
  844. default:
  845. break;
  846. }
  847. state.instruction_position += opcode.size();
  848. }
  849. }
  850. struct QualifiedIP {
  851. size_t alternative_index;
  852. size_t instruction_position;
  853. };
  854. using Tree = Trie<DisjointSpans<ByteCodeValueType const>, Vector<QualifiedIP>, Traits<DisjointSpans<ByteCodeValueType const>>, void, OrderedHashMapForTrie>;
  855. Tree trie { {} }; // Root node is empty, key{ instruction_bytes, dependent_instruction_bytes... } -> IP
  856. size_t common_hits = 0;
  857. size_t total_nodes = 0;
  858. size_t total_bytecode_entries_in_tree = 0;
  859. for (size_t i = 0; i < alternatives.size(); ++i) {
  860. auto& alternative = alternatives[i];
  861. auto& incoming_jump_edges = incoming_jump_edges_for_each_alternative[i];
  862. auto* active_node = &trie;
  863. auto alternative_span = alternative.spans<1>().singular_span();
  864. for (state.instruction_position = 0; state.instruction_position < alternative_span.size();) {
  865. total_nodes += 1;
  866. auto& opcode = alternative.get_opcode(state);
  867. auto opcode_bytes = alternative_span.slice(state.instruction_position, opcode.size());
  868. Vector<Span<ByteCodeValueType const>> node_key_bytes;
  869. node_key_bytes.append(opcode_bytes);
  870. if (auto edges = incoming_jump_edges.get(state.instruction_position); edges.has_value()) {
  871. for (auto& edge : *edges)
  872. node_key_bytes.append(edge.jump_insn);
  873. }
  874. active_node = static_cast<decltype(active_node)>(MUST(active_node->ensure_child(DisjointSpans<ByteCodeValueType const> { move(node_key_bytes) })));
  875. if (active_node->has_metadata()) {
  876. active_node->metadata_value().append({ i, state.instruction_position });
  877. common_hits += 1;
  878. } else {
  879. active_node->set_metadata(Vector<QualifiedIP> { QualifiedIP { i, state.instruction_position } });
  880. total_bytecode_entries_in_tree += opcode.size();
  881. }
  882. state.instruction_position += opcode.size();
  883. }
  884. }
  885. if constexpr (REGEX_DEBUG) {
  886. Function<void(decltype(trie)&, size_t)> print_tree = [&](decltype(trie)& node, size_t indent = 0) mutable {
  887. ByteString name = "(no ip)";
  888. ByteString insn;
  889. if (node.has_metadata()) {
  890. name = ByteString::formatted(
  891. "{}@{} ({} node{})",
  892. node.metadata_value().first().instruction_position,
  893. node.metadata_value().first().alternative_index,
  894. node.metadata_value().size(),
  895. node.metadata_value().size() == 1 ? "" : "s");
  896. MatchState state;
  897. state.instruction_position = node.metadata_value().first().instruction_position;
  898. auto& opcode = alternatives[node.metadata_value().first().alternative_index].get_opcode(state);
  899. insn = ByteString::formatted("{} {}", opcode.to_byte_string(), opcode.arguments_string());
  900. }
  901. dbgln("{:->{}}| {} -- {}", "", indent * 2, name, insn);
  902. for (auto& child : node.children())
  903. print_tree(static_cast<decltype(trie)&>(*child.value), indent + 1);
  904. };
  905. print_tree(trie, 0);
  906. }
  907. // This is really only worth it if we don't blow up the size by the 2-extra-instruction-per-node scheme, similarly, if no nodes are shared, we're better off not using a tree.
  908. auto tree_cost = (total_nodes - common_hits) * 2;
  909. auto chain_cost = total_nodes + alternatives.size() * 2;
  910. dbgln_if(REGEX_DEBUG, "Total nodes: {}, common hits: {} (tree cost = {}, chain cost = {})", total_nodes, common_hits, tree_cost, chain_cost);
  911. if (common_hits == 0 || tree_cost > chain_cost) {
  912. // It's better to lay these out as a normal sequence of instructions.
  913. auto patch_start = target.size();
  914. for (size_t i = 1; i < alternatives.size(); ++i) {
  915. target.empend(static_cast<ByteCodeValueType>(OpCodeId::ForkJump));
  916. target.empend(0u); // To be filled later.
  917. }
  918. size_t size_to_jump = 0;
  919. bool seen_one_empty = false;
  920. for (size_t i = alternatives.size(); i > 0; --i) {
  921. auto& entry = alternatives[i - 1];
  922. if (entry.is_empty()) {
  923. if (seen_one_empty)
  924. continue;
  925. seen_one_empty = true;
  926. }
  927. auto is_first = i == 1;
  928. auto instruction_size = entry.size() + (is_first ? 0 : 2); // Jump; -> +2
  929. size_to_jump += instruction_size;
  930. if (!is_first)
  931. target[patch_start + (i - 2) * 2 + 1] = size_to_jump + (alternatives.size() - i) * 2;
  932. dbgln_if(REGEX_DEBUG, "{} size = {}, cum={}", i - 1, instruction_size, size_to_jump);
  933. }
  934. seen_one_empty = false;
  935. for (size_t i = alternatives.size(); i > 0; --i) {
  936. auto& chunk = alternatives[i - 1];
  937. if (chunk.is_empty()) {
  938. if (seen_one_empty)
  939. continue;
  940. seen_one_empty = true;
  941. }
  942. ByteCode* previous_chunk = nullptr;
  943. size_t j = i - 1;
  944. auto seen_one_empty_before = chunk.is_empty();
  945. while (j >= 1) {
  946. --j;
  947. auto& candidate_chunk = alternatives[j];
  948. if (candidate_chunk.is_empty()) {
  949. if (seen_one_empty_before)
  950. continue;
  951. }
  952. previous_chunk = &candidate_chunk;
  953. break;
  954. }
  955. size_to_jump -= chunk.size() + (previous_chunk ? 2 : 0);
  956. target.extend(move(chunk));
  957. target.empend(static_cast<ByteCodeValueType>(OpCodeId::Jump));
  958. target.empend(size_to_jump); // Jump to the _END label
  959. }
  960. } else {
  961. target.ensure_capacity(total_bytecode_entries_in_tree + common_hits * 6);
  962. auto node_is = [](Tree const* node, QualifiedIP ip) {
  963. if (!node->has_metadata())
  964. return false;
  965. for (auto& node_ip : node->metadata_value()) {
  966. if (node_ip.alternative_index == ip.alternative_index && node_ip.instruction_position == ip.instruction_position)
  967. return true;
  968. }
  969. return false;
  970. };
  971. struct Patch {
  972. QualifiedIP source_ip;
  973. size_t target_ip;
  974. bool done { false };
  975. };
  976. Vector<Patch> patch_locations;
  977. patch_locations.ensure_capacity(total_nodes);
  978. auto add_patch_point = [&](Tree const* node, size_t target_ip) {
  979. if (!node->has_metadata())
  980. return;
  981. auto& node_ip = node->metadata_value().first();
  982. patch_locations.append({ node_ip, target_ip });
  983. };
  984. Queue<Tree*> nodes_to_visit;
  985. nodes_to_visit.enqueue(&trie);
  986. HashMap<size_t, NonnullOwnPtr<RedBlackTree<u64, u64>>> instruction_positions;
  987. if (has_any_backwards_jump)
  988. MUST(instruction_positions.try_ensure_capacity(alternatives.size()));
  989. auto ip_mapping_for_alternative = [&](size_t i) -> RedBlackTree<u64, u64>& {
  990. return *instruction_positions.ensure(i, [] {
  991. return make<RedBlackTree<u64, u64>>();
  992. });
  993. };
  994. // each node:
  995. // node.re
  996. // forkjump child1
  997. // forkjump child2
  998. // ...
  999. while (!nodes_to_visit.is_empty()) {
  1000. auto const* node = nodes_to_visit.dequeue();
  1001. for (auto& patch : patch_locations) {
  1002. if (!patch.done && node_is(node, patch.source_ip)) {
  1003. auto value = static_cast<ByteCodeValueType>(target.size() - patch.target_ip - 1);
  1004. target[patch.target_ip] = value;
  1005. patch.done = true;
  1006. }
  1007. }
  1008. if (!node->value().individual_spans().is_empty()) {
  1009. auto insn_bytes = node->value().individual_spans().first();
  1010. target.ensure_capacity(target.size() + insn_bytes.size());
  1011. state.instruction_position = target.size();
  1012. target.append(insn_bytes);
  1013. if (has_any_backwards_jump) {
  1014. for (auto& ip : node->metadata_value())
  1015. ip_mapping_for_alternative(ip.alternative_index).insert(ip.instruction_position, state.instruction_position);
  1016. }
  1017. auto& opcode = target.get_opcode(state);
  1018. ssize_t jump_offset;
  1019. auto is_jump = true;
  1020. auto patch_location = state.instruction_position + 1;
  1021. switch (opcode.opcode_id()) {
  1022. case OpCodeId::Jump:
  1023. jump_offset = static_cast<OpCode_Jump const&>(opcode).offset();
  1024. break;
  1025. case OpCodeId::JumpNonEmpty:
  1026. jump_offset = static_cast<OpCode_JumpNonEmpty const&>(opcode).offset();
  1027. break;
  1028. case OpCodeId::ForkJump:
  1029. jump_offset = static_cast<OpCode_ForkJump const&>(opcode).offset();
  1030. break;
  1031. case OpCodeId::ForkStay:
  1032. jump_offset = static_cast<OpCode_ForkStay const&>(opcode).offset();
  1033. break;
  1034. case OpCodeId::ForkReplaceJump:
  1035. jump_offset = static_cast<OpCode_ForkReplaceJump const&>(opcode).offset();
  1036. break;
  1037. case OpCodeId::ForkReplaceStay:
  1038. jump_offset = static_cast<OpCode_ForkReplaceStay const&>(opcode).offset();
  1039. break;
  1040. case OpCodeId::Repeat:
  1041. jump_offset = static_cast<ssize_t>(0) - static_cast<ssize_t>(static_cast<OpCode_Repeat const&>(opcode).offset()) - static_cast<ssize_t>(opcode.size());
  1042. break;
  1043. default:
  1044. is_jump = false;
  1045. break;
  1046. }
  1047. if (is_jump) {
  1048. VERIFY(node->has_metadata());
  1049. QualifiedIP ip = node->metadata_value().first();
  1050. auto intended_jump_ip = ip.instruction_position + jump_offset + opcode.size();
  1051. if (jump_offset < 0) {
  1052. VERIFY(has_any_backwards_jump);
  1053. // We should've already seen this instruction, so we can just patch it in.
  1054. auto& ip_mapping = ip_mapping_for_alternative(ip.alternative_index);
  1055. auto target_ip = ip_mapping.find(intended_jump_ip);
  1056. if (!target_ip) {
  1057. RegexDebug dbg;
  1058. size_t x = 0;
  1059. for (auto& entry : alternatives) {
  1060. warnln("----------- {} ----------", x++);
  1061. dbg.print_bytecode(entry);
  1062. }
  1063. dbgln("Regex Tree / Unknown backwards jump: {}@{} -> {}",
  1064. ip.instruction_position,
  1065. ip.alternative_index,
  1066. intended_jump_ip);
  1067. VERIFY_NOT_REACHED();
  1068. }
  1069. target[patch_location] = static_cast<ByteCodeValueType>(*target_ip - patch_location - 1);
  1070. } else {
  1071. patch_locations.append({ QualifiedIP { ip.alternative_index, intended_jump_ip }, patch_location });
  1072. }
  1073. }
  1074. }
  1075. for (auto const& child : node->children()) {
  1076. auto* child_node = static_cast<Tree*>(child.value.ptr());
  1077. target.append(static_cast<ByteCodeValueType>(OpCodeId::ForkJump));
  1078. add_patch_point(child_node, target.size());
  1079. target.append(static_cast<ByteCodeValueType>(0));
  1080. nodes_to_visit.enqueue(child_node);
  1081. }
  1082. }
  1083. for (auto& patch : patch_locations) {
  1084. if (patch.done)
  1085. continue;
  1086. auto& alternative = alternatives[patch.source_ip.alternative_index];
  1087. if (patch.source_ip.instruction_position >= alternative.size()) {
  1088. // This just wants to jump to the end of the alternative, which is fine.
  1089. // Patch it to jump to the end of the target instead.
  1090. target[patch.target_ip] = static_cast<ByteCodeValueType>(target.size() - patch.target_ip - 1);
  1091. continue;
  1092. }
  1093. dbgln("Regex Tree / Unpatched jump: {}@{} -> {}@{}",
  1094. patch.source_ip.instruction_position,
  1095. patch.source_ip.alternative_index,
  1096. patch.target_ip,
  1097. target[patch.target_ip]);
  1098. VERIFY_NOT_REACHED();
  1099. }
  1100. }
  1101. }
  1102. enum class LookupTableInsertionOutcome {
  1103. Successful,
  1104. ReplaceWithAnyChar,
  1105. TemporaryInversionNeeded,
  1106. PermanentInversionNeeded,
  1107. FlushOnInsertion,
  1108. FinishFlushOnInsertion,
  1109. CannotPlaceInTable,
  1110. };
  1111. static LookupTableInsertionOutcome insert_into_lookup_table(RedBlackTree<ByteCodeValueType, CharRange>& table, CompareTypeAndValuePair pair)
  1112. {
  1113. switch (pair.type) {
  1114. case CharacterCompareType::Inverse:
  1115. return LookupTableInsertionOutcome::PermanentInversionNeeded;
  1116. case CharacterCompareType::TemporaryInverse:
  1117. return LookupTableInsertionOutcome::TemporaryInversionNeeded;
  1118. case CharacterCompareType::AnyChar:
  1119. return LookupTableInsertionOutcome::ReplaceWithAnyChar;
  1120. case CharacterCompareType::CharClass:
  1121. return LookupTableInsertionOutcome::CannotPlaceInTable;
  1122. case CharacterCompareType::Char:
  1123. table.insert(pair.value, { (u32)pair.value, (u32)pair.value });
  1124. break;
  1125. case CharacterCompareType::CharRange: {
  1126. CharRange range { pair.value };
  1127. table.insert(range.from, range);
  1128. break;
  1129. }
  1130. case CharacterCompareType::EndAndOr:
  1131. return LookupTableInsertionOutcome::FinishFlushOnInsertion;
  1132. case CharacterCompareType::And:
  1133. return LookupTableInsertionOutcome::FlushOnInsertion;
  1134. case CharacterCompareType::Reference:
  1135. case CharacterCompareType::Property:
  1136. case CharacterCompareType::GeneralCategory:
  1137. case CharacterCompareType::Script:
  1138. case CharacterCompareType::ScriptExtension:
  1139. case CharacterCompareType::Or:
  1140. return LookupTableInsertionOutcome::CannotPlaceInTable;
  1141. case CharacterCompareType::Undefined:
  1142. case CharacterCompareType::RangeExpressionDummy:
  1143. case CharacterCompareType::String:
  1144. case CharacterCompareType::LookupTable:
  1145. VERIFY_NOT_REACHED();
  1146. }
  1147. return LookupTableInsertionOutcome::Successful;
  1148. }
  1149. void Optimizer::append_character_class(ByteCode& target, Vector<CompareTypeAndValuePair>&& pairs)
  1150. {
  1151. ByteCode arguments;
  1152. size_t argument_count = 0;
  1153. if (pairs.size() <= 1) {
  1154. for (auto& pair : pairs) {
  1155. arguments.append(to_underlying(pair.type));
  1156. if (pair.type != CharacterCompareType::AnyChar
  1157. && pair.type != CharacterCompareType::TemporaryInverse
  1158. && pair.type != CharacterCompareType::Inverse
  1159. && pair.type != CharacterCompareType::And
  1160. && pair.type != CharacterCompareType::Or
  1161. && pair.type != CharacterCompareType::EndAndOr)
  1162. arguments.append(pair.value);
  1163. ++argument_count;
  1164. }
  1165. } else {
  1166. RedBlackTree<ByteCodeValueType, CharRange> table;
  1167. RedBlackTree<ByteCodeValueType, CharRange> inverted_table;
  1168. auto* current_table = &table;
  1169. auto* current_inverted_table = &inverted_table;
  1170. bool invert_for_next_iteration = false;
  1171. bool is_currently_inverted = false;
  1172. auto flush_tables = [&] {
  1173. auto append_table = [&](auto& table) {
  1174. ++argument_count;
  1175. arguments.append(to_underlying(CharacterCompareType::LookupTable));
  1176. auto size_index = arguments.size();
  1177. arguments.append(0);
  1178. Optional<CharRange> active_range;
  1179. size_t range_count = 0;
  1180. for (auto& range : table) {
  1181. if (!active_range.has_value()) {
  1182. active_range = range;
  1183. continue;
  1184. }
  1185. if (range.from <= active_range->to + 1 && range.to + 1 >= active_range->from) {
  1186. active_range = CharRange { min(range.from, active_range->from), max(range.to, active_range->to) };
  1187. } else {
  1188. ++range_count;
  1189. arguments.append(active_range.release_value());
  1190. active_range = range;
  1191. }
  1192. }
  1193. if (active_range.has_value()) {
  1194. ++range_count;
  1195. arguments.append(active_range.release_value());
  1196. }
  1197. arguments[size_index] = range_count;
  1198. };
  1199. auto contains_regular_table = !table.is_empty();
  1200. auto contains_inverted_table = !inverted_table.is_empty();
  1201. if (contains_regular_table)
  1202. append_table(table);
  1203. if (contains_inverted_table) {
  1204. ++argument_count;
  1205. arguments.append(to_underlying(CharacterCompareType::TemporaryInverse));
  1206. append_table(inverted_table);
  1207. }
  1208. table.clear();
  1209. inverted_table.clear();
  1210. };
  1211. auto flush_on_every_insertion = false;
  1212. for (auto& value : pairs) {
  1213. auto should_invert_after_this_iteration = invert_for_next_iteration;
  1214. invert_for_next_iteration = false;
  1215. auto insertion_result = insert_into_lookup_table(*current_table, value);
  1216. switch (insertion_result) {
  1217. case LookupTableInsertionOutcome::Successful:
  1218. if (flush_on_every_insertion)
  1219. flush_tables();
  1220. break;
  1221. case LookupTableInsertionOutcome::ReplaceWithAnyChar: {
  1222. table.clear();
  1223. inverted_table.clear();
  1224. arguments.append(to_underlying(CharacterCompareType::AnyChar));
  1225. ++argument_count;
  1226. break;
  1227. }
  1228. case LookupTableInsertionOutcome::TemporaryInversionNeeded:
  1229. swap(current_table, current_inverted_table);
  1230. invert_for_next_iteration = true;
  1231. is_currently_inverted = !is_currently_inverted;
  1232. break;
  1233. case LookupTableInsertionOutcome::PermanentInversionNeeded:
  1234. flush_tables();
  1235. arguments.append(to_underlying(CharacterCompareType::Inverse));
  1236. ++argument_count;
  1237. break;
  1238. case LookupTableInsertionOutcome::FlushOnInsertion:
  1239. case LookupTableInsertionOutcome::FinishFlushOnInsertion:
  1240. flush_tables();
  1241. flush_on_every_insertion = insertion_result == LookupTableInsertionOutcome::FlushOnInsertion;
  1242. [[fallthrough]];
  1243. case LookupTableInsertionOutcome::CannotPlaceInTable:
  1244. if (is_currently_inverted) {
  1245. arguments.append(to_underlying(CharacterCompareType::TemporaryInverse));
  1246. ++argument_count;
  1247. }
  1248. arguments.append(to_underlying(value.type));
  1249. if (value.type != CharacterCompareType::AnyChar
  1250. && value.type != CharacterCompareType::TemporaryInverse
  1251. && value.type != CharacterCompareType::Inverse
  1252. && value.type != CharacterCompareType::And
  1253. && value.type != CharacterCompareType::Or
  1254. && value.type != CharacterCompareType::EndAndOr)
  1255. arguments.append(value.value);
  1256. ++argument_count;
  1257. break;
  1258. }
  1259. if (should_invert_after_this_iteration) {
  1260. swap(current_table, current_inverted_table);
  1261. is_currently_inverted = !is_currently_inverted;
  1262. }
  1263. }
  1264. flush_tables();
  1265. }
  1266. target.empend(static_cast<ByteCodeValueType>(OpCodeId::Compare));
  1267. target.empend(argument_count); // number of arguments
  1268. target.empend(arguments.size()); // size of arguments
  1269. target.extend(move(arguments));
  1270. }
  1271. template void Regex<PosixBasicParser>::run_optimization_passes();
  1272. template void Regex<PosixExtendedParser>::run_optimization_passes();
  1273. template void Regex<ECMA262Parser>::run_optimization_passes();
  1274. }