diff --git a/depend b/depend index 8b764bd060c49c..c3252dd87e821b 100644 --- a/depend +++ b/depend @@ -11694,6 +11694,7 @@ prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/arena.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/ast.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/buffer.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/compiler/align.h +prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/compiler/assume.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/compiler/exported.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/compiler/flex_array.h prism/diagnostic.$(OBJEXT): $(top_srcdir)/prism/compiler/force_inline.h diff --git a/lib/prism/prism.gemspec b/lib/prism/prism.gemspec index aac056b3f889e8..c18d68f4d8a5f9 100644 --- a/lib/prism/prism.gemspec +++ b/lib/prism/prism.gemspec @@ -48,6 +48,7 @@ Gem::Specification.new do |spec| "include/prism.h", "include/prism/compiler/accel.h", "include/prism/compiler/align.h", + "include/prism/compiler/assume.h", "include/prism/compiler/exported.h", "include/prism/compiler/fallthrough.h", "include/prism/compiler/filesystem.h", diff --git a/prism/compiler/assume.h b/prism/compiler/assume.h new file mode 100644 index 00000000000000..500e93e73cdc4d --- /dev/null +++ b/prism/compiler/assume.h @@ -0,0 +1,25 @@ +/** + * @file compiler/assume.h + */ +#ifndef PRISM_COMPILER_ASSUME_H +#define PRISM_COMPILER_ASSUME_H + +/** + * Tell the compiler that the given expression can be assumed to be true. Unlike + * assert, this emits no runtime check — it only feeds the optimizer's value + * range analysis so it can prune impossible paths. Use it to communicate an + * invariant the caller guarantees but that the compiler cannot otherwise prove. + */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L /* C23 or later */ + #define PRISM_ASSUME(expr_) [[assume(expr_)]] +#elif defined(__clang__) + #define PRISM_ASSUME(expr_) __builtin_assume(expr_) +#elif defined(_MSC_VER) + #define PRISM_ASSUME(expr_) __assume(expr_) +#elif defined(__GNUC__) + #define PRISM_ASSUME(expr_) ((expr_) ? (void) 0 : __builtin_unreachable()) +#else + #define PRISM_ASSUME(expr_) ((void) 0) +#endif + +#endif diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index 0dea7328694cf3..0683978084da04 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -1,5 +1,6 @@ #include "prism/internal/diagnostic.h" +#include "prism/compiler/assume.h" #include "prism/compiler/inline.h" #include "prism/internal/allocator.h" @@ -446,6 +447,7 @@ pm_diagnostic_id_name(pm_diagnostic_id_t diag_id) { static PRISM_INLINE const char * pm_diagnostic_id_message(pm_diagnostic_id_t diag_id) { assert(diag_id < PM_DIAGNOSTIC_ID_MAX); + PRISM_ASSUME(diag_id < PM_DIAGNOSTIC_ID_MAX); const char *message = diagnostic_messages[diag_id].message; assert(message); @@ -456,6 +458,7 @@ pm_diagnostic_id_message(pm_diagnostic_id_t diag_id) { static PRISM_INLINE uint8_t pm_diagnostic_id_level(pm_diagnostic_id_t diag_id) { assert(diag_id < PM_DIAGNOSTIC_ID_MAX); + PRISM_ASSUME(diag_id < PM_DIAGNOSTIC_ID_MAX); return (uint8_t) diagnostic_messages[diag_id].level; }