Drop redundant Known() guard when discriminant is provably Known (#257)
The optimized switch block opens with
const auto emboss_reserved_switch_discrim = \${discriminant};
if (!emboss_reserved_switch_discrim.Known()) return false;
The Known() guard is a safety net: if reading the discriminant
failed, Ok() must return false. But fields_in_dependency_order
guarantees the discriminant field is validated earlier in Ok(), and
for a non-conditional, non-virtual field of the current structure
the earlier \`if (has_X().ValueOrDefault() && !X().Ok()) return false;\`
already enforces X().Ok(). Past that point the Maybe<> wrapper around
the field read is provably Known and the extra guard is dead code.
_is_discriminant_provably_known recognizes the safe shape — a
single-segment field_reference to a non-conditional non-virtual field
in the current structure's field list — and removes the guard in
that case. The detection is intentionally narrow; nested paths and
computed discriminants keep the guard.
One line saved per qualifying switch — minor on hosted targets, but
on Thumb-2 and MicroBlaze this removes a load, a compare, and a
conditional return per switch block, plus the constant-pool slot.diff --git a/compiler/back_end/cpp/generated_code_templates b/compiler/back_end/cpp/generated_code_templates
index 6d0de85..9e2c781 100644
--- a/compiler/back_end/cpp/generated_code_templates
+++ b/compiler/back_end/cpp/generated_code_templates
@@ -403,8 +403,7 @@
// Check fields that depend on a common discriminant using a switch.
{
const auto emboss_reserved_switch_discrim = ${discriminant};
- if (!emboss_reserved_switch_discrim.Known()) return false;
- switch (emboss_reserved_switch_discrim.ValueOrDefault()) {
+${known_check} switch (emboss_reserved_switch_discrim.ValueOrDefault()) {
${switch_cases}
default: break;
diff --git a/compiler/back_end/cpp/header_generator.py b/compiler/back_end/cpp/header_generator.py
index b3305d3..0342f92 100644
--- a/compiler/back_end/cpp/header_generator.py
+++ b/compiler/back_end/cpp/header_generator.py
@@ -1380,6 +1380,47 @@
assert False, "Unsupported switch case type"
+def _is_discriminant_provably_known(discrim_expr, fields):
+ """Returns True when the switch's Known() guard is provably redundant.
+
+ The optimized switch block emits
+
+ if (!emboss_reserved_switch_discrim.Known()) return false;
+
+ as a safety net: if reading the discriminant failed for any reason,
+ Ok() must return false. But when the discriminant is a direct
+ reference to a non-conditional, non-virtual field of the current
+ structure, the earlier per-field validation loop (which runs in
+ dependency order, so the discriminant field is validated before any
+ field that references it) has already enforced its `.Ok()`. From
+ that point the Maybe<> wrapper around the field read is provably
+ Known, and the extra `Known()` check is dead code.
+
+ The detection is intentionally narrow: only a single-segment field
+ reference (`tag`, not `inner.tag` or `f(x)`) is recognized, since
+ proving the chain is sound for nested or computed paths needs
+ walking the IR and is rarely worth the complexity.
+ """
+ if discrim_expr.which_expression != "field_reference":
+ return False
+ path = discrim_expr.field_reference.path
+ if len(path) != 1:
+ return False
+ name = path[0].canonical_name.object_path[-1]
+ for f in fields:
+ if f.name.name.text != name:
+ continue
+ if ir_util.field_is_virtual(f):
+ return False
+ cond = f.existence_condition
+ return (
+ cond.type.which_type == "boolean"
+ and cond.type.boolean.has_field("value")
+ and bool(cond.type.boolean.value)
+ )
+ return False
+
+
def _get_switch_candidate(expression, ir):
"""Returns (discriminant_expr, case_value_expr) or (None, None)."""
if not _is_equality_check(expression):
@@ -1667,6 +1708,9 @@
group["discrim_rendered"] = _render_expression(
group["discrim_expr"], ir, subexpressions=subexpressions
).rendered
+ group["known_check_required"] = not _is_discriminant_provably_known(
+ group["discrim_expr"], fields
+ )
blocks.append(_emit_switch_block(group))
elif group["type"] == "demoted_to_if":
for field in group["encounter_order"]:
@@ -1757,9 +1801,17 @@
)
)
+ if group.get("known_check_required", True):
+ known_check = (
+ " if (!emboss_reserved_switch_discrim.Known()) return false;\n"
+ )
+ else:
+ known_check = ""
+
return code_template.format_template(
_TEMPLATES.ok_method_switch_block,
discriminant=group["discrim_rendered"],
+ known_check=known_check,
switch_cases="".join(rendered_arms),
)
diff --git a/testdata/golden_cpp/condition.emb.h b/testdata/golden_cpp/condition.emb.h
index f1111d5..725fe82 100644
--- a/testdata/golden_cpp/condition.emb.h
+++ b/testdata/golden_cpp/condition.emb.h
@@ -4247,7 +4247,6 @@
{
const auto emboss_reserved_switch_discrim =
emboss_reserved_local_ok_subexpr_2;
- if (!emboss_reserved_switch_discrim.Known()) return false;
switch (emboss_reserved_switch_discrim.ValueOrDefault()) {
case static_cast</**/ ::std::int32_t>(0LL):
if (!xc().Ok()) return false;
@@ -16699,7 +16698,6 @@
{
const auto emboss_reserved_switch_discrim =
emboss_reserved_local_ok_subexpr_2;
- if (!emboss_reserved_switch_discrim.Known()) return false;
switch (emboss_reserved_switch_discrim.ValueOrDefault()) {
case static_cast</**/ ::std::int32_t>(0LL):
if (!type_0().Ok()) return false;
diff --git a/testdata/golden_cpp/many_conditionals.emb.h b/testdata/golden_cpp/many_conditionals.emb.h
index 8f5fef0..7f7ee7a 100644
--- a/testdata/golden_cpp/many_conditionals.emb.h
+++ b/testdata/golden_cpp/many_conditionals.emb.h
@@ -98,7 +98,6 @@
{
const auto emboss_reserved_switch_discrim =
emboss_reserved_local_ok_subexpr_2;
- if (!emboss_reserved_switch_discrim.Known()) return false;
switch (emboss_reserved_switch_discrim.ValueOrDefault()) {
case static_cast</**/ ::std::int32_t>(0LL):
if (!f0().Ok()) return false;
@@ -10120,7 +10119,6 @@
{
const auto emboss_reserved_switch_discrim =
emboss_reserved_local_ok_subexpr_2;
- if (!emboss_reserved_switch_discrim.Known()) return false;
switch (emboss_reserved_switch_discrim.ValueOrDefault()) {
case static_cast</**/ ::std::int32_t>(0LL):
case static_cast</**/ ::std::int32_t>(1LL):
diff --git a/testdata/golden_cpp/parameters.emb.h b/testdata/golden_cpp/parameters.emb.h
index b601bd7..586bfdc 100644
--- a/testdata/golden_cpp/parameters.emb.h
+++ b/testdata/golden_cpp/parameters.emb.h
@@ -377,7 +377,6 @@
{
const auto emboss_reserved_switch_discrim =
emboss_reserved_local_ok_subexpr_2;
- if (!emboss_reserved_switch_discrim.Known()) return false;
switch (emboss_reserved_switch_discrim.ValueOrDefault()) {
case static_cast</**/ ::emboss::test::MessageId>(0):
if (!axes().Ok()) return false;