Preserve the runtime C++14 floor after user flags
User -CFLAGS may expand through GNU Make or the recipe shell after verilation. When the final expanded -std option or -ansi selects C++98 or C++11, generated runtime compilation can fall below the supported C++14 floor. Expose the configured minimum standard separately, keep the emitted user flags deferred, and append the minimum only for recognized pre-C++14 aliases. Evaluate the floor recursively for every Make target so automatic variables such as $@ cannot cache one target result into later targets. Newer and invalid standards remain untouched, and the last selector retains compiler-compatible precedence. Cover direct, Make-variable, environment-variable, -ansi, ordering, macro-text, invalid-option, automatic-build, simulation, and target-dependent expansion paths. The target regression builds a C++11 object followed by a C++17 object in one Make invocation. RED: the prior self-caching eval appended -std=gnu++14 to the C++17 target, producing __cplusplus=201402L and failing the 201703L assertion. Verification: make -j8; t_cflags_user_cxx_floor; t_flag_ldflags; make format with clang-format 18; changed-file clang-format and yapf checks; targeted pylint; distribution whitespace and C++ style tests; cppcheck-verilator; git diff --check.
This commit is contained in:
parent
dde8de0a0d
commit
5588c85073
10
configure.ac
10
configure.ac
|
|
@ -476,18 +476,22 @@ AC_SUBST(HAVE_DEV_GCOV)
|
|||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PROFILE,-pg)
|
||||
AC_SUBST(CFG_CXXFLAGS_PROFILE)
|
||||
|
||||
# Flag to select newest language standard supported
|
||||
# Flags to select minimum and newest language standards supported
|
||||
# Macros work such that first option that passes is the one we take
|
||||
# Currently enable c++17/c++14 due to packaged SystemC dependency
|
||||
# c++17 is the newest that Verilator is regularly tested to support
|
||||
# c++14 is the oldest that Verilator supports
|
||||
# gnu is required for Cygwin to compile verilated.h successfully
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_MINIMUM,-std=gnu++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_MINIMUM,-std=c++14)
|
||||
AC_SUBST(CFG_CXXFLAGS_STD_MINIMUM)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
|
||||
if test "$CFG_CXXFLAGS_STD_NEWEST" = "" ; then
|
||||
CFG_CXXFLAGS_STD_NEWEST=$CFG_CXXFLAGS_STD_MINIMUM
|
||||
fi
|
||||
AC_SUBST(CFG_CXXFLAGS_STD_NEWEST)
|
||||
|
||||
# Flags for compiling Verilator internals including parser, and Verilated files
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ CFG_CXX_VERSION = "@CFG_CXX_VERSION@"
|
|||
CFG_CXXFLAGS_PROFILE = @CFG_CXXFLAGS_PROFILE@
|
||||
# Select language required to compile (often empty)
|
||||
CFG_CXXFLAGS_STD = @CFG_CXXFLAGS_STD@
|
||||
# Select the minimum supported language standard
|
||||
CFG_CXXFLAGS_STD_MINIMUM = @CFG_CXXFLAGS_STD_MINIMUM@
|
||||
# Select newest language (unused by this Makefile, for some test's Makefiles)
|
||||
CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
|
||||
# Compiler flags to use to turn off unused and generated code warnings, such as -Wno-div-by-zero
|
||||
|
|
|
|||
|
|
@ -686,7 +686,7 @@ public:
|
|||
of.putSet("VM_MODPREFIX", v3Global.opt.modPrefix());
|
||||
|
||||
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
|
||||
of.puts("VM_USER_CFLAGS = \\\n");
|
||||
of.puts("VM_USER_CFLAGS_BASE = \\\n");
|
||||
const std::string solver = V3Options::getenvVERILATOR_SOLVER();
|
||||
if (v3Global.useRandomizeMethods() && solver != "")
|
||||
of.puts("\t-DVM_SOLVER_DEFAULT='\"" + V3OutFormatter::quoteNameControls(solver)
|
||||
|
|
@ -695,6 +695,11 @@ public:
|
|||
const VStringList& cFlags = v3Global.opt.cFlags();
|
||||
for (const string& i : cFlags) of.puts(" " + i + " \\\n");
|
||||
of.puts("\n");
|
||||
if (!cFlags.empty()) {
|
||||
of.puts("VM_USER_CFLAGS = $(VM_USER_CFLAGS_BASE) $(VM_CXXFLAGS_STD_FLOOR)\n\n");
|
||||
} else {
|
||||
of.puts("VM_USER_CFLAGS = $(VM_USER_CFLAGS_BASE)\n\n");
|
||||
}
|
||||
|
||||
of.puts("# User LDLIBS (from -LDFLAGS on Verilator command line)\n");
|
||||
of.puts("VM_USER_LDLIBS = \\\n");
|
||||
|
|
@ -730,6 +735,25 @@ public:
|
|||
}
|
||||
of.puts("# Include global rules\n");
|
||||
of.puts("include $(VERILATOR_ROOT)/include/verilated.mk\n");
|
||||
if (!cFlags.empty()) {
|
||||
of.puts("\n# Keep expanded user CFLAGS at or above the runtime C++ minimum\n");
|
||||
of.puts("VM_CXXFLAGS_STD_FLOOR = $(shell \\\n");
|
||||
of.puts(" for flag in $(VM_USER_CFLAGS_BASE); do "
|
||||
"printf '%s\\n' \"$$flag\"; done \\\n");
|
||||
of.puts(" | grep -e '^-ansi$$' -e '^-std=' -e '^--std=' \\\n");
|
||||
of.puts(" | tail -n 1 \\\n");
|
||||
of.puts(" | grep -F -x \\\n");
|
||||
of.puts(" -e '-ansi' \\\n");
|
||||
of.puts(" -e '-std=c++98' -e '--std=c++98' \\\n");
|
||||
of.puts(" -e '-std=gnu++98' -e '--std=gnu++98' \\\n");
|
||||
of.puts(" -e '-std=c++03' -e '--std=c++03' \\\n");
|
||||
of.puts(" -e '-std=gnu++03' -e '--std=gnu++03' \\\n");
|
||||
of.puts(" -e '-std=c++0x' -e '--std=c++0x' \\\n");
|
||||
of.puts(" -e '-std=gnu++0x' -e '--std=gnu++0x' \\\n");
|
||||
of.puts(" -e '-std=c++11' -e '--std=c++11' \\\n");
|
||||
of.puts(" -e '-std=gnu++11' -e '--std=gnu++11' >/dev/null \\\n");
|
||||
of.puts(" && printf '%s' '$(CFG_CXXFLAGS_STD_MINIMUM)')\n");
|
||||
}
|
||||
|
||||
if (v3Global.opt.exe()) {
|
||||
of.puts("\n### Executable rules... (from --exe)\n");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
static_assert(__cplusplus >= EXPECT_CXX_MIN, "The runtime requires C++14 or newer");
|
||||
|
||||
int cxx14FloorProbe() {
|
||||
const auto identity = [](const auto value) { return value; };
|
||||
return identity(14);
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: user CFLAGS preserve the runtime C++ standard floor
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import os
|
||||
import shlex
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios("vlt")
|
||||
cxx_source = os.path.abspath("t/t_cflags_user_cxx_floor.cpp")
|
||||
test.setenv("EXPECT_CXX_MIN", "201402L")
|
||||
test.setenv("TEST_ENV_CXXFLAGS", "")
|
||||
test.setenv("TEST_MAKE_CXXFLAGS", "-std=c++11")
|
||||
user_object = "t_cflags_user_cxx_floor.o"
|
||||
per_target_old_object = "t_cflags_user_cxx_floor_target_old.o"
|
||||
per_target_new_object = "t_cflags_user_cxx_floor_target_new.o"
|
||||
per_target_makefile = f"{test.obj_dir}/t_cflags_user_cxx_floor_targets.mk"
|
||||
|
||||
test.compile(
|
||||
verilator_flags2=[
|
||||
"--binary",
|
||||
"-CFLAGS",
|
||||
"'-DEXPECT_CXX_MIN=$(EXPECT_CXX_MIN) "
|
||||
"$(TEST_MAKE_CXXFLAGS) $${TEST_ENV_CXXFLAGS}'",
|
||||
cxx_source,
|
||||
],
|
||||
verilator_make_gmake=False,
|
||||
)
|
||||
|
||||
test.write_wholefile(
|
||||
per_target_makefile,
|
||||
f"""TEST_MAKE_CXXFLAGS = $(if $(filter {per_target_old_object},$@),-std=c++11,-std=c++17)
|
||||
EXPECT_CXX_MIN = $(if $(filter {per_target_old_object},$@),201402L,201703L)
|
||||
include {test.vm_prefix}.mk
|
||||
|
||||
{per_target_old_object}: {cxx_source}
|
||||
{per_target_new_object}: {cxx_source} {per_target_old_object}
|
||||
{per_target_old_object} {per_target_new_object}:
|
||||
\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ {cxx_source}
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def compile_case(name, make_flags, env_flags="", min_cxx="201402L", fails=False):
|
||||
logfile = f"{test.obj_dir}/vlt_gcc_{name}.log"
|
||||
cmd = [
|
||||
"env",
|
||||
"TEST_ENV_CXXFLAGS=" + shlex.quote(env_flags),
|
||||
os.environ["MAKE"],
|
||||
"-B",
|
||||
"-C " + test.obj_dir,
|
||||
"-f " + test.vm_prefix + ".mk",
|
||||
"VM_PREFIX=" + test.vm_prefix,
|
||||
"EXPECT_CXX_MIN=" + min_cxx,
|
||||
"TEST_MAKE_CXXFLAGS=" + shlex.quote(make_flags),
|
||||
user_object,
|
||||
]
|
||||
test.run(
|
||||
logfile=logfile,
|
||||
tee=test.verbose,
|
||||
fails="any" if fails else False,
|
||||
cmd=cmd,
|
||||
)
|
||||
|
||||
|
||||
compile_case("cxx98", "-std=c++98")
|
||||
compile_case("ansi", "-ansi")
|
||||
compile_case("ansi_then_new", "-ansi -std=c++17", min_cxx="201703L")
|
||||
compile_case("new_then_ansi", "-std=c++17 -ansi")
|
||||
compile_case("cxx11", "-std=c++11")
|
||||
compile_case("cxx14", "-std=c++14")
|
||||
compile_case("cxx17", "-std=c++17", min_cxx="201703L")
|
||||
compile_case("last_old", "-std=c++17", env_flags="-std=c++11")
|
||||
compile_case(
|
||||
"last_new",
|
||||
"-std=c++11",
|
||||
env_flags="-std=c++17",
|
||||
min_cxx="201703L",
|
||||
)
|
||||
compile_case(
|
||||
"macro",
|
||||
"-DKEEP=-std=c++11 -std=c++17",
|
||||
min_cxx="201703L",
|
||||
)
|
||||
compile_case("invalid", "-std=c++11junk", fails=True)
|
||||
test.run(
|
||||
logfile=f"{test.obj_dir}/vlt_gcc_per_target.log",
|
||||
tee=test.verbose,
|
||||
cmd=[
|
||||
os.environ["MAKE"],
|
||||
"-B",
|
||||
"-C " + test.obj_dir,
|
||||
"-f " + os.path.basename(per_target_makefile),
|
||||
"VM_PREFIX=" + test.vm_prefix,
|
||||
per_target_new_object,
|
||||
],
|
||||
)
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue