Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions xls/build_rules/xls_dslx_rules.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ def _get_dslx_test_cmdline(ctx, src, all_srcs, append_cmd_line_args = True):
"format_preference",
"configured_values",
"lower_to_proc_scoped_channels",
"lower_to_ir",
"convert_tests",
)

dslx_test_args = dict(_dslx_test_args)
Expand Down
2 changes: 2 additions & 0 deletions xls/dslx/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ cc_binary(
visibility = ["//xls:xls_users"],
deps = [
":command_line_utils",
":create_import_data",
":default_dslx_stdlib_path",
":parse_and_typecheck",
":virtualizable_file_system",
Expand All @@ -626,6 +627,7 @@ cc_binary(
"//xls/common:init_xls",
"//xls/common/file:filesystem",
"//xls/common/status:status_macros",
"//xls/dslx/ir_convert:ir_converter",
"//xls/dslx/run_routines",
"//xls/dslx/run_routines:ir_test_runner",
"//xls/dslx/run_routines:run_comparator",
Expand Down
4 changes: 4 additions & 0 deletions xls/dslx/frontend/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ class Module : public AstNode {
return GetTopWithT<TestProc>();
}

std::vector<Function*> GetFunctions() const {
return GetTopWithT<Function>();
}

std::vector<Impl*> GetImpls() const { return GetTopWithT<Impl>(); }

// Returns the identifiers for all functions within this module (in the order
Expand Down
75 changes: 75 additions & 0 deletions xls/dslx/interpreter_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
#include "xls/common/init_xls.h"
#include "xls/common/status/status_macros.h"
#include "xls/dslx/command_line_utils.h"
#include "xls/dslx/create_import_data.h"
#include "xls/dslx/default_dslx_stdlib_path.h"
#include "xls/dslx/ir_convert/ir_converter.h"
#include "xls/dslx/parse_and_typecheck.h"
#include "xls/dslx/run_routines/ir_test_runner.h"
#include "xls/dslx/run_routines/run_comparator.h"
Expand Down Expand Up @@ -106,6 +108,12 @@ ABSL_FLAG(
"this flag is off because the IR interpreter is too slow.");
ABSL_FLAG(std::string, configured_values, "",
"Configured values to use in DSLX parsing.");
ABSL_FLAG(std::optional<bool>, lower_to_ir, true,
"Enable checking if the code cannot be lowered to IR.");
ABSL_FLAG(std::optional<bool>, convert_tests, false,
"Include tests in the IR conversion test. Has effect only when "
"'lower_to_ir' flag is set.");

// LINT.ThenChange(//xls/build_rules/xls_dslx_rules.bzl)

namespace xls::dslx {
Expand Down Expand Up @@ -277,6 +285,73 @@ absl::StatusOr<TestResult> RealMain(
SetFileContents(absl::GetFlag(FLAGS_output_results_proto), text));
}

// Early feeback if the code cannot be lowered to IR.
std::optional<bool> lower_to_ir_flag = absl::GetFlag(FLAGS_lower_to_ir);
if (lower_to_ir_flag.value_or(false)) {
LOG(INFO) << "Checking if code can be lowered to IR";
std::optional<bool> convert_tests = absl::GetFlag(FLAGS_convert_tests);
bool is_convert_tests = convert_tests.value_or(false);
bool is_type_inference_v2 = type_inference_v2_flag.value_or(false);
bool printed_error = true;

ConvertOptions ir_convert_options = {
.emit_positions = true,
.emit_assert = true,
.emit_cover = true,
.verify_ir = true,
.warnings_as_errors = false,
.warnings = kAllWarningsSet,
.convert_tests = is_convert_tests,
.type_inference_v2 = is_type_inference_v2,
.lower_to_proc_scoped_channels = true,
};
std::array<std::string_view, 1> module_path{entry_module_path};

ImportData import_data(CreateImportData(
dslx_stdlib_path.string(), dslx_paths, ir_convert_options.warnings,
std::make_unique<RealFilesystem>()));

absl::StatusOr<TypecheckedModule> tm = ParseAndTypecheck(
program, entry_module_path, module_name, &import_data);

// Module conversion cannot be used because it skips CheckAcceptableTopProc.
// Instead, we collect non-parametric processes and functions which are then
// passed separately as tops.
std::vector<std::string> module_elements;

std::vector<Proc*> module_procs = tm->module->GetProcs();
for (Proc* elem : module_procs) {
if (!elem->IsParametric()) {
module_elements.push_back(elem->identifier());
}
}

std::vector<Function*> module_funcs = tm->module->GetFunctions();
for (Function* elem : module_funcs) {
if (!elem->IsParametric()) {
module_elements.push_back(elem->identifier());
}
}

std::vector<std::string> failed_ir_conversion_entries;
for (std::string& elem : module_elements) {
// Convert to IR each element separately.
absl::StatusOr<PackageConversionData> ir_conv_result =
ConvertFilesToPackage(module_path, dslx_stdlib_path.string(),
dslx_paths, ir_convert_options, elem,
module_name, &printed_error);
if (!ir_conv_result.ok()) {
failed_ir_conversion_entries.push_back(std::move(elem));
}
}

if (!failed_ir_conversion_entries.empty()) {
return absl::AbortedError(absl::StrFormat(
"IR conversion test failed for %s.",
absl::StrJoin(failed_ir_conversion_entries, ", ")));
}
}

return test_result.result();
}

Expand Down
51 changes: 51 additions & 0 deletions xls/dslx/interpreter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,57 @@ def test_out_of_tree_interpreter_invocation_with_tiv2_annotation(self):
self.assertEqual(p.returncode, 0)
self.assertIn('1 test(s) ran; 0 failed; 0 skipped', p.stderr)

def test_lower_to_ir_check(self):
"""Tests performing an early feedback IR conversion test."""
program = """
proc Generator {
out_ch: chan<u32> out;
val: u32;

init {()}
config(out_ch: chan<u32> out, val: u32, val2: u32) {
(out_ch, val + val2)
}
next(state: ()) {
send(join(), out_ch, val);
}
}

#[test_proc]
proc Testing {
terminator: chan<bool> out;
response: chan<u32> in;

init { }

config(terminator: chan<bool> out){
let (s, r) = chan<u32, u32:1>("test_chan");
spawn Generator(s, u32:66, u32:99);
(terminator, r)
}

next(state: ()) {
let (tok, data) = recv(join(), response);
send(tok, terminator, true);
}
}
"""
temp_file = self.create_tempfile(content=program)
# Note: we have to supply `env` to avoid the Python testbridge setting
# seeping in.
p = subp.run(
[self.interpreter_path.full_path, temp_file.full_path],
stdout=subp.PIPE,
stderr=subp.PIPE,
encoding='utf-8',
env={},
check=False,
)
print('p:', p)
self.assertEqual(p.returncode, 1)
self.assertIn('1 test(s) ran; 0 failed; 0 skipped', p.stderr)
self.assertIn('IR conversion test failed for Generator.', p.stderr)


if __name__ == '__main__':
test_base.main()
3 changes: 3 additions & 0 deletions xls/dslx/stdlib/tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ xls_dslx_test(
name = "round_dslx_test",
srcs = ["round_tests.x"],
deps = ["//xls/dslx/stdlib:round_dslx"],
dslx_test_args = {
"lower_to_ir": "false",
},
)

cc_test(
Expand Down
11 changes: 11 additions & 0 deletions xls/examples/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1405,6 +1405,17 @@ xls_dslx_library(
srcs = ["const_if.x"],
)

# This is an example that passes DSLX test but can't be converted to IR,
# so early IR conversion feedback is disabled ('lower_to_ir' flag).
xls_dslx_test(
name = "cannot_lower_to_ir_test",
srcs = ["cannot_lower_to_ir.x"],
dslx_test_args = {
"lower_to_ir": "false",
"convert_tests": "true",
},
)

xls_dslx_ir(
name = "const_if_ir",
dslx_top = "Main",
Expand Down
47 changes: 47 additions & 0 deletions xls/examples/cannot_lower_to_ir.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2026 The XLS Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// From `ProcWithUnconvertibleConfigGivesUsefulError` IR converter test
// as an example of proc that cannot be converted to IR.
proc Generator {
out_ch: chan<u32> out;
val: u32;

init {()}
config(out_ch: chan<u32> out, val: u32, val2: u32) {
(out_ch, val + val2)
}
next(state: ()) {
send(join(), out_ch, val);
}
}

#[test_proc]
proc Testing {
terminator: chan<bool> out;
response: chan<u32> in;

init { }

config(terminator: chan<bool> out){
let (s, r) = chan<u32, u32:1>("test_chan");
spawn Generator(s, u32:66, u32:99);
(terminator, r)
}

next(state: ()) {
let (tok, data) = recv(join(), response);
send(tok, terminator, true);
}
}
4 changes: 3 additions & 1 deletion xls/examples/dslx_intro/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ xls_dslx_opt_ir_test(
dep = ":crc32_one_byte_inferred",
)

# TODO(leary): 2019-07-24 Missing conversion of 'for/enumerate'.
# TODO: Remove `lower_to_ir` flag once the support for std::enumerate
# is merged: https://github.com/google/xls/pull/3816
xls_dslx_test(
name = "prefix_scan_equality_dslx_test",
srcs = ["prefix_scan_equality.x"],
dslx_test_args = {
"compare": "none",
"lower_to_ir": "false",
},
)

Expand Down
Loading