about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTrevor Gross <tmgross@umich.edu>2025-02-05 09:47:04 -0600
committerGitHub <noreply@github.com>2025-02-05 09:47:04 -0600
commit466cd81ff5e8950da6fae3b7a76d6768689da0b6 (patch)
tree2dee4b82b080d7161efede63b1803ca3639e668f
parent0e6d93f67db03751bd7fb0e73f09aae1f81f9689 (diff)
parenteee632ee1b75f2fcc6ee679d340169a7386a0604 (diff)
downloadrust-466cd81ff5e8950da6fae3b7a76d6768689da0b6.tar.gz
rust-466cd81ff5e8950da6fae3b7a76d6768689da0b6.zip
Merge pull request rust-lang/libm#489 from tgross35/sort-everything
Add checks via annotation that lists are sorted or exhaustive
-rw-r--r--library/compiler-builtins/libm/crates/libm-macros/src/shared.rs20
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/benches/icount.rs9
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs2
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs2
-rw-r--r--library/compiler-builtins/libm/etc/function-definitions.json57
-rwxr-xr-xlibrary/compiler-builtins/libm/etc/update-api-list.py152
-rw-r--r--library/compiler-builtins/libm/src/libm_helper.rs44
-rw-r--r--library/compiler-builtins/libm/src/math/mod.rs8
8 files changed, 209 insertions, 85 deletions
diff --git a/library/compiler-builtins/libm/crates/libm-macros/src/shared.rs b/library/compiler-builtins/libm/crates/libm-macros/src/shared.rs
index 4fd0834f662..da16cd8e287 100644
--- a/library/compiler-builtins/libm/crates/libm-macros/src/shared.rs
+++ b/library/compiler-builtins/libm/crates/libm-macros/src/shared.rs
@@ -18,7 +18,7 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
         None,
         &[
             "acosf", "acoshf", "asinf", "asinhf", "atanf", "atanhf", "cbrtf", "ceilf", "cosf",
-            "coshf", "erff", "erfcf", "exp10f", "exp2f", "expf", "expm1f", "fabsf", "floorf",
+            "coshf", "erfcf", "erff", "exp10f", "exp2f", "expf", "expm1f", "fabsf", "floorf",
             "j0f", "j1f", "lgammaf", "log10f", "log1pf", "log2f", "logf", "rintf", "roundf",
             "sinf", "sinhf", "sqrtf", "tanf", "tanhf", "tgammaf", "truncf", "y0f", "y1f",
         ],
@@ -30,8 +30,8 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
         None,
         &[
             "acos", "acosh", "asin", "asinh", "atan", "atanh", "cbrt", "ceil", "cos", "cosh",
-            "erf", "erfc", "exp10", "exp2", "exp", "expm1", "fabs", "floor", "j0", "j1", "lgamma",
-            "log10", "log1p", "log2", "log", "rint", "round", "sin", "sinh", "sqrt", "tan", "tanh",
+            "erf", "erfc", "exp", "exp10", "exp2", "expm1", "fabs", "floor", "j0", "j1", "lgamma",
+            "log", "log10", "log1p", "log2", "rint", "round", "sin", "sinh", "sqrt", "tan", "tanh",
             "tgamma", "trunc", "y0", "y1",
         ],
     ),
@@ -139,28 +139,28 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
         FloatTy::F16,
         Signature { args: &[Ty::F16, Ty::I32], returns: &[Ty::F16] },
         None,
