RegexOptimizer.cpp 59 KB

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