From 27e0c57069785f9a25634d11b196c7229d0ecd89 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Tue, 29 Oct 2024 11:18:36 +0100 Subject: [PATCH] Optimize parsing of literal bytes. We now create the bytes instance representing the literal as a global singleton to avoid instantiating it over and over again. Closes #1910. --- spicy/lib/spicy_rt.hlt | 2 +- spicy/runtime/include/parser.h | 7 +++--- spicy/runtime/src/parser.cc | 8 +++---- spicy/runtime/src/tests/parser.cc | 2 +- .../src/compiler/codegen/parsers/literals.cc | 23 +++++++++++-------- .../output | 2 +- tests/spicy/types/bytes/parse-length.spicy | 2 +- 7 files changed, 24 insertions(+), 22 deletions(-) diff --git a/spicy/lib/spicy_rt.hlt b/spicy/lib/spicy_rt.hlt index e284f9410..8e6fad50b 100644 --- a/spicy/lib/spicy_rt.hlt +++ b/spicy/lib/spicy_rt.hlt @@ -94,6 +94,6 @@ declare public void backtrack() &cxxname="spicy::rt::detail::backtrack" &have_pr declare public void initializeParsedUnit(inout ParsedUnit punit, any unit, TypeInfo ti) &cxxname="spicy::rt::ParsedUnit::initialize" &have_prototype; declare public bytes extractBytes(inout value_ref data, view cur, uint<64> n, bool eod_ok, string location, inout strong_ref filters) &cxxname="spicy::rt::detail::extractBytes" &have_prototype; -declare public bytes expectBytesLiteral(inout value_ref data, view cur, bytes literal, string location, inout strong_ref filters) &cxxname="spicy::rt::detail::expectBytesLiteral" &have_prototype; +declare public void expectBytesLiteral(inout value_ref data, view cur, bytes literal, string location, inout strong_ref filters) &cxxname="spicy::rt::detail::expectBytesLiteral" &have_prototype; } diff --git a/spicy/runtime/include/parser.h b/spicy/runtime/include/parser.h index e77f98540..551576f79 100644 --- a/spicy/runtime/include/parser.h +++ b/spicy/runtime/include/parser.h @@ -575,10 +575,9 @@ hilti::rt::Bytes extractBytes(hilti::rt::ValueReference& data * @returns `literal` (for convenience) * @throws ParseError if the literal isn't found at the beginning of *cur* */ -hilti::rt::Bytes expectBytesLiteral(hilti::rt::ValueReference& data, - const hilti::rt::stream::View& cur, hilti::rt::Bytes literal, - std::string_view location, - const hilti::rt::StrongReference& filters); +void expectBytesLiteral(hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur, + const hilti::rt::Bytes& literal, std::string_view location, + const hilti::rt::StrongReference& filters); } // namespace detail } // namespace spicy::rt diff --git a/spicy/runtime/src/parser.cc b/spicy/runtime/src/parser.cc index 31a588f0d..59191e0ed 100644 --- a/spicy/runtime/src/parser.cc +++ b/spicy/runtime/src/parser.cc @@ -216,9 +216,9 @@ hilti::rt::Bytes detail::extractBytes(hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur, hilti::rt::Bytes literal, - std::string_view location, const hilti::rt::StrongReference& filters) { +void detail::expectBytesLiteral(hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur, + const hilti::rt::Bytes& literal, std::string_view location, + const hilti::rt::StrongReference& filters) { if ( ! detail::waitForInputNoThrow(data, cur, literal.size(), filters) ) { auto msg = hilti::rt::fmt("expected %" PRIu64 R"( bytes for bytes literal "%s")" " (%" PRIu64 " available))", @@ -231,6 +231,4 @@ hilti::rt::Bytes detail::expectBytesLiteral( throw ParseError(hilti::rt::fmt(R"(expected bytes literal "%s" but input starts with "%s")", literal, content), location); } - - return literal; } diff --git a/spicy/runtime/src/tests/parser.cc b/spicy/runtime/src/tests/parser.cc index d4d8cf8ac..5f3fef6a1 100644 --- a/spicy/runtime/src/tests/parser.cc +++ b/spicy/runtime/src/tests/parser.cc @@ -454,7 +454,7 @@ TEST_CASE("expectBytesLiteral") { data->freeze(); auto view = data->view(); - CHECK_EQ(detail::expectBytesLiteral(data, data->view(), "123"_b, "", {}), "123"_b); + CHECK_NOTHROW(detail::expectBytesLiteral(data, data->view(), "123"_b, "", {})); CHECK_THROWS_WITH_AS(detail::expectBytesLiteral(data, data->view(), "abc"_b, "", {}), "expected bytes literal \"abc\" but input starts with \"123\" ()", const spicy::rt::ParseError&); diff --git a/spicy/toolchain/src/compiler/codegen/parsers/literals.cc b/spicy/toolchain/src/compiler/codegen/parsers/literals.cc index ec5d8e9b9..c44816e70 100644 --- a/spicy/toolchain/src/compiler/codegen/parsers/literals.cc +++ b/spicy/toolchain/src/compiler/codegen/parsers/literals.cc @@ -75,6 +75,14 @@ struct Visitor : public visitor::PreOrder { void operator()(hilti::ctor::Bytes* n) final { auto len = builder()->integer(static_cast(n->value().size())); + auto literal = builder()->id( + hilti::ID(fmt("__bytes_%" PRIu64, hilti::util::uitoa_n(hilti::util::hash(n->value()), 16, 5)))); + + if ( ! pb()->cg()->haveAddedDeclaration(literal->id()) ) { + auto d = builder()->constant(literal->id(), builder()->expression(n)); + pb()->cg()->addDeclaration(d); + } + switch ( state().literal_mode ) { case LiteralMode::Default: case LiteralMode::Skip: { @@ -89,8 +97,6 @@ struct Visitor : public visitor::PreOrder { pb()->parseError("unexpected token to consume", n->meta()); popBuilder(); - auto literal = builder()->addTmp("literal", builder()->expression(n)); - pushBuilder(builder()->addIf( builder()->unequal(literal, builder()->memberCall(state().cur, "sub", {builder()->begin(state().cur), @@ -108,27 +114,26 @@ struct Visitor : public visitor::PreOrder { auto expect_bytes_literal = builder()->call("spicy_rt::expectBytesLiteral", - {state().data, state().cur, builder()->expression(n), - builder()->expression(n->meta()), pb()->currentFilters(state())}); + {state().data, state().cur, literal, builder()->expression(n->meta()), + pb()->currentFilters(state())}); + builder()->addExpression(expect_bytes_literal); if ( state().literal_mode != LiteralMode::Skip ) - builder()->addAssign(lp->destination(n->type()->type()), expect_bytes_literal); - else - builder()->addExpression(expect_bytes_literal); + builder()->addAssign(lp->destination(n->type()->type()), literal); pb()->advanceInput(len); if ( check_for_look_ahead ) popBuilder(); - result = builder()->expression(n); + result = literal; return; } case LiteralMode::Search: // Handled in `parseLiteral`. case LiteralMode::Try: - auto cond = builder()->memberCall(state().cur, "starts_with", {builder()->expression(n)}); + auto cond = builder()->memberCall(state().cur, "starts_with", {literal}); result = builder()->ternary(builder()->and_(pb()->waitForInputOrEod(len), cond), builder()->sum(builder()->begin(state().cur), len), builder()->begin(state().cur)); diff --git a/tests/Baseline/spicy.types.function.cxxname-normalization/output b/tests/Baseline/spicy.types.function.cxxname-normalization/output index 627d71b15..fe7ed1c66 100644 --- a/tests/Baseline/spicy.types.function.cxxname-normalization/output +++ b/tests/Baseline/spicy.types.function.cxxname-normalization/output @@ -59,7 +59,7 @@ [debug/resolver] [spicy_rt.hlt:92:33-92:71] Attribute "&cxxname="spicy::rt::detail::backtrack"" -> Attribute "&cxxname="::spicy::rt::detail::backtrack"" [debug/resolver] [spicy_rt.hlt:94:89-94:132] Attribute "&cxxname="spicy::rt::ParsedUnit::initialize"" -> Attribute "&cxxname="::spicy::rt::ParsedUnit::initialize"" [debug/resolver] [spicy_rt.hlt:96:160-96:201] Attribute "&cxxname="spicy::rt::detail::extractBytes"" -> Attribute "&cxxname="::spicy::rt::detail::extractBytes"" -[debug/resolver] [spicy_rt.hlt:97:156-97:203] Attribute "&cxxname="spicy::rt::detail::expectBytesLiteral"" -> Attribute "&cxxname="::spicy::rt::detail::expectBytesLiteral"" +[debug/resolver] [spicy_rt.hlt:97:155-97:202] Attribute "&cxxname="spicy::rt::detail::expectBytesLiteral"" -> Attribute "&cxxname="::spicy::rt::detail::expectBytesLiteral"" [debug/resolver] [spicy.spicy:14:3-14:37] Attribute "&cxxname="hilti::rt::AddressFamily"" -> Attribute "&cxxname="::hilti::rt::AddressFamily"" [debug/resolver] [spicy.spicy:23:3-23:41] Attribute "&cxxname="hilti::rt::integer::BitOrder"" -> Attribute "&cxxname="::hilti::rt::integer::BitOrder"" [debug/resolver] [spicy.spicy:31:3-31:33] Attribute "&cxxname="hilti::rt::ByteOrder"" -> Attribute "&cxxname="::hilti::rt::ByteOrder"" diff --git a/tests/spicy/types/bytes/parse-length.spicy b/tests/spicy/types/bytes/parse-length.spicy index dec9668aa..3e6725993 100644 --- a/tests/spicy/types/bytes/parse-length.spicy +++ b/tests/spicy/types/bytes/parse-length.spicy @@ -5,7 +5,7 @@ # @TEST-EXEC: spicyc -p %INPUT | grep -q 'b2 = spicy_rt::extractBytes' # # Ensure literal-specific optimization kicks in. -# @TEST-EXEC: spicyc -p %INPUT | grep -q ' = spicy_rt::expectBytesLiteral' +# @TEST-EXEC: spicyc -p %INPUT | grep -q '^ *spicy_rt::expectBytesLiteral' # # Ensure we don't get any look-ahead checks when parsing the literals, we don't need them here. # @TEST-EXEC: spicyc -p %INPUT | grep -vq 'if.*lah'