From c6f06f52d21f1cb42fb87a7a9f00dabdb417941f Mon Sep 17 00:00:00 2001 From: Haavard Date: Mon, 14 Nov 2016 11:42:47 +0000 Subject: added support for pre-detecting function eval issues --- .../compiled_function/compiled_function_test.cpp | 16 +++++++++++++++ .../interpreted_function_test.cpp | 16 +++++++++++++++ vespalib/src/vespa/vespalib/eval/function.h | 11 +++++++++++ .../vespa/vespalib/eval/interpreted_function.cpp | 19 ++++++++++++++++++ .../src/vespa/vespalib/eval/interpreted_function.h | 1 + .../vespa/vespalib/eval/llvm/compiled_function.cpp | 23 ++++++++++++++++++++++ .../vespa/vespalib/eval/llvm/compiled_function.h | 1 + 7 files changed, 87 insertions(+) (limited to 'vespalib') diff --git a/vespalib/src/tests/eval/compiled_function/compiled_function_test.cpp b/vespalib/src/tests/eval/compiled_function/compiled_function_test.cpp index b3777a86df9..82dffd58cdc 100644 --- a/vespalib/src/tests/eval/compiled_function/compiled_function_test.cpp +++ b/vespalib/src/tests/eval/compiled_function/compiled_function_test.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include using namespace vespalib::eval; @@ -169,4 +171,18 @@ TEST("require that tensor operations have sane numeric fallbacks") { //----------------------------------------------------------------------------- +TEST("require function issues can be detected") { + auto simple = Function::parse("a+b"); + auto complex = Function::parse("join(a,b,f(a,b)(a+b))"); + EXPECT_FALSE(simple.has_error()); + EXPECT_FALSE(complex.has_error()); + EXPECT_FALSE(CompiledFunction::detect_issues(simple)); + EXPECT_TRUE(CompiledFunction::detect_issues(complex)); + std::cerr << "Example function issues:" << std::endl + << CompiledFunction::detect_issues(complex).list + << std::endl; +} + +//----------------------------------------------------------------------------- + TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/eval/interpreted_function/interpreted_function_test.cpp b/vespalib/src/tests/eval/interpreted_function/interpreted_function_test.cpp index 9d01488dda1..51aacfd2272 100644 --- a/vespalib/src/tests/eval/interpreted_function/interpreted_function_test.cpp +++ b/vespalib/src/tests/eval/interpreted_function/interpreted_function_test.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include using namespace vespalib::eval; using vespalib::Stash; @@ -188,4 +190,18 @@ TEST("require that matrix multiplication works with tensor function") { //----------------------------------------------------------------------------- +TEST("require function issues can be detected") { + auto simple = Function::parse("a+b"); + auto complex = Function::parse("join(a,b,f(a,b)(a+b))"); + EXPECT_FALSE(simple.has_error()); + EXPECT_FALSE(complex.has_error()); + EXPECT_FALSE(InterpretedFunction::detect_issues(simple)); + EXPECT_TRUE(InterpretedFunction::detect_issues(complex)); + std::cerr << "Example function issues:" << std::endl + << InterpretedFunction::detect_issues(complex).list + << std::endl; +} + +//----------------------------------------------------------------------------- + TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/vespa/vespalib/eval/function.h b/vespalib/src/vespa/vespalib/eval/function.h index a9e2b980025..35a89ce6512 100644 --- a/vespalib/src/vespa/vespalib/eval/function.h +++ b/vespalib/src/vespa/vespalib/eval/function.h @@ -67,6 +67,17 @@ public: vespalib::string &wrapper, vespalib::string &body, vespalib::string &error); + /** + * Issues is used to report issues relating to the function + * structure, typically to explain why a function cannot be + * evaluated in a specific context due to it using features not + * supported in that context. + **/ + struct Issues { + std::vector list; + operator bool() const { return !list.empty(); } + Issues(std::vector &&list_in) : list(std::move(list_in)) {} + }; }; } // namespace vespalib::eval diff --git a/vespalib/src/vespa/vespalib/eval/interpreted_function.cpp b/vespalib/src/vespa/vespalib/eval/interpreted_function.cpp index e1e04ca6ced..a4329f97de2 100644 --- a/vespalib/src/vespa/vespalib/eval/interpreted_function.cpp +++ b/vespalib/src/vespa/vespalib/eval/interpreted_function.cpp @@ -11,6 +11,7 @@ #include #include "tensor_spec.h" #include "simple_tensor_engine.h" +#include namespace vespalib { namespace eval { @@ -453,5 +454,23 @@ InterpretedFunction::eval(Context &ctx) const return state.stack.back(); } +Function::Issues +InterpretedFunction::detect_issues(const Function &function) +{ + struct NotSupported : NodeTraverser { + std::vector issues; + bool open(const nodes::Node &) override { return true; } + void close(const nodes::Node &node) override { + if (nodes::check_type(node)) { + issues.push_back(make_string("unsupported node type: %s", + getClassName(node).c_str())); + } + } + } checker; + function.root().traverse(checker); + return Function::Issues(std::move(checker.issues)); +} + } // namespace vespalib::eval } // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/eval/interpreted_function.h b/vespalib/src/vespa/vespalib/eval/interpreted_function.h index 615f9d4eb82..fa1ea6580dd 100644 --- a/vespalib/src/vespa/vespalib/eval/interpreted_function.h +++ b/vespalib/src/vespa/vespalib/eval/interpreted_function.h @@ -94,6 +94,7 @@ public: size_t program_size() const { return _program.size(); } size_t num_params() const { return _num_params; } const Value &eval(Context &ctx) const; + static Function::Issues detect_issues(const Function &function); }; } // namespace vespalib::eval diff --git a/vespalib/src/vespa/vespalib/eval/llvm/compiled_function.cpp b/vespalib/src/vespa/vespalib/eval/llvm/compiled_function.cpp index 957df7503c3..9b120b677e2 100644 --- a/vespalib/src/vespa/vespalib/eval/llvm/compiled_function.cpp +++ b/vespalib/src/vespa/vespalib/eval/llvm/compiled_function.cpp @@ -3,6 +3,10 @@ #include #include "compiled_function.h" #include +#include +#include +#include +#include namespace vespalib { namespace eval { @@ -46,5 +50,24 @@ CompiledFunction::estimate_cost_us(const std::vector ¶ms) const return BenchmarkTimer::benchmark(actual, baseline, 4.0) * 1000.0 * 1000.0; } +Function::Issues +CompiledFunction::detect_issues(const Function &function) +{ + struct NotSupported : NodeTraverser { + std::vector issues; + bool open(const nodes::Node &) override { return true; } + void close(const nodes::Node &node) override { + if (nodes::check_type(node)) { + issues.push_back(make_string("unsupported node type: %s", + getClassName(node).c_str())); + } + } + } checker; + function.root().traverse(checker); + return Function::Issues(std::move(checker.issues)); +} + } // namespace vespalib::eval } // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/eval/llvm/compiled_function.h b/vespalib/src/vespa/vespalib/eval/llvm/compiled_function.h index cd7ae2189b8..3b10d9a20c5 100644 --- a/vespalib/src/vespa/vespalib/eval/llvm/compiled_function.h +++ b/vespalib/src/vespa/vespalib/eval/llvm/compiled_function.h @@ -56,6 +56,7 @@ public: } void dump() const { _llvm_wrapper.dump(); } double estimate_cost_us(const std::vector ¶ms) const; + static Function::Issues detect_issues(const Function &function); }; } // namespace vespalib::eval -- cgit v1.2.3