util: Add runtime parser for boolean lookup tables

Reviewed-by: Alyssa Rosenzweig <alyssa.rosenzweig@intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41872>
This commit is contained in:
Caio Oliveira 2026-05-30 14:57:03 -07:00 committed by Marge Bot
parent 0dcca5ad8c
commit b74eff31d9
3 changed files with 188 additions and 1 deletions

View file

@ -3,6 +3,10 @@
* SPDX-License-Identifier: MIT
*/
#include "lut.h"
#include <string.h>
/*
* This table was generated by an offline tool in the following manner:
* for each function value, we generated a tree of all expressions that evaluate
@ -269,3 +273,124 @@ const char *util_lut3_to_str[256] = {
[0xFE] = "a | b | c",
[0xFF] = "ones",
};
#if !defined(_MSC_VER)
struct lut3_parser {
const char *s;
bool error;
};
static void
lut3_skip_space(struct lut3_parser *p)
{
while (p->s[0] == ' ')
p->s++;
}
static bool
lut3_match(struct lut3_parser *p, char c)
{
lut3_skip_space(p);
if (p->s[0] == c) {
p->s++;
return true;
} else {
return false;
}
}
static bool
lut3_match_str(struct lut3_parser *p, const char *pat)
{
lut3_skip_space(p);
size_t len = strlen(pat);
if (!strncmp(p->s, pat, len)) {
p->s += len;
return true;
} else {
return false;
}
}
static util_lut3 lut3_parse_or(struct lut3_parser *p);
static util_lut3
lut3_parse_value(struct lut3_parser *p)
{
if (lut3_match(p, 'a')) return UTIL_LUT3(a);
if (lut3_match(p, 'b')) return UTIL_LUT3(b);
if (lut3_match(p, 'c')) return UTIL_LUT3(c);
if (lut3_match_str(p, "zeros")) return UTIL_LUT3(0);
if (lut3_match_str(p, "ones")) return UTIL_LUT3(~0);
if (lut3_match(p, '(')) {
util_lut3 val = lut3_parse_or(p);
if (lut3_match(p, ')'))
return val;
}
p->error = true;
return 0;
}
static util_lut3
lut3_parse_not(struct lut3_parser *p)
{
bool has_not = false;
while (lut3_match(p, '~'))
has_not = !has_not;
util_lut3 val = lut3_parse_value(p);
return has_not ? ~val : val;
}
static util_lut3
lut3_parse_and(struct lut3_parser *p)
{
util_lut3 val = lut3_parse_not(p);
while (lut3_match(p, '&'))
val &= lut3_parse_not(p);
return val;
}
static util_lut3
lut3_parse_xor(struct lut3_parser *p)
{
util_lut3 val = lut3_parse_and(p);
while (lut3_match(p, '^'))
val ^= lut3_parse_and(p);
return val;
}
static util_lut3
lut3_parse_or(struct lut3_parser *p)
{
util_lut3 val = lut3_parse_xor(p);
while (lut3_match(p, '|'))
val |= lut3_parse_xor(p);
return val;
}
util_lut3
util_lut3_parse(const char *s, bool *ok)
{
struct lut3_parser p = { .s = s };
util_lut3 val = 0;
if (s && s[0]) {
val = lut3_parse_or(&p);
lut3_skip_space(&p);
if (p.s[0])
p.error = true;
} else {
p.error = true;
}
if (ok)
*ok = !p.error;
return val;
}
#endif

View file

@ -6,9 +6,14 @@
#pragma once
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include "util/macros.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Represents a boolean lookup table in sum-of-minterms form. These are
* natural encodings, matching the Intel BFN and Apple BITOP instructions.
@ -61,6 +66,8 @@ util_lut2_invert_source(util_lut2 l, unsigned s)
{
return (util_lut2)(util_lut3_invert_source((util_lut3)l, s) & 0xf);
}
util_lut3 util_lut3_parse(const char *s, bool *ok);
#endif
/*
@ -102,6 +109,10 @@ util_lut3_swap_sources(util_lut3 l, unsigned a, unsigned b)
UNREACHABLE("invalid source selection");
}
/* Finding minimal string forms of LUTs is tricky, so we precompute. */
extern const char *util_lut3_to_str[256];
#ifdef __cplusplus
} /* extern "C" */
#endif

View file

@ -72,4 +72,55 @@ TEST(lut, swap_sources3)
EXPECT_LUT3(util_lut3_swap_sources(UTIL_LUT3(a | ~b | c), 0, 2), "a | ~b | c");
EXPECT_LUT3(util_lut3_swap_sources(UTIL_LUT3(a | ~b | c), 1, 2), "a | b | ~c");
}
TEST(lut, parse)
{
for (unsigned i = 0; i < ARRAY_SIZE(util_lut3_to_str); i++) {
const char *str = util_lut3_to_str[i];
bool ok = false;
const unsigned got = util_lut3_parse(str, &ok);
EXPECT_TRUE(ok) << "CASE: " << str;
EXPECT_EQ(got, i) << "CASE: " << str;
}
const char *invalids[] = {
NULL,
"",
" ",
"a)",
"onesx",
"a &",
"((a)",
};
for (unsigned i = 0; i < ARRAY_SIZE(invalids); i++) {
bool ok = true;
util_lut3_parse(invalids[i], &ok);
EXPECT_FALSE(ok) << "CASE #" << i;
}
struct {
const char *a;
const char *b;
} equivalents[] = {
{ "~~a", "a" },
{ "a | b & c", "a | (b & c)" },
{ "a | b ^ c", "a | (b ^ c)" },
{ "a & (b | c)", "a & b | a & c" },
{ "a & ones", "a" },
{ "~(a & b)", "~a | ~b" },
{ "((a | b) & (b | c))", "(a | b) & (b | c)" },
};
for (unsigned i = 0; i < ARRAY_SIZE(equivalents); i++) {
bool ok_a = false;
bool ok_b = false;
util_lut3 a = util_lut3_parse(equivalents[i].a, &ok_a);
util_lut3 b = util_lut3_parse(equivalents[i].b, &ok_b);
EXPECT_TRUE(ok_a) << equivalents[i].a;
EXPECT_TRUE(ok_b) << equivalents[i].b;
EXPECT_EQ(a, b) << equivalents[i].a << " and " << equivalents[i].b;
}
}
#endif