RegexOptimizer.cpp 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500
  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 = false;
  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::Or:
  262. case CharacterCompareType::EndAndOr:
  263. // These are the default behaviour for [...], so we don't need to do anything (unless we add support for 'And' below).
  264. break;
  265. case CharacterCompareType::And:
  266. // FIXME: These are too difficult to handle, so bail out.
  267. return true;
  268. case CharacterCompareType::Undefined:
  269. case CharacterCompareType::RangeExpressionDummy:
  270. // These do not occur in valid bytecode.
  271. VERIFY_NOT_REACHED();
  272. }
  273. }
  274. if constexpr (REGEX_DEBUG) {
  275. dbgln("lhs ranges:");
  276. for (auto it = lhs_ranges.begin(); it != lhs_ranges.end(); ++it)
  277. dbgln(" {}..{}", it.key(), *it);
  278. dbgln("lhs negated ranges:");
  279. for (auto it = lhs_negated_ranges.begin(); it != lhs_negated_ranges.end(); ++it)
  280. dbgln(" {}..{}", it.key(), *it);
  281. }
  282. temporary_inverse = false;
  283. reset_temporary_inverse = false;
  284. inverse = false;
  285. auto in_or = false; // We're in an OR block, so we should wait for the EndAndOr to decide if we would match.
  286. auto matched_in_or = false;
  287. auto inverse_matched_in_or = false;
  288. for (auto const& pair : rhs) {
  289. if (reset_temporary_inverse) {
  290. reset_temporary_inverse = false;
  291. temporary_inverse = false;
  292. } else {
  293. reset_temporary_inverse = true;
  294. }
  295. if constexpr (REGEX_DEBUG) {
  296. dbgln("check {} ({}) [inverted? {}] against {{", character_compare_type_name(pair.type), pair.value, current_lhs_inversion_state());
  297. for (auto it = lhs_ranges.begin(); it != lhs_ranges.end(); ++it)
  298. dbgln(" {}..{}", it.key(), *it);
  299. for (auto it = lhs_negated_ranges.begin(); it != lhs_negated_ranges.end(); ++it)
  300. dbgln(" ^[{}..{}]", it.key(), *it);
  301. for (auto& char_class : lhs_char_classes)
  302. dbgln(" {}", character_class_name(char_class));
  303. for (auto& char_class : lhs_negated_char_classes)
  304. dbgln(" ^{}", character_class_name(char_class));
  305. dbgln("}}, in or: {}, matched in or: {}, inverse matched in or: {}", in_or, matched_in_or, inverse_matched_in_or);
  306. }
  307. switch (pair.type) {
  308. case CharacterCompareType::Inverse:
  309. inverse = !inverse;
  310. break;
  311. case CharacterCompareType::TemporaryInverse:
  312. temporary_inverse = true;
  313. reset_temporary_inverse = false;
  314. break;
  315. case CharacterCompareType::AnyChar:
  316. // Special case: if not inverted, AnyChar is always in the range.
  317. if (!in_or && !current_lhs_inversion_state())
  318. return true;
  319. if (in_or) {
  320. matched_in_or = true;
  321. inverse_matched_in_or = false;
  322. }
  323. break;
  324. case CharacterCompareType::Char: {
  325. auto matched = range_contains(pair.value);
  326. if (!in_or && (current_lhs_inversion_state() ^ matched))
  327. return true;
  328. if (in_or) {
  329. matched_in_or |= matched;
  330. inverse_matched_in_or |= !matched;
  331. }
  332. break;
  333. }
  334. case CharacterCompareType::String:
  335. // FIXME: We just need to look at the last character of this string, but we only have the first character here.
  336. // Just bail out to avoid false positives.
  337. return true;
  338. case CharacterCompareType::CharClass: {
  339. auto contains = char_class_contains(static_cast<CharClass>(pair.value));
  340. if (!in_or && (current_lhs_inversion_state() ^ contains))
  341. return true;
  342. if (in_or) {
  343. matched_in_or |= contains;
  344. inverse_matched_in_or |= !contains;
  345. }
  346. break;
  347. }
  348. case CharacterCompareType::CharRange: {
  349. auto range = CharRange(pair.value);
  350. auto contains = range_contains(range);
  351. if (!in_or && (contains ^ current_lhs_inversion_state()))
  352. return true;
  353. if (in_or) {
  354. matched_in_or |= contains;
  355. inverse_matched_in_or |= !contains;
  356. }
  357. break;
  358. }
  359. case CharacterCompareType::LookupTable:
  360. // We've transformed this into a series of ranges in flat_compares(), so bail out if we see it.
  361. return true;
  362. case CharacterCompareType::Reference:
  363. // We've handled this before coming here.
  364. break;
  365. case CharacterCompareType::Property:
  366. // The only reasonable scenario where we can check these properties without spending too much time is if:
  367. // - the ranges are empty
  368. // - the char classes are empty
  369. // - the unicode properties are empty or contain only this property
  370. if (!lhs_ranges.is_empty() || !lhs_negated_ranges.is_empty() || !lhs_char_classes.is_empty() || !lhs_negated_char_classes.is_empty())
  371. return true;
  372. if (has_any_unicode_property && !lhs_unicode_properties.is_empty() && !lhs_negated_unicode_properties.is_empty()) {
  373. auto contains = lhs_unicode_properties.contains(static_cast<Unicode::Property>(pair.value));
  374. if (!in_or && (current_lhs_inversion_state() ^ contains))
  375. return true;
  376. auto inverse_contains = lhs_negated_unicode_properties.contains(static_cast<Unicode::Property>(pair.value));
  377. if (!in_or && !(current_lhs_inversion_state() ^ inverse_contains))
  378. return true;
  379. if (in_or) {
  380. matched_in_or |= contains;
  381. inverse_matched_in_or |= inverse_contains;
  382. }
  383. }
  384. break;
  385. case CharacterCompareType::GeneralCategory:
  386. if (!lhs_ranges.is_empty() || !lhs_negated_ranges.is_empty() || !lhs_char_classes.is_empty() || !lhs_negated_char_classes.is_empty())
  387. return true;
  388. if (has_any_unicode_property && !lhs_unicode_general_categories.is_empty() && !lhs_negated_unicode_general_categories.is_empty()) {
  389. auto contains = lhs_unicode_general_categories.contains(static_cast<Unicode::GeneralCategory>(pair.value));
  390. if (!in_or && (current_lhs_inversion_state() ^ contains))
  391. return true;
  392. auto inverse_contains = lhs_negated_unicode_general_categories.contains(static_cast<Unicode::GeneralCategory>(pair.value));
  393. if (!in_or && !(current_lhs_inversion_state() ^ inverse_contains))
  394. return true;
  395. if (in_or) {
  396. matched_in_or |= contains;
  397. inverse_matched_in_or |= inverse_contains;
  398. }
  399. }
  400. break;
  401. case CharacterCompareType::Script:
  402. if (!lhs_ranges.is_empty() || !lhs_negated_ranges.is_empty() || !lhs_char_classes.is_empty() || !lhs_negated_char_classes.is_empty())
  403. return true;
  404. if (has_any_unicode_property && !lhs_unicode_scripts.is_empty() && !lhs_negated_unicode_scripts.is_empty()) {
  405. auto contains = lhs_unicode_scripts.contains(static_cast<Unicode::Script>(pair.value));
  406. if (!in_or && (current_lhs_inversion_state() ^ contains))
  407. return true;
  408. auto inverse_contains = lhs_negated_unicode_scripts.contains(static_cast<Unicode::Script>(pair.value));
  409. if (!in_or && !(current_lhs_inversion_state() ^ inverse_contains))
  410. return true;
  411. if (in_or) {
  412. matched_in_or |= contains;
  413. inverse_matched_in_or |= inverse_contains;
  414. }
  415. }
  416. break;
  417. case CharacterCompareType::ScriptExtension:
  418. if (!lhs_ranges.is_empty() || !lhs_negated_ranges.is_empty() || !lhs_char_classes.is_empty() || !lhs_negated_char_classes.is_empty())
  419. return true;
  420. if (has_any_unicode_property && !lhs_unicode_script_extensions.is_empty() && !lhs_negated_unicode_script_extensions.is_empty()) {
  421. auto contains = lhs_unicode_script_extensions.contains(static_cast<Unicode::Script>(pair.value));
  422. if (!in_or && (current_lhs_inversion_state() ^ contains))
  423. return true;
  424. auto inverse_contains = lhs_negated_unicode_script_extensions.contains(static_cast<Unicode::Script>(pair.value));
  425. if (!in_or && !(current_lhs_inversion_state() ^ inverse_contains))
  426. return true;
  427. if (in_or) {
  428. matched_in_or |= contains;
  429. inverse_matched_in_or |= inverse_contains;
  430. }
  431. }
  432. break;
  433. case CharacterCompareType::Or:
  434. in_or = true;
  435. break;
  436. case CharacterCompareType::EndAndOr:
  437. // FIXME: Handle And when we support it below.
  438. VERIFY(in_or);
  439. in_or = false;
  440. if (current_lhs_inversion_state()) {
  441. if (!inverse_matched_in_or)
  442. return true;
  443. } else {
  444. if (matched_in_or)
  445. return true;
  446. }
  447. break;
  448. case CharacterCompareType::And:
  449. // FIXME: These are too difficult to handle, so bail out.
  450. return true;
  451. case CharacterCompareType::Undefined:
  452. case CharacterCompareType::RangeExpressionDummy:
  453. // These do not occur in valid bytecode.
  454. VERIFY_NOT_REACHED();
  455. }
  456. }
  457. return false;
  458. }
  459. enum class AtomicRewritePreconditionResult {
  460. SatisfiedWithProperHeader,
  461. SatisfiedWithEmptyHeader,
  462. NotSatisfied,
  463. };
  464. static AtomicRewritePreconditionResult block_satisfies_atomic_rewrite_precondition(ByteCode const& bytecode, Block const& repeated_block, Block const& following_block)
  465. {
  466. Vector<Vector<CompareTypeAndValuePair>> repeated_values;
  467. MatchState state;
  468. auto has_seen_actionable_opcode = false;
  469. for (state.instruction_position = repeated_block.start; state.instruction_position < repeated_block.end;) {
  470. auto& opcode = bytecode.get_opcode(state);
  471. switch (opcode.opcode_id()) {
  472. case OpCodeId::Compare: {
  473. has_seen_actionable_opcode = true;
  474. auto compares = static_cast<OpCode_Compare const&>(opcode).flat_compares();
  475. if (repeated_values.is_empty() && any_of(compares, [](auto& compare) { return compare.type == CharacterCompareType::AnyChar; }))
  476. return AtomicRewritePreconditionResult::NotSatisfied;
  477. repeated_values.append(move(compares));
  478. break;
  479. }
  480. case OpCodeId::CheckBegin:
  481. case OpCodeId::CheckEnd:
  482. has_seen_actionable_opcode = true;
  483. if (repeated_values.is_empty())
  484. return AtomicRewritePreconditionResult::SatisfiedWithProperHeader;
  485. break;
  486. case OpCodeId::CheckBoundary:
  487. // FIXME: What should we do with these? for now, let's fail.
  488. return AtomicRewritePreconditionResult::NotSatisfied;
  489. case OpCodeId::Restore:
  490. case OpCodeId::GoBack:
  491. return AtomicRewritePreconditionResult::NotSatisfied;
  492. case OpCodeId::ForkJump:
  493. case OpCodeId::ForkReplaceJump:
  494. case OpCodeId::JumpNonEmpty:
  495. // We could attempt to recursively resolve the follow set, but pretending that this just goes nowhere is faster.
  496. if (!has_seen_actionable_opcode)
  497. return AtomicRewritePreconditionResult::NotSatisfied;
  498. break;
  499. default:
  500. break;
  501. }
  502. state.instruction_position += opcode.size();
  503. }
  504. dbgln_if(REGEX_DEBUG, "Found {} entries in reference", repeated_values.size());
  505. bool following_block_has_at_least_one_compare = false;
  506. // Find the first compare in the following block, it must NOT match any of the values in `repeated_values'.
  507. auto final_instruction = following_block.start;
  508. for (state.instruction_position = following_block.start; state.instruction_position < following_block.end;) {
  509. final_instruction = state.instruction_position;
  510. auto& opcode = bytecode.get_opcode(state);
  511. switch (opcode.opcode_id()) {
  512. case OpCodeId::Compare: {
  513. following_block_has_at_least_one_compare = true;
  514. // We found a compare, let's see what it has.
  515. auto compares = static_cast<OpCode_Compare const&>(opcode).flat_compares();
  516. if (compares.is_empty())
  517. break;
  518. if (any_of(compares, [&](auto& compare) {
  519. return compare.type == CharacterCompareType::AnyChar || compare.type == CharacterCompareType::Reference;
  520. }))
  521. return AtomicRewritePreconditionResult::NotSatisfied;
  522. if (any_of(repeated_values, [&](auto& repeated_value) { return has_overlap(compares, repeated_value); }))
  523. return AtomicRewritePreconditionResult::NotSatisfied;
  524. return AtomicRewritePreconditionResult::SatisfiedWithProperHeader;
  525. }
  526. case OpCodeId::CheckBegin:
  527. case OpCodeId::CheckEnd:
  528. return AtomicRewritePreconditionResult::SatisfiedWithProperHeader; // Nothing can match the end!
  529. case OpCodeId::CheckBoundary:
  530. // FIXME: What should we do with these? For now, consider them a failure.
  531. return AtomicRewritePreconditionResult::NotSatisfied;
  532. case OpCodeId::ForkJump:
  533. case OpCodeId::ForkReplaceJump:
  534. case OpCodeId::JumpNonEmpty:
  535. // See note in the previous switch, same cases.
  536. if (!following_block_has_at_least_one_compare)
  537. return AtomicRewritePreconditionResult::NotSatisfied;
  538. break;
  539. default:
  540. break;
  541. }
  542. state.instruction_position += opcode.size();
  543. }
  544. // If the following block falls through, we can't rewrite it.
  545. state.instruction_position = final_instruction;
  546. switch (bytecode.get_opcode(state).opcode_id()) {
  547. case OpCodeId::Jump:
  548. case OpCodeId::JumpNonEmpty:
  549. case OpCodeId::ForkJump:
  550. case OpCodeId::ForkReplaceJump:
  551. break;
  552. default:
  553. return AtomicRewritePreconditionResult::NotSatisfied;
  554. }
  555. if (following_block_has_at_least_one_compare)
  556. return AtomicRewritePreconditionResult::SatisfiedWithProperHeader;
  557. return AtomicRewritePreconditionResult::SatisfiedWithEmptyHeader;
  558. }
  559. template<typename Parser>
  560. bool Regex<Parser>::attempt_rewrite_entire_match_as_substring_search(BasicBlockList const& basic_blocks)
  561. {
  562. // If there's no jumps, we can probably rewrite this as a substring search (Compare { string = str }).
  563. if (basic_blocks.size() > 1)
  564. return false;
  565. if (basic_blocks.is_empty()) {
  566. parser_result.optimization_data.pure_substring_search = ""sv;
  567. return true; // Empty regex, sure.
  568. }
  569. auto& bytecode = parser_result.bytecode;
  570. auto is_unicode = parser_result.options.has_flag_set(AllFlags::Unicode);
  571. // We have a single basic block, let's see if it's a series of character or string compares.
  572. StringBuilder final_string;
  573. MatchState state;
  574. while (state.instruction_position < bytecode.size()) {
  575. auto& opcode = bytecode.get_opcode(state);
  576. switch (opcode.opcode_id()) {
  577. case OpCodeId::Compare: {
  578. auto& compare = static_cast<OpCode_Compare const&>(opcode);
  579. for (auto& flat_compare : compare.flat_compares()) {
  580. if (flat_compare.type != CharacterCompareType::Char)
  581. return false;
  582. if (is_unicode || flat_compare.value <= 0x7f)
  583. final_string.append_code_point(flat_compare.value);
  584. else
  585. final_string.append(bit_cast<char>(static_cast<u8>(flat_compare.value)));
  586. }
  587. break;
  588. }
  589. default:
  590. return false;
  591. }
  592. state.instruction_position += opcode.size();
  593. }
  594. parser_result.optimization_data.pure_substring_search = final_string.to_byte_string();
  595. return true;
  596. }
  597. template<typename Parser>
  598. void Regex<Parser>::attempt_rewrite_loops_as_atomic_groups(BasicBlockList const& basic_blocks)
  599. {
  600. auto& bytecode = parser_result.bytecode;
  601. if constexpr (REGEX_DEBUG) {
  602. RegexDebug dbg;
  603. dbg.print_bytecode(*this);
  604. for (auto const& block : basic_blocks)
  605. dbgln("block from {} to {}", block.start, block.end);
  606. }
  607. // A pattern such as:
  608. // bb0 | RE0
  609. // | ForkX bb0
  610. // -------------------------
  611. // bb1 | RE1
  612. // can be rewritten as:
  613. // -------------------------
  614. // bb0 | RE0
  615. // | ForkReplaceX bb0
  616. // -------------------------
  617. // bb1 | RE1
  618. // provided that first(RE1) not-in end(RE0), which is to say
  619. // that RE1 cannot start with whatever RE0 has matched (ever).
  620. //
  621. // Alternatively, a second form of this pattern can also occur:
  622. // bb0 | *
  623. // | ForkX bb2
  624. // ------------------------
  625. // bb1 | RE0
  626. // | Jump bb0
  627. // ------------------------
  628. // bb2 | RE1
  629. // which can be transformed (with the same preconditions) to:
  630. // bb0 | *
  631. // | ForkReplaceX bb2
  632. // ------------------------
  633. // bb1 | RE0
  634. // | Jump bb0
  635. // ------------------------
  636. // bb2 | RE1
  637. enum class AlternateForm {
  638. DirectLoopWithoutHeader, // loop without proper header, a block forking to itself. i.e. the first form.
  639. DirectLoopWithoutHeaderAndEmptyFollow, // loop without proper header, a block forking to itself. i.e. the first form but with RE1 being empty.
  640. DirectLoopWithHeader, // loop with proper header, i.e. the second form.
  641. };
  642. struct CandidateBlock {
  643. Block forking_block;
  644. Optional<Block> new_target_block;
  645. AlternateForm form;
  646. };
  647. Vector<CandidateBlock> candidate_blocks;
  648. auto is_an_eligible_jump = [](OpCode const& opcode, size_t ip, size_t block_start, AlternateForm alternate_form) {
  649. switch (opcode.opcode_id()) {
  650. case OpCodeId::JumpNonEmpty: {
  651. auto const& op = static_cast<OpCode_JumpNonEmpty const&>(opcode);
  652. auto form = op.form();
  653. if (form != OpCodeId::Jump && alternate_form == AlternateForm::DirectLoopWithHeader)
  654. return false;
  655. if (form != OpCodeId::ForkJump && form != OpCodeId::ForkStay && alternate_form == AlternateForm::DirectLoopWithoutHeader)
  656. return false;
  657. return op.offset() + ip + opcode.size() == block_start;
  658. }
  659. case OpCodeId::ForkJump:
  660. if (alternate_form == AlternateForm::DirectLoopWithHeader)
  661. return false;
  662. return static_cast<OpCode_ForkJump const&>(opcode).offset() + ip + opcode.size() == block_start;
  663. case OpCodeId::ForkStay:
  664. if (alternate_form == AlternateForm::DirectLoopWithHeader)
  665. return false;
  666. return static_cast<OpCode_ForkStay const&>(opcode).offset() + ip + opcode.size() == block_start;
  667. case OpCodeId::Jump:
  668. // Infinite loop does *not* produce forks.
  669. if (alternate_form == AlternateForm::DirectLoopWithoutHeader)
  670. return false;
  671. if (alternate_form == AlternateForm::DirectLoopWithHeader)
  672. return static_cast<OpCode_Jump const&>(opcode).offset() + ip + opcode.size() == block_start;
  673. VERIFY_NOT_REACHED();
  674. default:
  675. return false;
  676. }
  677. };
  678. for (size_t i = 0; i < basic_blocks.size(); ++i) {
  679. auto forking_block = basic_blocks[i];
  680. Optional<Block> fork_fallback_block;
  681. if (i + 1 < basic_blocks.size())
  682. fork_fallback_block = basic_blocks[i + 1];
  683. MatchState state;
  684. // Check if the last instruction in this block is a jump to the block itself:
  685. {
  686. state.instruction_position = forking_block.end;
  687. auto& opcode = bytecode.get_opcode(state);
  688. if (is_an_eligible_jump(opcode, state.instruction_position, forking_block.start, AlternateForm::DirectLoopWithoutHeader)) {
  689. // We've found RE0 (and RE1 is just the following block, if any), let's see if the precondition applies.
  690. // if RE1 is empty, there's no first(RE1), so this is an automatic pass.
  691. if (!fork_fallback_block.has_value()
  692. || (fork_fallback_block->end == fork_fallback_block->start && block_satisfies_atomic_rewrite_precondition(bytecode, forking_block, *fork_fallback_block) != AtomicRewritePreconditionResult::NotSatisfied)) {
  693. candidate_blocks.append({ forking_block, fork_fallback_block, AlternateForm::DirectLoopWithoutHeader });
  694. break;
  695. }
  696. auto precondition = block_satisfies_atomic_rewrite_precondition(bytecode, forking_block, *fork_fallback_block);
  697. if (precondition == AtomicRewritePreconditionResult::SatisfiedWithProperHeader) {
  698. candidate_blocks.append({ forking_block, fork_fallback_block, AlternateForm::DirectLoopWithoutHeader });
  699. break;
  700. }
  701. if (precondition == AtomicRewritePreconditionResult::SatisfiedWithEmptyHeader) {
  702. candidate_blocks.append({ forking_block, fork_fallback_block, AlternateForm::DirectLoopWithoutHeaderAndEmptyFollow });
  703. break;
  704. }
  705. }
  706. }
  707. // Check if the last instruction in the last block is a direct jump to this block
  708. if (fork_fallback_block.has_value()) {
  709. state.instruction_position = fork_fallback_block->end;
  710. auto& opcode = bytecode.get_opcode(state);
  711. if (is_an_eligible_jump(opcode, state.instruction_position, forking_block.start, AlternateForm::DirectLoopWithHeader)) {
  712. // We've found bb1 and bb0, let's just make sure that bb0 forks to bb2.
  713. state.instruction_position = forking_block.end;
  714. auto& opcode = bytecode.get_opcode(state);
  715. if (opcode.opcode_id() == OpCodeId::ForkJump || opcode.opcode_id() == OpCodeId::ForkStay) {
  716. Optional<Block> block_following_fork_fallback;
  717. if (i + 2 < basic_blocks.size())
  718. block_following_fork_fallback = basic_blocks[i + 2];
  719. if (!block_following_fork_fallback.has_value()
  720. || block_satisfies_atomic_rewrite_precondition(bytecode, *fork_fallback_block, *block_following_fork_fallback) != AtomicRewritePreconditionResult::NotSatisfied) {
  721. candidate_blocks.append({ forking_block, {}, AlternateForm::DirectLoopWithHeader });
  722. break;
  723. }
  724. }
  725. }
  726. // We've found a slightly degenerate case, where the next block jumps back to the _jump_ instruction in the forking block.
  727. // This is a direct loop without a proper header that is posing as a loop with a header.
  728. if (is_an_eligible_jump(opcode, state.instruction_position, forking_block.end, AlternateForm::DirectLoopWithHeader)) {
  729. // We've found bb1 and bb0, let's just make sure that bb0 forks to bb2.
  730. state.instruction_position = forking_block.end;
  731. auto& opcode = bytecode.get_opcode(state);
  732. if (opcode.opcode_id() == OpCodeId::ForkJump || opcode.opcode_id() == OpCodeId::ForkStay) {
  733. Optional<Block> block_following_fork_fallback;
  734. if (i + 2 < basic_blocks.size())
  735. block_following_fork_fallback = basic_blocks[i + 2];
  736. if (!block_following_fork_fallback.has_value()
  737. || block_satisfies_atomic_rewrite_precondition(bytecode, *fork_fallback_block, *block_following_fork_fallback) != AtomicRewritePreconditionResult::NotSatisfied) {
  738. candidate_blocks.append({ forking_block, {}, AlternateForm::DirectLoopWithoutHeader });
  739. break;
  740. }
  741. }
  742. }
  743. }
  744. }
  745. dbgln_if(REGEX_DEBUG, "Found {} candidate blocks", candidate_blocks.size());
  746. if (candidate_blocks.is_empty()) {
  747. dbgln_if(REGEX_DEBUG, "Failed to find anything for {}", pattern_value);
  748. return;
  749. }
  750. RedBlackTree<size_t, size_t> needed_patches;
  751. // Reverse the blocks, so we can patch the bytecode without messing with the latter patches.
  752. quick_sort(candidate_blocks, [](auto& a, auto& b) { return b.forking_block.start > a.forking_block.start; });
  753. for (auto& candidate : candidate_blocks) {
  754. // Note that both forms share a ForkReplace patch in forking_block.
  755. // Patch the ForkX in forking_block to be a ForkReplaceX instead.
  756. auto& opcode_id = bytecode[candidate.forking_block.end];
  757. if (opcode_id == (ByteCodeValueType)OpCodeId::ForkStay) {
  758. opcode_id = (ByteCodeValueType)OpCodeId::ForkReplaceStay;
  759. } else if (opcode_id == (ByteCodeValueType)OpCodeId::ForkJump) {
  760. opcode_id = (ByteCodeValueType)OpCodeId::ForkReplaceJump;
  761. } else if (opcode_id == (ByteCodeValueType)OpCodeId::JumpNonEmpty) {
  762. auto& jump_opcode_id = bytecode[candidate.forking_block.end + 3];
  763. if (jump_opcode_id == (ByteCodeValueType)OpCodeId::ForkStay)
  764. jump_opcode_id = (ByteCodeValueType)OpCodeId::ForkReplaceStay;
  765. else if (jump_opcode_id == (ByteCodeValueType)OpCodeId::ForkJump)
  766. jump_opcode_id = (ByteCodeValueType)OpCodeId::ForkReplaceJump;
  767. else
  768. VERIFY_NOT_REACHED();
  769. } else {
  770. VERIFY_NOT_REACHED();
  771. }
  772. }
  773. if (!needed_patches.is_empty()) {
  774. MatchState state;
  775. auto bytecode_size = bytecode.size();
  776. state.instruction_position = 0;
  777. struct Patch {
  778. ssize_t value;
  779. size_t offset;
  780. bool should_negate { false };
  781. };
  782. for (;;) {
  783. if (state.instruction_position >= bytecode_size)
  784. break;
  785. auto& opcode = bytecode.get_opcode(state);
  786. Stack<Patch, 2> patch_points;
  787. switch (opcode.opcode_id()) {
  788. case OpCodeId::Jump:
  789. patch_points.push({ static_cast<OpCode_Jump const&>(opcode).offset(), state.instruction_position + 1 });
  790. break;
  791. case OpCodeId::JumpNonEmpty:
  792. patch_points.push({ static_cast<OpCode_JumpNonEmpty const&>(opcode).offset(), state.instruction_position + 1 });
  793. patch_points.push({ static_cast<OpCode_JumpNonEmpty const&>(opcode).checkpoint(), state.instruction_position + 2 });
  794. break;
  795. case OpCodeId::ForkJump:
  796. patch_points.push({ static_cast<OpCode_ForkJump const&>(opcode).offset(), state.instruction_position + 1 });
  797. break;
  798. case OpCodeId::ForkStay:
  799. patch_points.push({ static_cast<OpCode_ForkStay const&>(opcode).offset(), state.instruction_position + 1 });
  800. break;
  801. case OpCodeId::Repeat:
  802. patch_points.push({ -(ssize_t) static_cast<OpCode_Repeat const&>(opcode).offset(), state.instruction_position + 1, true });
  803. break;
  804. default:
  805. break;
  806. }
  807. while (!patch_points.is_empty()) {
  808. auto& patch_point = patch_points.top();
  809. auto target_offset = patch_point.value + state.instruction_position + opcode.size();
  810. constexpr auto do_patch = [](auto& patch_it, auto& patch_point, auto& target_offset, auto& bytecode, auto ip) {
  811. if (patch_it.key() == ip)
  812. return;
  813. if (patch_point.value < 0 && target_offset <= patch_it.key() && ip > patch_it.key())
  814. bytecode[patch_point.offset] += (patch_point.should_negate ? 1 : -1) * (*patch_it);
  815. else if (patch_point.value > 0 && target_offset >= patch_it.key() && ip < patch_it.key())
  816. bytecode[patch_point.offset] += (patch_point.should_negate ? -1 : 1) * (*patch_it);
  817. };
  818. if (auto patch_it = needed_patches.find_largest_not_above_iterator(target_offset); !patch_it.is_end())
  819. do_patch(patch_it, patch_point, target_offset, bytecode, state.instruction_position);
  820. else if (auto patch_it = needed_patches.find_largest_not_above_iterator(state.instruction_position); !patch_it.is_end())
  821. do_patch(patch_it, patch_point, target_offset, bytecode, state.instruction_position);
  822. patch_points.pop();
  823. }
  824. state.instruction_position += opcode.size();
  825. }
  826. }
  827. if constexpr (REGEX_DEBUG) {
  828. warnln("Transformed to:");
  829. RegexDebug dbg;
  830. dbg.print_bytecode(*this);
  831. }
  832. }
  833. void Optimizer::append_alternation(ByteCode& target, ByteCode&& left, ByteCode&& right)
  834. {
  835. Array<ByteCode, 2> alternatives;
  836. alternatives[0] = move(left);
  837. alternatives[1] = move(right);
  838. append_alternation(target, alternatives);
  839. }
  840. template<typename K, typename V, typename KTraits>
  841. using OrderedHashMapForTrie = OrderedHashMap<K, V, KTraits>;
  842. void Optimizer::append_alternation(ByteCode& target, Span<ByteCode> alternatives)
  843. {
  844. if (alternatives.size() == 0)
  845. return;
  846. if (alternatives.size() == 1)
  847. return target.extend(move(alternatives[0]));
  848. if (all_of(alternatives, [](auto& x) { return x.is_empty(); }))
  849. return;
  850. for (auto& entry : alternatives)
  851. entry.flatten();
  852. #if REGEX_DEBUG
  853. ScopeLogger<true> log;
  854. warnln("Alternations:");
  855. RegexDebug dbg;
  856. for (auto& entry : alternatives) {
  857. warnln("----------");
  858. dbg.print_bytecode(entry);
  859. }
  860. ScopeGuard print_at_end {
  861. [&] {
  862. warnln("======================");
  863. RegexDebug dbg;
  864. dbg.print_bytecode(target);
  865. }
  866. };
  867. #endif
  868. // First, find incoming jump edges.
  869. // We need them for two reasons:
  870. // - 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)
  871. // - We need to know which jumps to patch when we're done
  872. struct JumpEdge {
  873. Span<ByteCodeValueType const> jump_insn;
  874. };
  875. Vector<HashMap<size_t, Vector<JumpEdge>>> incoming_jump_edges_for_each_alternative;
  876. incoming_jump_edges_for_each_alternative.resize(alternatives.size());
  877. auto has_any_backwards_jump = false;
  878. MatchState state;
  879. for (size_t i = 0; i < alternatives.size(); ++i) {
  880. auto& alternative = alternatives[i];
  881. // 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.
  882. // Jump{offset=0}
  883. alternative.append(static_cast<ByteCodeValueType>(OpCodeId::Jump));
  884. alternative.append(0);
  885. auto& incoming_jump_edges = incoming_jump_edges_for_each_alternative[i];
  886. auto alternative_bytes = alternative.spans<1>().singular_span();
  887. for (state.instruction_position = 0; state.instruction_position < alternative.size();) {
  888. auto& opcode = alternative.get_opcode(state);
  889. auto opcode_bytes = alternative_bytes.slice(state.instruction_position, opcode.size());
  890. switch (opcode.opcode_id()) {
  891. case OpCodeId::Jump:
  892. incoming_jump_edges.ensure(static_cast<OpCode_Jump const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  893. has_any_backwards_jump |= static_cast<OpCode_Jump const&>(opcode).offset() < 0;
  894. break;
  895. case OpCodeId::JumpNonEmpty:
  896. incoming_jump_edges.ensure(static_cast<OpCode_JumpNonEmpty const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  897. has_any_backwards_jump |= static_cast<OpCode_JumpNonEmpty const&>(opcode).offset() < 0;
  898. break;
  899. case OpCodeId::ForkJump:
  900. incoming_jump_edges.ensure(static_cast<OpCode_ForkJump const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  901. has_any_backwards_jump |= static_cast<OpCode_ForkJump const&>(opcode).offset() < 0;
  902. break;
  903. case OpCodeId::ForkStay:
  904. incoming_jump_edges.ensure(static_cast<OpCode_ForkStay const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  905. has_any_backwards_jump |= static_cast<OpCode_ForkStay const&>(opcode).offset() < 0;
  906. break;
  907. case OpCodeId::ForkReplaceJump:
  908. incoming_jump_edges.ensure(static_cast<OpCode_ForkReplaceJump const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  909. has_any_backwards_jump |= static_cast<OpCode_ForkReplaceJump const&>(opcode).offset() < 0;
  910. break;
  911. case OpCodeId::ForkReplaceStay:
  912. incoming_jump_edges.ensure(static_cast<OpCode_ForkReplaceStay const&>(opcode).offset() + state.instruction_position).append({ opcode_bytes });
  913. has_any_backwards_jump |= static_cast<OpCode_ForkReplaceStay const&>(opcode).offset() < 0;
  914. break;
  915. case OpCodeId::Repeat:
  916. incoming_jump_edges.ensure(state.instruction_position - static_cast<OpCode_Repeat const&>(opcode).offset()).append({ opcode_bytes });
  917. has_any_backwards_jump = true;
  918. break;
  919. default:
  920. break;
  921. }
  922. state.instruction_position += opcode.size();
  923. }
  924. }
  925. struct QualifiedIP {
  926. size_t alternative_index;
  927. size_t instruction_position;
  928. };
  929. using Tree = Trie<DisjointSpans<ByteCodeValueType const>, Vector<QualifiedIP>, Traits<DisjointSpans<ByteCodeValueType const>>, void, OrderedHashMapForTrie>;
  930. Tree trie { {} }; // Root node is empty, key{ instruction_bytes, dependent_instruction_bytes... } -> IP
  931. size_t common_hits = 0;
  932. size_t total_nodes = 0;
  933. size_t total_bytecode_entries_in_tree = 0;
  934. for (size_t i = 0; i < alternatives.size(); ++i) {
  935. auto& alternative = alternatives[i];
  936. auto& incoming_jump_edges = incoming_jump_edges_for_each_alternative[i];
  937. auto* active_node = &trie;
  938. auto alternative_span = alternative.spans<1>().singular_span();
  939. for (state.instruction_position = 0; state.instruction_position < alternative_span.size();) {
  940. total_nodes += 1;
  941. auto& opcode = alternative.get_opcode(state);
  942. auto opcode_bytes = alternative_span.slice(state.instruction_position, opcode.size());
  943. Vector<Span<ByteCodeValueType const>> node_key_bytes;
  944. node_key_bytes.append(opcode_bytes);
  945. if (auto edges = incoming_jump_edges.get(state.instruction_position); edges.has_value()) {
  946. for (auto& edge : *edges)
  947. node_key_bytes.append(edge.jump_insn);
  948. }
  949. active_node = static_cast<decltype(active_node)>(MUST(active_node->ensure_child(DisjointSpans<ByteCodeValueType const> { move(node_key_bytes) })));
  950. if (active_node->has_metadata()) {
  951. active_node->metadata_value().append({ i, state.instruction_position });
  952. common_hits += 1;
  953. } else {
  954. active_node->set_metadata(Vector<QualifiedIP> { QualifiedIP { i, state.instruction_position } });
  955. total_bytecode_entries_in_tree += opcode.size();
  956. }
  957. state.instruction_position += opcode.size();
  958. }
  959. }
  960. if constexpr (REGEX_DEBUG) {
  961. Function<void(decltype(trie)&, size_t)> print_tree = [&](decltype(trie)& node, size_t indent = 0) mutable {
  962. ByteString name = "(no ip)";
  963. ByteString insn;
  964. if (node.has_metadata()) {
  965. name = ByteString::formatted(
  966. "{}@{} ({} node{})",
  967. node.metadata_value().first().instruction_position,
  968. node.metadata_value().first().alternative_index,
  969. node.metadata_value().size(),
  970. node.metadata_value().size() == 1 ? "" : "s");
  971. MatchState state;
  972. state.instruction_position = node.metadata_value().first().instruction_position;
  973. auto& opcode = alternatives[node.metadata_value().first().alternative_index].get_opcode(state);
  974. insn = ByteString::formatted("{} {}", opcode.to_byte_string(), opcode.arguments_string());
  975. }
  976. dbgln("{:->{}}| {} -- {}", "", indent * 2, name, insn);
  977. for (auto& child : node.children())
  978. print_tree(static_cast<decltype(trie)&>(*child.value), indent + 1);
  979. };
  980. print_tree(trie, 0);
  981. }
  982. // 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.
  983. auto tree_cost = (total_nodes - common_hits) * 2;
  984. auto chain_cost = total_nodes + alternatives.size() * 2;
  985. dbgln_if(REGEX_DEBUG, "Total nodes: {}, common hits: {} (tree cost = {}, chain cost = {})", total_nodes, common_hits, tree_cost, chain_cost);
  986. if (common_hits == 0 || tree_cost > chain_cost) {
  987. // It's better to lay these out as a normal sequence of instructions.
  988. auto patch_start = target.size();
  989. for (size_t i = 1; i < alternatives.size(); ++i) {
  990. target.empend(static_cast<ByteCodeValueType>(OpCodeId::ForkJump));
  991. target.empend(0u); // To be filled later.
  992. }
  993. size_t size_to_jump = 0;
  994. bool seen_one_empty = false;
  995. for (size_t i = alternatives.size(); i > 0; --i) {
  996. auto& entry = alternatives[i - 1];
  997. if (entry.is_empty()) {
  998. if (seen_one_empty)
  999. continue;
  1000. seen_one_empty = true;
  1001. }
  1002. auto is_first = i == 1;
  1003. auto instruction_size = entry.size() + (is_first ? 0 : 2); // Jump; -> +2
  1004. size_to_jump += instruction_size;
  1005. if (!is_first)
  1006. target[patch_start + (i - 2) * 2 + 1] = size_to_jump + (alternatives.size() - i) * 2;
  1007. dbgln_if(REGEX_DEBUG, "{} size = {}, cum={}", i - 1, instruction_size, size_to_jump);
  1008. }
  1009. seen_one_empty = false;
  1010. for (size_t i = alternatives.size(); i > 0; --i) {
  1011. auto& chunk = alternatives[i - 1];
  1012. if (chunk.is_empty()) {
  1013. if (seen_one_empty)
  1014. continue;
  1015. seen_one_empty = true;
  1016. }
  1017. ByteCode* previous_chunk = nullptr;
  1018. size_t j = i - 1;
  1019. auto seen_one_empty_before = chunk.is_empty();
  1020. while (j >= 1) {
  1021. --j;
  1022. auto& candidate_chunk = alternatives[j];
  1023. if (candidate_chunk.is_empty()) {
  1024. if (seen_one_empty_before)
  1025. continue;
  1026. }
  1027. previous_chunk = &candidate_chunk;
  1028. break;
  1029. }
  1030. size_to_jump -= chunk.size() + (previous_chunk ? 2 : 0);
  1031. target.extend(move(chunk));
  1032. target.empend(static_cast<ByteCodeValueType>(OpCodeId::Jump));
  1033. target.empend(size_to_jump); // Jump to the _END label
  1034. }
  1035. } else {
  1036. target.ensure_capacity(total_bytecode_entries_in_tree + common_hits * 6);
  1037. auto node_is = [](Tree const* node, QualifiedIP ip) {
  1038. if (!node->has_metadata())
  1039. return false;
  1040. for (auto& node_ip : node->metadata_value()) {
  1041. if (node_ip.alternative_index == ip.alternative_index && node_ip.instruction_position == ip.instruction_position)
  1042. return true;
  1043. }
  1044. return false;
  1045. };
  1046. struct Patch {
  1047. QualifiedIP source_ip;
  1048. size_t target_ip;
  1049. bool done { false };
  1050. };
  1051. Vector<Patch> patch_locations;
  1052. patch_locations.ensure_capacity(total_nodes);
  1053. auto add_patch_point = [&](Tree const* node, size_t target_ip) {
  1054. if (!node->has_metadata())
  1055. return;
  1056. auto& node_ip = node->metadata_value().first();
  1057. patch_locations.append({ node_ip, target_ip });
  1058. };
  1059. Queue<Tree*> nodes_to_visit;
  1060. nodes_to_visit.enqueue(&trie);
  1061. HashMap<size_t, NonnullOwnPtr<RedBlackTree<u64, u64>>> instruction_positions;
  1062. if (has_any_backwards_jump)
  1063. MUST(instruction_positions.try_ensure_capacity(alternatives.size()));
  1064. auto ip_mapping_for_alternative = [&](size_t i) -> RedBlackTree<u64, u64>& {
  1065. return *instruction_positions.ensure(i, [] {
  1066. return make<RedBlackTree<u64, u64>>();
  1067. });
  1068. };
  1069. // each node:
  1070. // node.re
  1071. // forkjump child1
  1072. // forkjump child2
  1073. // ...
  1074. while (!nodes_to_visit.is_empty()) {
  1075. auto const* node = nodes_to_visit.dequeue();
  1076. for (auto& patch : patch_locations) {
  1077. if (!patch.done && node_is(node, patch.source_ip)) {
  1078. auto value = static_cast<ByteCodeValueType>(target.size() - patch.target_ip - 1);
  1079. if (value == 0)
  1080. target[patch.target_ip - 1] = static_cast<ByteCodeValueType>(OpCodeId::Jump);
  1081. target[patch.target_ip] = value;
  1082. patch.done = true;
  1083. }
  1084. }
  1085. if (!node->value().individual_spans().is_empty()) {
  1086. auto insn_bytes = node->value().individual_spans().first();
  1087. target.ensure_capacity(target.size() + insn_bytes.size());
  1088. state.instruction_position = target.size();
  1089. target.append(insn_bytes);
  1090. if (has_any_backwards_jump) {
  1091. for (auto& ip : node->metadata_value())
  1092. ip_mapping_for_alternative(ip.alternative_index).insert(ip.instruction_position, state.instruction_position);
  1093. }
  1094. auto& opcode = target.get_opcode(state);
  1095. ssize_t jump_offset;
  1096. auto is_jump = true;
  1097. auto patch_location = state.instruction_position + 1;
  1098. switch (opcode.opcode_id()) {
  1099. case OpCodeId::Jump:
  1100. jump_offset = static_cast<OpCode_Jump const&>(opcode).offset();
  1101. break;
  1102. case OpCodeId::JumpNonEmpty:
  1103. jump_offset = static_cast<OpCode_JumpNonEmpty const&>(opcode).offset();
  1104. break;
  1105. case OpCodeId::ForkJump:
  1106. jump_offset = static_cast<OpCode_ForkJump const&>(opcode).offset();
  1107. break;
  1108. case OpCodeId::ForkStay:
  1109. jump_offset = static_cast<OpCode_ForkStay const&>(opcode).offset();
  1110. break;
  1111. case OpCodeId::ForkReplaceJump:
  1112. jump_offset = static_cast<OpCode_ForkReplaceJump const&>(opcode).offset();
  1113. break;
  1114. case OpCodeId::ForkReplaceStay:
  1115. jump_offset = static_cast<OpCode_ForkReplaceStay const&>(opcode).offset();
  1116. break;
  1117. case OpCodeId::Repeat:
  1118. jump_offset = static_cast<ssize_t>(0) - static_cast<ssize_t>(static_cast<OpCode_Repeat const&>(opcode).offset()) - static_cast<ssize_t>(opcode.size());
  1119. break;
  1120. default:
  1121. is_jump = false;
  1122. break;
  1123. }
  1124. if (is_jump) {
  1125. VERIFY(node->has_metadata());
  1126. QualifiedIP ip = node->metadata_value().first();
  1127. auto intended_jump_ip = ip.instruction_position + jump_offset + opcode.size();
  1128. if (jump_offset < 0) {
  1129. VERIFY(has_any_backwards_jump);
  1130. // We should've already seen this instruction, so we can just patch it in.
  1131. auto& ip_mapping = ip_mapping_for_alternative(ip.alternative_index);
  1132. auto target_ip = ip_mapping.find(intended_jump_ip);
  1133. if (!target_ip) {
  1134. RegexDebug dbg;
  1135. size_t x = 0;
  1136. for (auto& entry : alternatives) {
  1137. warnln("----------- {} ----------", x++);
  1138. dbg.print_bytecode(entry);
  1139. }
  1140. dbgln("Regex Tree / Unknown backwards jump: {}@{} -> {}",
  1141. ip.instruction_position,
  1142. ip.alternative_index,
  1143. intended_jump_ip);
  1144. VERIFY_NOT_REACHED();
  1145. }
  1146. target[patch_location] = static_cast<ByteCodeValueType>(*target_ip - patch_location - 1);
  1147. } else {
  1148. patch_locations.append({ QualifiedIP { ip.alternative_index, intended_jump_ip }, patch_location });
  1149. }
  1150. }
  1151. }
  1152. for (auto const& child : node->children()) {
  1153. auto* child_node = static_cast<Tree*>(child.value.ptr());
  1154. target.append(static_cast<ByteCodeValueType>(OpCodeId::ForkJump));
  1155. add_patch_point(child_node, target.size());
  1156. target.append(static_cast<ByteCodeValueType>(0));
  1157. nodes_to_visit.enqueue(child_node);
  1158. }
  1159. }
  1160. for (auto& patch : patch_locations) {
  1161. if (patch.done)
  1162. continue;
  1163. auto& alternative = alternatives[patch.source_ip.alternative_index];
  1164. if (patch.source_ip.instruction_position >= alternative.size()) {
  1165. // This just wants to jump to the end of the alternative, which is fine.
  1166. // Patch it to jump to the end of the target instead.
  1167. target[patch.target_ip] = static_cast<ByteCodeValueType>(target.size() - patch.target_ip - 1);
  1168. continue;
  1169. }
  1170. dbgln("Regex Tree / Unpatched jump: {}@{} -> {}@{}",
  1171. patch.source_ip.instruction_position,
  1172. patch.source_ip.alternative_index,
  1173. patch.target_ip,
  1174. target[patch.target_ip]);
  1175. VERIFY_NOT_REACHED();
  1176. }
  1177. }
  1178. }
  1179. enum class LookupTableInsertionOutcome {
  1180. Successful,
  1181. ReplaceWithAnyChar,
  1182. TemporaryInversionNeeded,
  1183. PermanentInversionNeeded,
  1184. FlushOnInsertion,
  1185. FinishFlushOnInsertion,
  1186. CannotPlaceInTable,
  1187. };
  1188. static LookupTableInsertionOutcome insert_into_lookup_table(RedBlackTree<ByteCodeValueType, CharRange>& table, CompareTypeAndValuePair pair)
  1189. {
  1190. switch (pair.type) {
  1191. case CharacterCompareType::Inverse:
  1192. return LookupTableInsertionOutcome::PermanentInversionNeeded;
  1193. case CharacterCompareType::TemporaryInverse:
  1194. return LookupTableInsertionOutcome::TemporaryInversionNeeded;
  1195. case CharacterCompareType::AnyChar:
  1196. return LookupTableInsertionOutcome::ReplaceWithAnyChar;
  1197. case CharacterCompareType::CharClass:
  1198. return LookupTableInsertionOutcome::CannotPlaceInTable;
  1199. case CharacterCompareType::Char:
  1200. table.insert(pair.value, { (u32)pair.value, (u32)pair.value });
  1201. break;
  1202. case CharacterCompareType::CharRange: {
  1203. CharRange range { pair.value };
  1204. table.insert(range.from, range);
  1205. break;
  1206. }
  1207. case CharacterCompareType::EndAndOr:
  1208. return LookupTableInsertionOutcome::FinishFlushOnInsertion;
  1209. case CharacterCompareType::And:
  1210. return LookupTableInsertionOutcome::FlushOnInsertion;
  1211. case CharacterCompareType::Reference:
  1212. case CharacterCompareType::Property:
  1213. case CharacterCompareType::GeneralCategory:
  1214. case CharacterCompareType::Script:
  1215. case CharacterCompareType::ScriptExtension:
  1216. case CharacterCompareType::Or:
  1217. return LookupTableInsertionOutcome::CannotPlaceInTable;
  1218. case CharacterCompareType::Undefined:
  1219. case CharacterCompareType::RangeExpressionDummy:
  1220. case CharacterCompareType::String:
  1221. case CharacterCompareType::LookupTable:
  1222. VERIFY_NOT_REACHED();
  1223. }
  1224. return LookupTableInsertionOutcome::Successful;
  1225. }
  1226. void Optimizer::append_character_class(ByteCode& target, Vector<CompareTypeAndValuePair>&& pairs)
  1227. {
  1228. ByteCode arguments;
  1229. size_t argument_count = 0;
  1230. if (pairs.size() <= 1) {
  1231. for (auto& pair : pairs) {
  1232. arguments.append(to_underlying(pair.type));
  1233. if (pair.type != CharacterCompareType::AnyChar
  1234. && pair.type != CharacterCompareType::TemporaryInverse
  1235. && pair.type != CharacterCompareType::Inverse
  1236. && pair.type != CharacterCompareType::And
  1237. && pair.type != CharacterCompareType::Or
  1238. && pair.type != CharacterCompareType::EndAndOr)
  1239. arguments.append(pair.value);
  1240. ++argument_count;
  1241. }
  1242. } else {
  1243. RedBlackTree<ByteCodeValueType, CharRange> table;
  1244. RedBlackTree<ByteCodeValueType, CharRange> inverted_table;
  1245. auto* current_table = &table;
  1246. auto* current_inverted_table = &inverted_table;
  1247. bool invert_for_next_iteration = false;
  1248. bool is_currently_inverted = false;
  1249. auto flush_tables = [&] {
  1250. auto append_table = [&](auto& table) {
  1251. ++argument_count;
  1252. arguments.append(to_underlying(CharacterCompareType::LookupTable));
  1253. auto size_index = arguments.size();
  1254. arguments.append(0);
  1255. Optional<CharRange> active_range;
  1256. size_t range_count = 0;
  1257. for (auto& range : table) {
  1258. if (!active_range.has_value()) {
  1259. active_range = range;
  1260. continue;
  1261. }
  1262. if (range.from <= active_range->to + 1 && range.to + 1 >= active_range->from) {
  1263. active_range = CharRange { min(range.from, active_range->from), max(range.to, active_range->to) };
  1264. } else {
  1265. ++range_count;
  1266. arguments.append(active_range.release_value());
  1267. active_range = range;
  1268. }
  1269. }
  1270. if (active_range.has_value()) {
  1271. ++range_count;
  1272. arguments.append(active_range.release_value());
  1273. }
  1274. arguments[size_index] = range_count;
  1275. };
  1276. auto contains_regular_table = !table.is_empty();
  1277. auto contains_inverted_table = !inverted_table.is_empty();
  1278. if (contains_regular_table)
  1279. append_table(table);
  1280. if (contains_inverted_table) {
  1281. ++argument_count;
  1282. arguments.append(to_underlying(CharacterCompareType::TemporaryInverse));
  1283. append_table(inverted_table);
  1284. }
  1285. table.clear();
  1286. inverted_table.clear();
  1287. };
  1288. auto flush_on_every_insertion = false;
  1289. for (auto& value : pairs) {
  1290. auto should_invert_after_this_iteration = invert_for_next_iteration;
  1291. invert_for_next_iteration = false;
  1292. auto insertion_result = insert_into_lookup_table(*current_table, value);
  1293. switch (insertion_result) {
  1294. case LookupTableInsertionOutcome::Successful:
  1295. if (flush_on_every_insertion)
  1296. flush_tables();
  1297. break;
  1298. case LookupTableInsertionOutcome::ReplaceWithAnyChar: {
  1299. table.clear();
  1300. inverted_table.clear();
  1301. arguments.append(to_underlying(CharacterCompareType::AnyChar));
  1302. ++argument_count;
  1303. break;
  1304. }
  1305. case LookupTableInsertionOutcome::TemporaryInversionNeeded:
  1306. swap(current_table, current_inverted_table);
  1307. invert_for_next_iteration = true;
  1308. is_currently_inverted = !is_currently_inverted;
  1309. break;
  1310. case LookupTableInsertionOutcome::PermanentInversionNeeded:
  1311. flush_tables();
  1312. arguments.append(to_underlying(CharacterCompareType::Inverse));
  1313. ++argument_count;
  1314. break;
  1315. case LookupTableInsertionOutcome::FlushOnInsertion:
  1316. case LookupTableInsertionOutcome::FinishFlushOnInsertion:
  1317. flush_tables();
  1318. flush_on_every_insertion = insertion_result == LookupTableInsertionOutcome::FlushOnInsertion;
  1319. [[fallthrough]];
  1320. case LookupTableInsertionOutcome::CannotPlaceInTable:
  1321. if (is_currently_inverted) {
  1322. arguments.append(to_underlying(CharacterCompareType::TemporaryInverse));
  1323. ++argument_count;
  1324. }
  1325. arguments.append(to_underlying(value.type));
  1326. if (value.type != CharacterCompareType::AnyChar
  1327. && value.type != CharacterCompareType::TemporaryInverse
  1328. && value.type != CharacterCompareType::Inverse
  1329. && value.type != CharacterCompareType::And
  1330. && value.type != CharacterCompareType::Or
  1331. && value.type != CharacterCompareType::EndAndOr)
  1332. arguments.append(value.value);
  1333. ++argument_count;
  1334. break;
  1335. }
  1336. if (should_invert_after_this_iteration) {
  1337. swap(current_table, current_inverted_table);
  1338. is_currently_inverted = !is_currently_inverted;
  1339. }
  1340. }
  1341. flush_tables();
  1342. }
  1343. target.empend(static_cast<ByteCodeValueType>(OpCodeId::Compare));
  1344. target.empend(argument_count); // number of arguments
  1345. target.empend(arguments.size()); // size of arguments
  1346. target.extend(move(arguments));
  1347. }
  1348. template void Regex<PosixBasicParser>::run_optimization_passes();
  1349. template void Regex<PosixExtendedParser>::run_optimization_passes();
  1350. template void Regex<ECMA262Parser>::run_optimization_passes();
  1351. }