-        &["scalbnf16", "ldexpf16"],
+        &["ldexpf16", "scalbnf16"],
     ),
     (
         // `(f32, i32) -> f32`
         FloatTy::F32,
         Signature { args: &[Ty::F32, Ty::I32], returns: &[Ty::F32] },
         None,
-        &["scalbnf", "ldexpf"],
+        &["ldexpf", "scalbnf"],
     ),
     (
         // `(f64, i64) -> f64`
         FloatTy::F64,
         Signature { args: &[Ty::F64, Ty::I32], returns: &[Ty::F64] },
         None,
-        &["scalbn", "ldexp"],
+        &["ldexp", "scalbn"],
     ),
     (
         // `(f128, i32) -> f128`
         FloatTy::F128,
         Signature { args: &[Ty::F128, Ty::I32], returns: &[Ty::F128] },
         None,
-        &["scalbnf128", "ldexpf128"],
+        &["ldexpf128", "scalbnf128"],
     ),
     (
         // `(f32, &mut f32) -> f32` as `(f32) -> (f32, f32)`
@@ -312,6 +312,12 @@ pub static ALL_OPERATIONS: LazyLock<Vec<MathOpInfo>> = LazyLock::new(|| {
             };
             ret.push(api);
         }
+
+        if !names.is_sorted() {
+            let mut sorted = (*names).to_owned();
+            sorted.sort_unstable();
+            panic!("names list is not sorted: {names:?}\nExpected: {sorted:?}");
+        }
     }
 
     ret.sort_by_key(|item| item.name);
diff --git a/library/compiler-builtins/libm/crates/libm-test/benches/icount.rs b/library/compiler-builtins/libm/crates/libm-test/benches/icount.rs
index 13de799c77f..53ecb5a37c4 100644
--- a/library/compiler-builtins/libm/crates/libm-test/benches/icount.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/benches/icount.rs
@@ -52,7 +52,10 @@ libm_macros::for_each_function! {
 }
 
 main!(
-    library_benchmark_groups = icount_bench_acos_group,
+    library_benchmark_groups =
+    // verify-apilist-start
+    // verify-sorted-start
+    icount_bench_acos_group,
     icount_bench_acosf_group,
     icount_bench_acosh_group,
     icount_bench_acoshf_group,
@@ -169,6 +172,8 @@ main!(
     icount_bench_scalbnf16_group,
     icount_bench_scalbnf_group,
     icount_bench_sin_group,
+    icount_bench_sincos_group,
+    icount_bench_sincosf_group,
     icount_bench_sinf_group,
     icount_bench_sinh_group,
     icount_bench_sinhf_group,
@@ -192,4 +197,6 @@ main!(
     icount_bench_y1f_group,
     icount_bench_yn_group,
     icount_bench_ynf_group,
+    // verify-sorted-end
+    // verify-apilist-end
 );
diff --git a/library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs b/library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs
index e3211b91379..ab77d541c81 100644
--- a/library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/src/mpfloat.rs
@@ -132,6 +132,7 @@ libm_macros::for_each_function! {
     emit_types: [RustFn],
     skip: [
         // Most of these need a manual implementation
+        // verify-sorted-start
         ceil,
         ceilf,
         ceilf128,
@@ -188,6 +189,7 @@ libm_macros::for_each_function! {
         truncf128,
         truncf16,yn,
         ynf,
+        // verify-sorted-end
     ],
     fn_extra: match MACRO_FN_NAME {
         // Remap function names that are different between mpfr and libm
diff --git a/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs b/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs
index 191c7e69de3..0b0a9f09721 100644
--- a/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/tests/compare_built_musl.rs
@@ -79,6 +79,7 @@ libm_macros::for_each_function! {
         ynf,
 
         // Not provided by musl
+        // verify-sorted-start
         ceilf128,
         ceilf16,
         copysignf128,
@@ -107,5 +108,6 @@ libm_macros::for_each_function! {
         sqrtf16,
         truncf128,
         truncf16,
+        // verify-sorted-end
     ],
 }
diff --git a/library/compiler-builtins/libm/etc/function-definitions.json b/library/compiler-builtins/libm/etc/function-definitions.json
index e38dfd23663..a1d3adf591f 100644
--- a/library/compiler-builtins/libm/etc/function-definitions.json
+++ b/library/compiler-builtins/libm/etc/function-definitions.json
@@ -2,7 +2,6 @@
     "__comment": "Autogenerated by update-api-list.py. List of files that define a function with a given name. This file is checked in to make it obvious if refactoring breaks things",
     "acos": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/acos.rs"
         ],
         "type": "f64"
@@ -15,7 +14,6 @@
     },
     "acosh": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/acosh.rs"
         ],
         "type": "f64"
@@ -28,7 +26,6 @@
     },
     "asin": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/asin.rs"
         ],
         "type": "f64"
