Aharrypotter opened a new pull request, #19879:
URL: https://github.com/apache/tvm/pull/19879
## Summary
This PR extends the TFLite resource hashtable family from #19519 item G from
"import + size" to a constant-foldable `HASHTABLE_FIND` subset. It builds on
the
static `HASHTABLE` / `HASHTABLE_IMPORT` import support added in #19639 and
the
`HASHTABLE_LOOKUP` converter from #19654.
Relax has no string tensor type and no runtime hashtable operator, so this PR
targets the only subset that can be lowered today: tables whose keys and
values
are constants imported in a `CALL_ONCE` init subgraph, queried by a constant
string tensor. In that case the lookup is resolved at import time and
emitted as
a `relax.const`. Runtime string queries remain explicitly guarded instead of
being lowered with incorrect semantics.
## Design
### Static String Tensor Decoding
The frontend now decodes constant TFLite string tensors through a shared
helper
`_get_string_tensor_value`. It parses the TFLite string-tensor binary layout:
```text
[count(i32)][offset_0 .. offset_count(i32)][utf8 string data]
```
The helper validates the buffer length against the declared count, checks
that
offsets are in-bounds and monotonically non-decreasing, and verifies the
decoded
element count matches the tensor shape. This is reusable infrastructure for
any
future TFLite string support, independent of `HASHTABLE_FIND`.
### Hashtable Import State
`HASHTABLE_IMPORT` now stores the actual constant keys and values (numeric or
decoded string buffers) in shared conversion state, instead of only recording
table metadata (size, key/value dtype). Duplicate keys are rejected, because
the
constant-fold lookup assumes a unique key mapping. The captured table is
keyed by
the same `table_id` / handle resolution used by `HASHTABLE` and
`HASHTABLE_SIZE`,
so a `CALL_ONCE` init subgraph and the main graph agree on the same logical
table.
### Constant-Foldable Find
`HASHTABLE_FIND` resolves the table handle through the importer-local handle
map
and the statically imported keys/values. For the supported subset it builds a
Python key -> value map, applies the per-element default value, overwrites
hits
from the table, preserves the query tensor shape, and emits the result as a
`relax.const`. The op produces no runtime Relax computation, which matches
the
fact that both the table and the query are compile-time constants.
The supported subset is intentionally narrow and guard-first:
- the table must be a constant `string -> int64` table imported via a
supported
`CALL_ONCE` `HASHTABLE_IMPORT`
- the query tensor must be a constant string buffer
- the default tensor must be a scalar or match the query shape
### String Graph Input Guard
`TensorType.STRING` graph inputs are now rejected in `from_tflite` with a
clear
`OpNotImplemented` instead of a low-level FFI `unknown dtype string` error,
since
Relax cannot represent a string tensor. This is the path hit by runtime
string
queries for `HASHTABLE_FIND` and string-valued `HASHTABLE_LOOKUP`, so the
guard
gives a frontend-level diagnostic for both.
## Operator Support
| Operator | TFLite options | Relax lowering | Supported subset |
|---|---|---|---|
| `HASHTABLE_IMPORT` | `HashtableImportOptions` | store constant keys/values
in importer state | `CALL_ONCE` init, constant keys/values, no duplicate keys |
| `HASHTABLE_FIND` | `HashtableFindOptions` | constant-fold to `relax.const`
| constant `string -> int64` table + constant string query |
## Not Included
- Runtime (non-constant) string queries and runtime hashtable lookup.
- `int64 -> string` find, which would require string-typed Relax outputs.
- Any general `TensorType.STRING` tensor representation in Relax.
- A runtime Relax hashtable operator with string hashing / comparison.
- Mutable runtime resource-state threading through Relax functions.
These require core Relax support and are out of scope for the frontend.
## Tests
The tests manually build minimal TFLite flatbuffers and compare the imported
Relax IR with `tvm.ir.assert_structural_equal`. Unsupported patterns use
`pytest.raises`.
| Test | Coverage |
|---|---|
| `test_hashtable_call_once_import_find_string_to_int64` | constant `string
-> int64` find folds to a `relax.const` |
| `test_hashtable_call_once_import_find_string_to_int64_2d_query` | query
shape preserved for a 2-D query |
| `test_hashtable_call_once_import_find_int64_to_string_unsupported` |
`int64 -> string` table rejected |
| `test_hashtable_call_once_import_find_runtime_query_unsupported` | runtime
string query rejected |
| `test_hashtable_call_once_import_duplicate_keys_unsupported` | duplicate
static keys rejected |
| `test_hashtable_lookup_string_value_unsupported` | string graph input now
gives a clean `OpNotImplemented` |
Local validation:
```bash
python -m ruff format --check \
python/tvm/relax/frontend/tflite/tflite_frontend.py \
tests/python/relax/test_frontend_tflite.py
python -m ruff check \
python/tvm/relax/frontend/tflite/tflite_frontend.py \
tests/python/relax/test_frontend_tflite.py
python -m pytest \
tests/python/relax/test_frontend_tflite.py \
-k "hashtable or resource or variable" -q
python -m pytest \
tests/python/relax/test_frontend_tflite.py -q
```
Result:
```text
ruff format --check: 2 files already formatted
ruff check: All checks passed
14 passed, 535 deselected
549 passed
```
## References
- Issue #19519 item G: TFLite resource / variable / hashtable operators
- PR #19639: TFLite resource variable and static hashtable import support
- PR #19654: TFLite `HASHTABLE_LOOKUP` converter
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]