From 86b95e30377715377724abe5725589fcbb74eecd Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Wed, 25 Mar 2026 13:59:05 -0700 Subject: [PATCH] Implement a handler for XLS->LLVM conversion for the cover op. We conditionally invoke no-op inline asm that is marked to be side effecting in an effort to keep an edge in the CFG around for coverage-guided fuzzing. PiperOrigin-RevId: 889423101 --- xls/jit/BUILD | 24 ++++++++++++++++++++++++ xls/jit/coverage_test.x | 18 ++++++++++++++++++ xls/jit/ir_builder_visitor.cc | 23 +++++++++++++++++++++-- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 xls/jit/coverage_test.x diff --git a/xls/jit/BUILD b/xls/jit/BUILD index 83693ad2ee..b5902a51a8 100644 --- a/xls/jit/BUILD +++ b/xls/jit/BUILD @@ -1381,12 +1381,36 @@ xls_aot_generate( with_msan = XLS_IS_MSAN_BUILD, ) +xls_dslx_library( + name = "coverage_test_dslx", + srcs = ["coverage_test.x"], +) + +xls_dslx_ir( + name = "coverage_test_ir", + dslx_top = "run_coverage", + ir_conv_args = {"lower_to_proc_scoped_channels": "true"}, + library = ":coverage_test_dslx", +) + +xls_aot_generate( + name = "coverage_test_aot", + testonly = True, + src = ":coverage_test_ir", + enable_llvm_coverage = True, + salt_symbols = False, + top = "run_coverage", + top_type = "FUNCTION", + with_msan = XLS_IS_MSAN_BUILD, +) + # Tests that the coverage instrumentation can be enabled. build_test( name = "jit_build_test", targets = [ ":multi_function_aot_coverage", ":multi_function_aot_coverage_jobs", + ":coverage_test_aot", ], ) diff --git a/xls/jit/coverage_test.x b/xls/jit/coverage_test.x new file mode 100644 index 0000000000..b51c11d05a --- /dev/null +++ b/xls/jit/coverage_test.x @@ -0,0 +1,18 @@ +// 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. + +pub fn run_coverage(a: u32, b: u32, c: u32) -> u32 { + cover!("a_plus_b_gt_c", a + b > c); + a + b + c +} diff --git a/xls/jit/ir_builder_visitor.cc b/xls/jit/ir_builder_visitor.cc index 41692e8739..aaee27d8b6 100644 --- a/xls/jit/ir_builder_visitor.cc +++ b/xls/jit/ir_builder_visitor.cc @@ -43,6 +43,7 @@ #include "llvm/include/llvm/IR/DerivedTypes.h" #include "llvm/include/llvm/IR/GEPNoWrapFlags.h" #include "llvm/include/llvm/IR/IRBuilder.h" +#include "llvm/include/llvm/IR/InlineAsm.h" #include "llvm/include/llvm/IR/Instructions.h" #include "llvm/include/llvm/IR/Intrinsics.h" #include "llvm/include/llvm/IR/LLVMContext.h" @@ -323,7 +324,7 @@ std::vector NumberedStrings(std::string_view s, int64_t count) { return result; } -// Returns the concatentation of two vectors. +// Returns the concatenation of two vectors. std::vector ConcatVectors(absl::Span a, absl::Span b) { std::vector result(a.begin(), a.end()); @@ -2295,8 +2296,26 @@ absl::Status IrBuilderVisitor::HandleCover(Cover* cover) { // support to the JIT. XLS_ASSIGN_OR_RETURN(NodeIrContext node_context, NewNodeIrContext(cover, {"condition"})); + std::optional*> exit_builder; + if (jit_context_.llvm_compiler().include_llvm_coverage()) { + llvm::IRBuilder<>& b = node_context.entry_builder(); + llvm::Value* cond = Truthiness(node_context.LoadOperand(0), b); + LlvmIfThen if_then = CreateIfThen(cond, b, cover->GetName()); + llvm::FunctionType* ft = + llvm::FunctionType::get(llvm::Type::getVoidTy(ctx()), {}, false); + // We invoke inline asm (with hasSideEffects=true) to ensure that the + // coverage point is not optimized away. + llvm::InlineAsm* ia = + llvm::InlineAsm::get(ft, absl::StrCat("# coverage: ", cover->label()), + "", /*hasSideEffects=*/true); + if_then.then_builder->CreateCall(ia, {}); + + exit_builder = if_then.Finalize().get(); + } + llvm::Value* token = type_converter()->GetToken(); - return FinalizeNodeIrContextWithValue(std::move(node_context), token); + return FinalizeNodeIrContextWithValue(std::move(node_context), token, + exit_builder); } absl::Status IrBuilderVisitor::HandleDecode(Decode* decode) {