@@ -41,7 +38,6 @@
     },
     "asinh": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/asinh.rs"
         ],
         "type": "f64"
@@ -54,14 +50,12 @@
     },
     "atan": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/atan.rs"
         ],
         "type": "f64"
     },
     "atan2": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/atan2.rs"
         ],
         "type": "f64"
@@ -80,7 +74,6 @@
     },
     "atanh": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/atanh.rs"
         ],
         "type": "f64"
@@ -93,7 +86,6 @@
     },
     "cbrt": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/cbrt.rs"
         ],
         "type": "f64"
@@ -106,7 +98,6 @@
     },
     "ceil": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/arch/i586.rs",
             "src/math/arch/wasm32.rs",
             "src/math/ceil.rs",
@@ -138,7 +129,6 @@
     },
     "copysign": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/copysign.rs",
             "src/math/generic/copysign.rs",
             "src/math/support/float_traits.rs"
@@ -168,7 +158,6 @@
     },
     "cos": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/cos.rs"
         ],
         "type": "f64"
@@ -181,7 +170,6 @@
     },
     "cosh": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/cosh.rs"
         ],
         "type": "f64"
@@ -194,14 +182,12 @@
     },
     "erf": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/erf.rs"
         ],
         "type": "f64"
     },
     "erfc": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/erf.rs"
         ],
         "type": "f64"
@@ -220,7 +206,6 @@
     },
     "exp": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/exp.rs",
             "src/math/support/float_traits.rs"
         ],
@@ -228,7 +213,6 @@
     },
     "exp10": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/exp10.rs"
         ],
         "type": "f64"
@@ -241,7 +225,6 @@
     },
     "exp2": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/exp2.rs"
         ],
         "type": "f64"
@@ -260,7 +243,6 @@
     },
     "expm1": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/expm1.rs"
         ],
         "type": "f64"
@@ -273,7 +255,6 @@
     },
     "fabs": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/arch/wasm32.rs",
             "src/math/fabs.rs",
             "src/math/generic/fabs.rs"
@@ -304,7 +285,6 @@
     },
     "fdim": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/fdim.rs",
             "src/math/generic/fdim.rs"
         ],
@@ -333,7 +313,6 @@
     },
     "floor": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/arch/i586.rs",
             "src/math/arch/wasm32.rs",
             "src/math/floor.rs",
@@ -365,7 +344,6 @@
     },
     "fma": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/fma.rs"
         ],
         "type": "f64"
@@ -378,7 +356,6 @@
     },
     "fmax": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/fmax.rs",
             "src/math/generic/fmax.rs"
         ],
@@ -407,7 +384,6 @@
     },
     "fmin": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/fmin.rs",
             "src/math/generic/fmin.rs"
         ],
@@ -436,7 +412,6 @@
     },
     "fmod": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/fmod.rs",
             "src/math/generic/fmod.rs"
         ],
@@ -465,7 +440,6 @@
     },
     "frexp": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/frexp.rs"
         ],
         "type": "f64"
@@ -478,7 +452,6 @@
     },
     "hypot": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/hypot.rs"
         ],
         "type": "f64"
@@ -491,7 +464,6 @@
     },
     "ilogb": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/ilogb.rs"
         ],
         "type": "f64"
@@ -504,7 +476,6 @@
     },
     "j0": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/j0.rs"
         ],
         "type": "f64"
@@ -517,7 +488,6 @@
     },
     "j1": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/j1.rs"
         ],
         "type": "f64"
@@ -530,7 +500,6 @@
     },
     "jn": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/jn.rs"
         ],
         "type": "f64"
@@ -543,7 +512,6 @@
     },
     "ldexp": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/ldexp.rs"
         ],
         "type": "f64"
@@ -568,14 +536,12 @@
     },
     "lgamma": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/lgamma.rs"
         ],
         "type": "f64"
     },
     "lgamma_r": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/lgamma_r.rs"
         ],
         "type": "f64"
