diff --git a/xls/public/BUILD b/xls/public/BUILD index 1bb895c5cf..8c647e0f56 100644 --- a/xls/public/BUILD +++ b/xls/public/BUILD @@ -157,6 +157,8 @@ cc_library( hdrs = ["c_api.h"], deps = [ ":runtime_build_actions", + "//xls/interpreter:ir_interpreter", + "//xls/ir:ir_parser", ], ) diff --git a/xls/public/c_api.cc b/xls/public/c_api.cc index dcdc84fc73..d6244f09fb 100644 --- a/xls/public/c_api.cc +++ b/xls/public/c_api.cc @@ -14,6 +14,8 @@ #include "xls/public/c_api.h" +#include "xls/interpreter/function_interpreter.h" +#include "xls/ir/ir_parser.h" #include "xls/public/runtime_build_actions.h" namespace { @@ -45,6 +47,7 @@ bool xls_convert_dslx_to_ir(const char* dslx, const char* path, CHECK(dslx != nullptr); CHECK(path != nullptr); CHECK(dslx_stdlib_path != nullptr); + CHECK(error_out != nullptr); std::vector additional_search_paths_cpp = ToCpp(additional_search_paths, additional_search_paths_count); @@ -67,6 +70,7 @@ bool xls_convert_dslx_path_to_ir(const char* path, const char* dslx_stdlib_path, char** error_out, char** ir_out) { CHECK(path != nullptr); CHECK(dslx_stdlib_path != nullptr); + CHECK(error_out != nullptr); std::vector additional_search_paths_cpp = ToCpp(additional_search_paths, additional_search_paths_count); @@ -83,4 +87,126 @@ bool xls_convert_dslx_path_to_ir(const char* path, const char* dslx_stdlib_path, return false; } +bool xls_parse_typed_value(const char* input, char** error_out, + xls_value** xls_value_out) { + CHECK(input != nullptr); + CHECK(error_out != nullptr); + CHECK(xls_value_out != nullptr); + + absl::StatusOr value_or = xls::Parser::ParseTypedValue(input); + if (value_or.ok()) { + *xls_value_out = reinterpret_cast( + new xls::Value(std::move(value_or).value())); + return true; + } + + *xls_value_out = nullptr; + *error_out = ToOwnedCString(value_or.status().ToString()); + return false; +} + +void xls_value_free(xls_value* v) { + if (v == nullptr) { + return; + } + delete reinterpret_cast(v); +} + +void xls_package_free(struct xls_package* p) { + if (p == nullptr) { + return; + } + delete reinterpret_cast(p); +} + +bool xls_value_to_string(const struct xls_value* v, char** string_out) { + CHECK(v != nullptr); + CHECK(string_out != nullptr); + std::string s = reinterpret_cast(v)->ToString(); + *string_out = strdup(s.c_str()); + return *string_out != nullptr; +} + +bool xls_value_eq(const struct xls_value* v, const struct xls_value* w) { + CHECK(v != nullptr); + CHECK(w != nullptr); + + const auto* lhs = reinterpret_cast(v); + const auto* rhs = reinterpret_cast(w); + return *lhs == *rhs; +} + +bool xls_parse_ir_package(const char* ir, const char* filename, + char** error_out, + struct xls_package** xls_package_out) { + CHECK(ir != nullptr); + CHECK(error_out != nullptr); + CHECK(xls_package_out != nullptr); + + std::optional cpp_filename; + if (filename != nullptr) { + cpp_filename.emplace(filename); + } + absl::StatusOr> package_or = + xls::Parser::ParsePackage(ir, cpp_filename); + if (package_or.ok()) { + *xls_package_out = + reinterpret_cast(package_or.value().release()); + *error_out = nullptr; + return true; + } + + *xls_package_out = nullptr; + *error_out = ToOwnedCString(package_or.status().ToString()); + return false; +} + +bool xls_package_get_function(struct xls_package* package, + const char* function_name, char** error_out, + struct xls_function** result_out) { + xls::Package* xls_package = reinterpret_cast(package); + absl::StatusOr function_or = + xls_package->GetFunction(function_name); + if (function_or.ok()) { + *result_out = reinterpret_cast(function_or.value()); + return true; + } + + *result_out = nullptr; + *error_out = ToOwnedCString(function_or.status().ToString()); + return false; +} + +bool xls_interpret_function(struct xls_function* function, size_t argc, + const struct xls_value** args, char** error_out, + struct xls_value** result_out) { + CHECK(function != nullptr); + CHECK(args != nullptr); + + xls::Function* xls_function = reinterpret_cast(function); + + std::vector xls_args; + xls_args.reserve(argc); + for (size_t i = 0; i < argc; ++i) { + CHECK(args[i] != nullptr); + xls_args.push_back(*reinterpret_cast(args[i])); + } + + absl::StatusOr> result_or = + xls::InterpretFunction(xls_function, xls_args); + if (result_or.ok()) { + // TODO(cdleary): 2024-05-30 We should pass back interpreter events through + // this API instead of dropping them. + xls::Value result_value = + xls::DropInterpreterEvents(std::move(result_or)).value(); + *result_out = reinterpret_cast( + new xls::Value(std::move(result_value))); + return true; + } + + *result_out = nullptr; + *error_out = ToOwnedCString(result_or.status().ToString()); + return false; +} + } // extern "C" diff --git a/xls/public/c_api.h b/xls/public/c_api.h index 2f01172c8d..1c7fef5f65 100644 --- a/xls/public/c_api.h +++ b/xls/public/c_api.h @@ -37,6 +37,11 @@ extern "C" { +// Opaque structs. +struct xls_value; +struct xls_package; +struct xls_function; + bool xls_convert_dslx_to_ir(const char* dslx, const char* path, const char* module_name, const char* dslx_stdlib_path, @@ -49,6 +54,43 @@ bool xls_convert_dslx_path_to_ir(const char* path, const char* dslx_stdlib_path, size_t additional_search_paths_count, char** error_out, char** ir_out); +// Parses a string that represents a typed XLS value; e.g. `bits[32]:0x42`. +bool xls_parse_typed_value(const char* input, char** error_out, + struct xls_value** xls_value_out); + +// Returns a string representation of the given value `v`. +bool xls_value_to_string(const struct xls_value* v, char** string_out); + +// Returns whether `v` is equal to `w`. +bool xls_value_eq(const struct xls_value* v, const struct xls_value* w); + +// Deallocates a value, e.g. one as created by `xls_parse_typed_value`. +void xls_value_free(struct xls_value* v); + +void xls_package_free(struct xls_package* p); + +// Parses IR text to a package. + +// Note: `filename` may be nullptr. +bool xls_parse_ir_package(const char* ir, const char* filename, + char** error_out, + struct xls_package** xls_package_out); + +// Returns a function contained within the given `package`. +// +// Note: the returned function does not need to be freed, it is tied to the +// package's lifetime. +bool xls_package_get_function(struct xls_package* package, + const char* function_name, char** error_out, + struct xls_function** result_out); + +// Interprets the given `function` using the given `args` (an array of size +// `argc`) -- interpretation runs to a function result placed in `result_out`, +// or `error_out` is populated and false is returned in the event of an error. +bool xls_interpret_function(struct xls_function* function, size_t argc, + const struct xls_value** args, char** error_out, + struct xls_value** result_out); + } // extern "C" #endif // XLS_PUBLIC_C_API_H_ diff --git a/xls/public/c_api_test.cc b/xls/public/c_api_test.cc index 9eafcb68d1..d0ad16042d 100644 --- a/xls/public/c_api_test.cc +++ b/xls/public/c_api_test.cc @@ -27,7 +27,7 @@ namespace { using testing::HasSubstr; -// Smoke test for ConvertDslxToIr C API. +// Smoke test for `xls_convert_dslx_to_ir` C API. TEST(XlsCApiTest, ConvertDslxToIrSimple) { const std::string kProgram = "fn id(x: u32) -> u32 { x }"; const char* additional_search_paths[] = {}; @@ -75,7 +75,7 @@ TEST(XlsCApiTest, ConvertDslxToIrError) { EXPECT_THAT(error_out, HasSubstr("Unrecognized character: '@'")); } -// Smoke test for ConvertDslxPathToIr C API. +// Smoke test for `xls_convert_dslx_path_to_ir` C API. TEST(XlsCApiTest, ConvertDslxPathToIr) { const std::string kProgram = "fn id(x: u32) -> u32 { x }"; @@ -104,4 +104,49 @@ TEST(XlsCApiTest, ConvertDslxPathToIr) { EXPECT_THAT(ir_out, HasSubstr("fn __my_module__id")); } +TEST(XlsCApiTest, ParseTypedValueAndFreeIt) { + char* error = nullptr; + struct xls_value* value = nullptr; + ASSERT_TRUE(xls_parse_typed_value("bits[32]:0x42", &error, &value)); + + char* string_out = nullptr; + ASSERT_TRUE(xls_value_to_string(value, &string_out)); + EXPECT_EQ(std::string{string_out}, "bits[32]:66"); + free(string_out); + + xls_value_free(value); +} + +TEST(XlsCApiTest, ParsePackageAndInterpretFunctionInIt) { + const std::string kPackage = R"( +package p + +fn f(x: bits[32]) -> bits[32] { + ret y: bits[32] = identity(x) +} +)"; + + char* error = nullptr; + struct xls_package* package = nullptr; + ASSERT_TRUE(xls_parse_ir_package(kPackage.c_str(), "p.ir", &error, &package)) + << "xls_parse_ir_package error: " << error; + absl::Cleanup free_package([package] { xls_package_free(package); }); + + struct xls_function* function = nullptr; + ASSERT_TRUE(xls_package_get_function(package, "f", &error, &function)); + + struct xls_value* ft = nullptr; + ASSERT_TRUE(xls_parse_typed_value("bits[32]:0x42", &error, &ft)); + absl::Cleanup free_ft([ft] { xls_value_free(ft); }); + + const struct xls_value* args[] = {ft}; + + struct xls_value* result = nullptr; + ASSERT_TRUE( + xls_interpret_function(function, /*argc=*/1, args, &error, &result)); + absl::Cleanup free_result([result] { xls_value_free(result); }); + + ASSERT_TRUE(xls_value_eq(ft, result)); +} + } // namespace