Skip to content

Commit

Permalink
Optimize parsing of literal bytes.
Browse files Browse the repository at this point in the history
We now create the bytes instance representing the literal as a global
singleton to avoid instantiating it over and over again.

Closes #1910.
  • Loading branch information
rsmmr committed Oct 30, 2024
1 parent 88ea73f commit 27e0c57
Show file tree
Hide file tree
Showing 7 changed files with 24 additions and 22 deletions.
2 changes: 1 addition & 1 deletion spicy/lib/spicy_rt.hlt
Original file line number Diff line number Diff line change
Expand Up @@ -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<stream> data, view<stream> cur, uint<64> n, bool eod_ok, string location, inout strong_ref<Filters> filters) &cxxname="spicy::rt::detail::extractBytes" &have_prototype;
declare public bytes expectBytesLiteral(inout value_ref<stream> data, view<stream> cur, bytes literal, string location, inout strong_ref<Filters> filters) &cxxname="spicy::rt::detail::expectBytesLiteral" &have_prototype;
declare public void expectBytesLiteral(inout value_ref<stream> data, view<stream> cur, bytes literal, string location, inout strong_ref<Filters> filters) &cxxname="spicy::rt::detail::expectBytesLiteral" &have_prototype;

}
7 changes: 3 additions & 4 deletions spicy/runtime/include/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -575,10 +575,9 @@ hilti::rt::Bytes extractBytes(hilti::rt::ValueReference<hilti::rt::Stream>& 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<hilti::rt::Stream>& data,
const hilti::rt::stream::View& cur, hilti::rt::Bytes literal,
std::string_view location,
const hilti::rt::StrongReference<spicy::rt::filter::detail::Filters>& filters);
void expectBytesLiteral(hilti::rt::ValueReference<hilti::rt::Stream>& data, const hilti::rt::stream::View& cur,
const hilti::rt::Bytes& literal, std::string_view location,
const hilti::rt::StrongReference<spicy::rt::filter::detail::Filters>& filters);

} // namespace detail
} // namespace spicy::rt
8 changes: 3 additions & 5 deletions spicy/runtime/src/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ hilti::rt::Bytes detail::extractBytes(hilti::rt::ValueReference<hilti::rt::Strea
return cur.sub(cur.begin() + size).data();
}

hilti::rt::Bytes detail::expectBytesLiteral(
hilti::rt::ValueReference<hilti::rt::Stream>& data, const hilti::rt::stream::View& cur, hilti::rt::Bytes literal,
std::string_view location, const hilti::rt::StrongReference<spicy::rt::filter::detail::Filters>& filters) {
void detail::expectBytesLiteral(hilti::rt::ValueReference<hilti::rt::Stream>& data, const hilti::rt::stream::View& cur,
const hilti::rt::Bytes& literal, std::string_view location,
const hilti::rt::StrongReference<spicy::rt::filter::detail::Filters>& filters) {
if ( ! detail::waitForInputNoThrow(data, cur, literal.size(), filters) ) {
auto msg = hilti::rt::fmt("expected %" PRIu64 R"( bytes for bytes literal "%s")"
" (%" PRIu64 " available))",
Expand All @@ -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;
}
2 changes: 1 addition & 1 deletion spicy/runtime/src/tests/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ TEST_CASE("expectBytesLiteral") {
data->freeze();
auto view = data->view();

CHECK_EQ(detail::expectBytesLiteral(data, data->view(), "123"_b, "<location>", {}), "123"_b);
CHECK_NOTHROW(detail::expectBytesLiteral(data, data->view(), "123"_b, "<location>", {}));
CHECK_THROWS_WITH_AS(detail::expectBytesLiteral(data, data->view(), "abc"_b, "<location>", {}),
"expected bytes literal \"abc\" but input starts with \"123\" (<location>)",
const spicy::rt::ParseError&);
Expand Down
23 changes: 14 additions & 9 deletions spicy/toolchain/src/compiler/codegen/parsers/literals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ struct Visitor : public visitor::PreOrder {
void operator()(hilti::ctor::Bytes* n) final {
auto len = builder()->integer(static_cast<uint64_t>(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: {
Expand All @@ -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),
Expand All @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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""
Expand Down
2 changes: 1 addition & 1 deletion tests/spicy/types/bytes/parse-length.spicy
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down

0 comments on commit 27e0c57

Please sign in to comment.