@@ -594,14 +560,12 @@
     },
     "log": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/log.rs"
         ],
         "type": "f64"
     },
     "log10": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/log10.rs"
         ],
         "type": "f64"
@@ -614,7 +578,6 @@
     },
     "log1p": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/log1p.rs"
         ],
         "type": "f64"
@@ -627,7 +590,6 @@
     },
     "log2": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/log2.rs"
         ],
         "type": "f64"
@@ -646,7 +608,6 @@
     },
     "modf": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/modf.rs"
         ],
         "type": "f64"
@@ -659,7 +620,6 @@
     },
     "nextafter": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/nextafter.rs"
         ],
         "type": "f64"
@@ -672,7 +632,6 @@
     },
     "pow": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/pow.rs"
         ],
         "type": "f64"
@@ -685,7 +644,6 @@
     },
     "remainder": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/remainder.rs"
         ],
         "type": "f64"
@@ -698,7 +656,6 @@
     },
     "remquo": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/remquo.rs"
         ],
         "type": "f64"
@@ -711,7 +668,6 @@
     },
     "rint": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/arch/aarch64.rs",
             "src/math/arch/wasm32.rs",
             "src/math/generic/rint.rs",
@@ -744,7 +700,6 @@
     },
     "round": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/generic/round.rs",
             "src/math/round.rs"
         ],
@@ -773,7 +728,6 @@
     },
     "scalbn": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/generic/scalbn.rs",
             "src/math/scalbn.rs"
         ],
@@ -802,14 +756,12 @@
     },
     "sin": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/sin.rs"
         ],
         "type": "f64"
     },
     "sincos": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/sincos.rs"
         ],
         "type": "f64"
@@ -828,7 +780,6 @@
     },
     "sinh": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/sinh.rs"
         ],
         "type": "f64"
@@ -841,7 +792,6 @@
     },
     "sqrt": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/arch/i686.rs",
             "src/math/arch/wasm32.rs",
             "src/math/generic/sqrt.rs",
@@ -874,7 +824,6 @@
     },
     "tan": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/tan.rs"
         ],
         "type": "f64"
@@ -887,7 +836,6 @@
     },
     "tanh": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/tanh.rs"
         ],
         "type": "f64"
@@ -900,7 +848,6 @@
     },
     "tgamma": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/tgamma.rs"
         ],
         "type": "f64"
@@ -913,7 +860,6 @@
     },
     "trunc": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/arch/wasm32.rs",
             "src/math/generic/trunc.rs",
             "src/math/trunc.rs"
@@ -944,7 +890,6 @@
     },
     "y0": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/j0.rs"
         ],
         "type": "f64"
@@ -957,7 +902,6 @@
     },
     "y1": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/j1.rs"
         ],
         "type": "f64"
@@ -970,7 +914,6 @@
     },
     "yn": {
         "sources": [
-            "src/libm_helper.rs",
             "src/math/jn.rs"
         ],
         "type": "f64"
diff --git a/library/compiler-builtins/libm/etc/update-api-list.py b/library/compiler-builtins/libm/etc/update-api-list.py
index 67f73e59cf1..9cf62555422 100755
--- a/library/compiler-builtins/libm/etc/update-api-list.py
+++ b/library/compiler-builtins/libm/etc/update-api-list.py
@@ -1,24 +1,40 @@
 #!/usr/bin/env python3
 """Create a text file listing all public API. This can be used to ensure that all
 functions are covered by our macros.
+
+This file additionally does tidy-esque checks that all functions are listed where
+needed, or that lists are sorted.
 """
 
 import difflib
 import json
+import re
 import subprocess as sp
 import sys
 from dataclasses import dataclass
-from glob import glob
+from glob import glob, iglob
 from pathlib import Path
-from typing import Any, TypeAlias
+from typing import Any, Callable, TypeAlias
 
-ETC_DIR = Path(__file__).parent
+SELF_PATH = Path(__file__)
+ETC_DIR = SELF_PATH.parent
 ROOT_DIR = ETC_DIR.parent
 
+# Loose approximation of what gets checked in to git, without needing `git ls-files`.
+DIRECTORIES = [".github", "ci", "crates", "etc", "src"]
+
+# These files do not trigger a retest.
+IGNORED_SOURCES = ["src/libm_helper.rs"]
+
 IndexTy: TypeAlias = dict[str, dict[str, Any]]
 """Type of the `index` item in rustdoc's JSON output"""
 
 
+def eprint(*args, **kwargs):
+    """Print to stderr."""
+    print(*args, file=sys.stderr, **kwargs)
+
+
 @dataclass
 class Crate:
     """Representation of public interfaces and function defintion locations in
@@ -120,6 +136,9 @@ class Crate:
             for src in (s for s in base_sources if "generic" in s):
                 sources.add(src)
 
+            for src in IGNORED_SOURCES:
+                sources.discard(src)
+
         # Sort the set
         self.defs = {k: sorted(v) for (k, v) in defs.items()}
 
@@ -137,7 +156,7 @@ class Crate:
         if check:
             with open(out_file, "r") as f:
                 current = f.read()
-            diff_and_exit(current, output)
+            diff_and_exit(current, output, "function list")
         else:
             with open(out_file, "w") as f:
                 f.write(output)
@@ -162,18 +181,115 @@ class Crate:
         if check:
             with open(out_file, "r") as f:
                 current = f.read()
-            diff_and_exit(current, output)
+            diff_and_exit(current, output, "source list")
         else:
             with open(out_file, "w") as f:
                 f.write(output)
 
+    def tidy_lists(self) -> None:
+        """In each file, check annotations indicating blocks of code should be sorted or should
+        include all public API.
+        """
+        for dirname in DIRECTORIES:
+            dir = ROOT_DIR.joinpath(dirname)
+            for fname in iglob("**", root_dir=dir, recursive=True):
+                fpath = dir.joinpath(fname)
+                if fpath.is_dir() or fpath == SELF_PATH:
+                    continue
+
+                lines = fpath.read_text().splitlines()
+
+                validate_delimited_block(
+                    fpath,
+                    lines,
+                    "verify-sorted-start",
+                    "verify-sorted-end",
+                    ensure_sorted,
+                )
+
+                validate_delimited_block(
+                    fpath,
+                    lines,
+                    "verify-apilist-start",
+                    "verify-apilist-end",
+                    lambda p, n, lines: self.ensure_contains_api(p, n, lines),
+                )
+
+    def ensure_contains_api(self, fpath: Path, line_num: int, lines: list[str]):
+        """Given a list of strings, ensure that each public function we have is named
+        somewhere.
+        """
+        not_found = []
+        for func in self.public_functions:
+            # The function name may be on its own or somewhere in a snake case string.
+            pat = re.compile(rf"(\b|_){func}(\b|_)")
+            found = next((line for line in lines if pat.search(line)), None)
+
+            if found is None:
+                not_found.append(func)
+
+        if len(not_found) == 0:
+            return
+
+        relpath = fpath.relative_to(ROOT_DIR)
+        eprint(f"functions not found at {relpath}:{line_num}: {not_found}")
+        exit(1)
+
+
+def validate_delimited_block(
+    fpath: Path,
+    lines: list[str],
+    start: str,
+    end: str,
+    validate: Callable[[Path, int, list[str]], None],
+) -> None:
+    """Identify blocks of code wrapped within `start` and `end`, collect their contents
+    to a list of strings, and call `validate` for each of those lists.
+    """
+    relpath = fpath.relative_to(ROOT_DIR)
+    block_lines = []
+    block_start_line: None | int = None
+    for line_num, line in enumerate(lines):
+        line_num += 1
+
+        if start in line:
+            block_start_line = line_num
+            continue
+
+        if end in line:
+            if block_start_line is None:
+                eprint(f"`{end}` without `{start}` at {relpath}:{line_num}")
+                exit(1)
+
+            validate(fpath, block_start_line, block_lines)
+            block_lines = []
+            block_start_line = None
+            continue
+
+        if block_start_line is not None:
+            block_lines.append(line)
+
+    if block_start_line is not None:
+        eprint(f"`{start}` without `{end}` at {relpath}:{block_start_line}")
+        exit(1)
 
-def diff_and_exit(actual: str, expected: str):
+
+def ensure_sorted(fpath: Path, block_start_line: int, lines: list[str]) -> None:
+    """Ensure that a list of lines is sorted, otherwise print a diff and exit."""
+    relpath = fpath.relative_to(ROOT_DIR)
+    diff_and_exit(
+        "".join(lines),
+        "".join(sorted(lines)),
+        f"sorted block at {relpath}:{block_start_line}",
+    )
+
+
+def diff_and_exit(actual: str, expected: str, name: str):
     """If the two strings are different, print a diff between them and then exit
     with an error.
     """
     if actual == expected:
-        print("output matches expected; success")
+        print(f"{name} output matches expected; success")
         return
 
     a = [f"{line}\n" for line in actual.splitlines()]
@@ -181,7 +297,7 @@ def diff_and_exit(actual: str, expected: str):
 
     diff = difflib.unified_diff(a, b, "actual", "expected")
     sys.stdout.writelines(diff)
-    print("mismatched function list")
+    print(f"mismatched {name}")
     exit(1)
 
 
@@ -214,23 +330,31 @@ def base_name(name: str) -> tuple[str, str]:
     return (name, "f64")
 
 
+def ensure_updated_list(check: bool) -> None:
+    """Runner to update the function list and JSON, or check that it is already up
+    to date.
+    """
+    crate = Crate()
+    crate.write_function_list(check)
+    crate.write_function_defs(check)
+
+    if check:
+        crate.tidy_lists()
+
+
 def main():
     """By default overwrite the file. If `--check` is passed, print a diff instead and
     error if the files are different.
     """
     match sys.argv:
         case [_]:
-            check = False
+            ensure_updated_list(False)
         case [_, "--check"]:
-            check = True
+            ensure_updated_list(True)
         case _:
             print("unrecognized arguments")
             exit(1)
 
-    crate = Crate()
-    crate.write_function_list(check)
-    crate.write_function_defs(check)
-
 
 if __name__ == "__main__":
     main()
diff --git a/library/compiler-builtins/libm/src/libm_helper.rs b/library/compiler-builtins/libm/src/libm_helper.rs
index 73bae456761..0768839c779 100644
--- a/library/compiler-builtins/libm/src/libm_helper.rs
+++ b/library/compiler-builtins/libm/src/libm_helper.rs
@@ -44,9 +44,11 @@ macro_rules! libm_helper {
     };
 }
 
+// verify-apilist-start
 libm_helper! {
     f32,
     funcs: {
+        // verify-sorted-start
         (fn acos(x: f32) -> (f32);                  => acosf);
         (fn acosh(x: f32) -> (f32);                 => acoshf);
         (fn asin(x: f32) -> (f32);                  => asinf);
@@ -62,8 +64,8 @@ libm_helper! {
         (fn erf(x: f32) -> (f32);                   => erff);
         (fn erfc(x: f32) -> (f32);                  => erfcf);
         (fn exp(x: f32) -> (f32);                   => expf);
-        (fn exp2(x: f32) -> (f32);                  => exp2f);
         (fn exp10(x: f32) -> (f32);                 => exp10f);
+        (fn exp2(x: f32) -> (f32);                  => exp2f);
         (fn expm1(x: f32) -> (f32);                 => expm1f);
         (fn fabs(x: f32) -> (f32);                  => fabsf);
         (fn fdim(x: f32, y: f32) -> (f32);          => fdimf);
@@ -79,12 +81,12 @@ libm_helper! {
         (fn j1(x: f32) -> (f32);                    => j1f);
         (fn jn(n: i32, x: f32) -> (f32);            => jnf);
         (fn ldexp(x: f32, n: i32) -> (f32);         => ldexpf);
-        (fn lgamma_r(x: f32) -> (f32, i32);         => lgammaf_r);
         (fn lgamma(x: f32) -> (f32);                => lgammaf);
+        (fn lgamma_r(x: f32) -> (f32, i32);         => lgammaf_r);
         (fn log(x: f32) -> (f32);                   => logf);
+        (fn log10(x: f32) -> (f32);                 => log10f);
         (fn log1p(x: f32) -> (f32);                 => log1pf);
         (fn log2(x: f32) -> (f32);                  => log2f);
-        (fn log10(x: f32) -> (f32);                 => log10f);
         (fn modf(x: f32) -> (f32, f32);             => modff);
         (fn nextafter(x: f32, y: f32) -> (f32);     => nextafterf);
         (fn pow(x: f32, y: f32) -> (f32);           => powf);
@@ -104,12 +106,14 @@ libm_helper! {
         (fn y0(x: f32) -> (f32);                    => y0f);
         (fn y1(x: f32) -> (f32);                    => y1f);
         (fn yn(n: i32, x: f32) -> (f32);            => ynf);
+        // verify-sorted-end
     }
 }
 
 libm_helper! {
     f64,
     funcs: {
+        // verify-sorted-start
         (fn acos(x: f64) -> (f64);                  => acos);
         (fn acosh(x: f64) -> (f64);                 => acosh);
         (fn asin(x: f64) -> (f64);                  => asin);
@@ -125,8 +129,8 @@ libm_helper! {
         (fn erf(x: f64) -> (f64);                   => erf);
         (fn erfc(x: f64) -> (f64);                  => erfc);
         (fn exp(x: f64) -> (f64);                   => exp);
-        (fn exp2(x: f64) -> (f64);                  => exp2);
         (fn exp10(x: f64) -> (f64);                 => exp10);
+        (fn exp2(x: f64) -> (f64);                  => exp2);
         (fn expm1(x: f64) -> (f64);                 => expm1);
         (fn fabs(x: f64) -> (f64);                  => fabs);
         (fn fdim(x: f64, y: f64) -> (f64);          => fdim);
@@ -142,12 +146,12 @@ libm_helper! {
         (fn j1(x: f64) -> (f64);                    => j1);
         (fn jn(n: i32, x: f64) -> (f64);            => jn);
         (fn ldexp(x: f64, n: i32) -> (f64);         => ldexp);
-        (fn lgamma_r(x: f64) -> (f64, i32);         => lgamma_r);
         (fn lgamma(x: f64) -> (f64);                => lgamma);
+        (fn lgamma_r(x: f64) -> (f64, i32);         => lgamma_r);
         (fn log(x: f64) -> (f64);                   => log);
+        (fn log10(x: f64) -> (f64);                 => log10);
         (fn log1p(x: f64) -> (f64);                 => log1p);
         (fn log2(x: f64) -> (f64);                  => log2);
-        (fn log10(x: f64) -> (f64);                 => log10);
         (fn modf(x: f64) -> (f64, f64);             => modf);
         (fn nextafter(x: f64, y: f64) -> (f64);     => nextafter);
         (fn pow(x: f64, y: f64) -> (f64);           => pow);
@@ -167,6 +171,7 @@ libm_helper! {
         (fn y0(x: f64) -> (f64);                    => y0);
         (fn y1(x: f64) -> (f64);                    => y1);
         (fn yn(n: i32, x: f64) -> (f64);            => yn);
+        // verify-sorted-end
     }
 }
 
@@ -174,9 +179,22 @@ libm_helper! {
 libm_helper! {
     f16,
     funcs: {
+        // verify-sorted-start
+        (fn ceilf(x: f16) -> (f16);                 => ceilf16);
         (fn copysign(x: f16, y: f16) -> (f16);      => copysignf16);
         (fn fabs(x: f16) -> (f16);                  => fabsf16);
         (fn fdim(x: f16, y: f16) -> (f16);          => fdimf16);
+        (fn floorf(x: f16) -> (f16);                => floorf16);
+        (fn fmaxf(x: f16, y: f16) -> (f16);         => fmaxf16);
+        (fn fminf(x: f16, y: f16) -> (f16);         => fminf16);
+        (fn fmodf(x: f16, y: f16) -> (f16);         => fmodf16);
+        (fn ldexpf16(x: f16, n: i32) -> (f16);      => ldexpf16);
+        (fn rintf(x: f16) -> (f16);                 => rintf16);
+        (fn roundf(x: f16) -> (f16);                => roundf16);
+        (fn scalbnf16(x: f16, n: i32) -> (f16);     => ldexpf16);
+        (fn sqrtf(x: f16) -> (f16);                 => sqrtf16);
+        (fn truncf(x: f16) -> (f16);                => truncf16);
+        // verify-sorted-end
     }
 }
 
@@ -184,8 +202,22 @@ libm_helper! {
 libm_helper! {
     f128,
     funcs: {
+        // verify-sorted-start
+        (fn ceil(x: f128) -> (f128);                => ceilf128);
         (fn copysign(x: f128, y: f128) -> (f128);   => copysignf128);
         (fn fabs(x: f128) -> (f128);                => fabsf128);
         (fn fdim(x: f128, y: f128) -> (f128);       => fdimf128);
+        (fn floor(x: f128) -> (f128);               => floorf128);
+        (fn fmax(x: f128, y: f128) -> (f128);       => fmaxf128);
+        (fn fmin(x: f128, y: f128) -> (f128);       => fminf128);
+        (fn fmod(x: f128, y: f128) -> (f128);       => fmodf128);
+        (fn ldexpf128(x: f128, n: i32) -> (f128);   => ldexpf128);
+        (fn rint(x: f128) -> (f128);                => rintf128);
+        (fn round(x: f128) -> (f128);               => roundf128);
+        (fn scalbnf128(x: f128, n: i32) -> (f128);  => ldexpf128);
+        (fn sqrt(x: f128) -> (f128);                => sqrtf128);
+        (fn trunc(x: f128) -> (f128);               => truncf128);
+        // verify-sorted-end
     }
 }
+// verify-apilist-end
diff --git a/library/compiler-builtins/libm/src/math/mod.rs b/library/compiler-builtins/libm/src/math/mod.rs
index 9b07dc8a75e..f0698ad02eb 100644
--- a/library/compiler-builtins/libm/src/math/mod.rs
+++ b/library/compiler-builtins/libm/src/math/mod.rs
@@ -341,6 +341,7 @@ pub use self::truncf::truncf;
 
 cfg_if! {
     if #[cfg(f16_enabled)] {
+        // verify-sorted-start
         mod ceilf16;
         mod copysignf16;
         mod fabsf16;
@@ -355,7 +356,9 @@ cfg_if! {
         mod scalbnf16;
         mod sqrtf16;
         mod truncf16;
+        // verify-sorted-end
 
+        // verify-sorted-start
         pub use self::ceilf16::ceilf16;
         pub use self::copysignf16::copysignf16;
         pub use self::fabsf16::fabsf16;
@@ -370,11 +373,13 @@ cfg_if! {
         pub use self::scalbnf16::scalbnf16;
         pub use self::sqrtf16::sqrtf16;
         pub use self::truncf16::truncf16;
+        // verify-sorted-end
     }
 }
 
 cfg_if! {
     if #[cfg(f128_enabled)] {
+        // verify-sorted-start
         mod ceilf128;
         mod copysignf128;
         mod fabsf128;
@@ -389,7 +394,9 @@ cfg_if! {
         mod scalbnf128;
         mod sqrtf128;
         mod truncf128;
+        // verify-sorted-end
 
+        // verify-sorted-start
         pub use self::ceilf128::ceilf128;
         pub use self::copysignf128::copysignf128;
         pub use self::fabsf128::fabsf128;
@@ -404,6 +411,7 @@ cfg_if! {
         pub use self::scalbnf128::scalbnf128;
         pub use self::sqrtf128::sqrtf128;
         pub use self::truncf128::truncf128;
+        // verify-sorted-end
     }
 }