https://github.com/localspook created 
https://github.com/llvm/llvm-project/pull/165181

None

>From f6574b6b59b91ce299fa6977b2b326aae0bf235d Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <[email protected]>
Date: Sun, 26 Oct 2025 16:57:54 -0700
Subject: [PATCH] [UBSan] Add check that detects division by zero for
 fixed-point types

---
 clang/docs/ReleaseNotes.rst                      |  2 +-
 clang/docs/UndefinedBehaviorSanitizer.rst        |  4 ++++
 clang/include/clang/Basic/Features.def           |  4 ++++
 clang/include/clang/Basic/Sanitizers.def         |  4 +++-
 clang/lib/CodeGen/CGExpr.cpp                     |  8 ++++++--
 clang/lib/CodeGen/CGExprScalar.cpp               | 13 ++++++++++++-
 .../has_feature_undefined_behavior_sanitizer.cpp | 16 ++++++++++++++++
 compiler-rt/lib/ubsan/ubsan_checks.inc           |  2 ++
 compiler-rt/lib/ubsan/ubsan_handlers.cpp         |  2 ++
 compiler-rt/lib/ubsan/ubsan_value.h              |  4 ++++
 .../TestCases/FixedPoint/divide-by-zero.cpp      | 10 ++++++++++
 11 files changed, 64 insertions(+), 5 deletions(-)
 create mode 100644 
compiler-rt/test/ubsan/TestCases/FixedPoint/divide-by-zero.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index edb872c1f388d..b4a5f7dcbfa20 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -274,7 +274,7 @@ New Compiler Flags
 - New option ``-fno-sanitize-debug-trap-reasons`` added to disable emitting 
trap reasons into the debug info when compiling with trapping UBSan (e.g. 
``-fsanitize-trap=undefined``).
 - New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap 
reasons into the debug info when compiling with trapping UBSan (e.g. 
``-fsanitize-trap=undefined``).
 - New options for enabling allocation token instrumentation: 
``-fsanitize=alloc-token``, ``-falloc-token-max=``, 
``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``.
-
+- New option for diagnosing division by zero with fixed point numbers 
(``-ffixed-point``): ``-fsanitize=fixed-point-divide-by-zero``, and a new group 
that includes it: ``-fsanitize=fixed-point``.
 
 Lanai Support
 ^^^^^^^^^^^^^^
diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst 
b/clang/docs/UndefinedBehaviorSanitizer.rst
index 0a2d833783e57..568d3f092de3e 100644
--- a/clang/docs/UndefinedBehaviorSanitizer.rst
+++ b/clang/docs/UndefinedBehaviorSanitizer.rst
@@ -119,6 +119,8 @@ Available checks are:
   -  ``-fsanitize=enum``: Load of a value of an enumerated type which
      is not in the range of representable values for that enumerated
      type.
+  -  ``-fsanitize=fixed-point-divide-by-zero``: Fixed point division by zero
+     (when compiling with ``-ffixed-point``).
   -  ``-fsanitize=float-cast-overflow``: Conversion to, from, or
      between floating-point types which would overflow the
      destination. Because the range of representable values for all
@@ -224,6 +226,8 @@ You can also use the following check groups:
      ``nullability-*`` group of checks.
   -  ``-fsanitize=undefined-trap``: Deprecated alias of
      ``-fsanitize=undefined``.
+  -  ``-fsanitize=fixed-point``: Checks for undefined behavior with fixed point
+     values. Enables ``-fsanitize=fixed-point-divide-by-zero``.
   -  ``-fsanitize=implicit-integer-truncation``: Catches lossy integral
      conversions. Enables ``implicit-signed-integer-truncation`` and
      ``implicit-unsigned-integer-truncation``.
diff --git a/clang/include/clang/Basic/Features.def 
b/clang/include/clang/Basic/Features.def
index 0e91b42a132c1..204150627fd8f 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -66,6 +66,10 @@ FEATURE(array_bounds_sanitizer,
         LangOpts.Sanitize.has(SanitizerKind::ArrayBounds))
 FEATURE(enum_sanitizer,
         LangOpts.Sanitize.has(SanitizerKind::Enum))
+FEATURE(fixed_point_divide_by_zero_sanitizer,
+        LangOpts.Sanitize.has(SanitizerKind::FixedPointDivideByZero))
+FEATURE(fixed_point_sanitizer,
+        LangOpts.Sanitize.hasOneOf(SanitizerKind::FixedPoint))
 FEATURE(float_cast_overflow_sanitizer,
         LangOpts.Sanitize.has(SanitizerKind::FloatCastOverflow))
 FEATURE(integer_divide_by_zero_sanitizer,
diff --git a/clang/include/clang/Basic/Sanitizers.def 
b/clang/include/clang/Basic/Sanitizers.def
index da85431625026..b77a838d71da1 100644
--- a/clang/include/clang/Basic/Sanitizers.def
+++ b/clang/include/clang/Basic/Sanitizers.def
@@ -94,6 +94,8 @@ SANITIZER("array-bounds", ArrayBounds)
 SANITIZER("bool", Bool)
 SANITIZER("builtin", Builtin)
 SANITIZER("enum", Enum)
+SANITIZER("fixed-point-divide-by-zero", FixedPointDivideByZero)
+SANITIZER_GROUP("fixed-point", FixedPoint, FixedPointDivideByZero)
 SANITIZER("float-cast-overflow", FloatCastOverflow)
 SANITIZER("float-divide-by-zero", FloatDivideByZero)
 SANITIZER("function", Function)
@@ -149,7 +151,7 @@ SANITIZER("shadow-call-stack", ShadowCallStack)
 // ABI or address space layout implications, and only catch undefined behavior.
 SANITIZER_GROUP("undefined", Undefined,
                 Alignment | Bool | Builtin | ArrayBounds | Enum |
-                    FloatCastOverflow |
+                    FixedPoint | FloatCastOverflow |
                     IntegerDivideByZero | NonnullAttribute | Null | ObjectSize 
|
                     PointerOverflow | Return | ReturnsNonnullAttribute | Shift 
|
                     SignedIntegerOverflow | Unreachable | VLABound | Function)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index e8255b0554da8..02876e72f83cb 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -79,6 +79,8 @@ enum VariableTypeDescriptorKind : uint16_t {
   TK_Float = 0x0001,
   /// An _BitInt(N) type.
   TK_BitInt = 0x0002,
+  /// A fixed-point type.
+  TK_FixedPoint = 0x0003,
   /// Any other type. The value representation is unspecified.
   TK_Unknown = 0xffff
 };
@@ -3724,8 +3726,8 @@ LValue CodeGenFunction::EmitPredefinedLValue(const 
PredefinedExpr *E) {
 ///
 /// followed by an array of i8 containing the type name with extra information
 /// for BitInt. TypeKind is TK_Integer(0) for an integer, TK_Float(1) for a
-/// floating point value, TK_BitInt(2) for BitInt and TK_Unknown(0xFFFF) for
-/// anything else.
+/// floating point value, TK_BitInt(2) for BitInt, TK_FixedPoint(3) for a
+// fixed point value, and TK_Unknown(0xFFFF) for anything else.
 llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) {
   // Only emit each type's descriptor once.
   if (llvm::Constant *C = CGM.getTypeDescriptorFromMap(T))
@@ -3757,6 +3759,8 @@ llvm::Constant 
*CodeGenFunction::EmitCheckTypeDescriptor(QualType T) {
   } else if (T->isFloatingType()) {
     TypeKind = TK_Float;
     TypeInfo = getContext().getTypeSize(T);
+  } else if (T->isFixedPointType()) {
+    TypeKind = TK_FixedPoint;
   }
 
   // Format the type name as if for a diagnostic, including quotes and
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index 715160d067817..65474a3f14d13 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4591,9 +4591,20 @@ Value *ScalarExprEmitter::EmitFixedPointBinOp(const 
BinOpInfo &op) {
     Result = FPBuilder.CreateMul(LHS, LHSFixedSema, RHS, RHSFixedSema);
     break;
   case BO_DivAssign:
-  case BO_Div:
+  case BO_Div: {
+    SanitizerDebugLocation SanScope(&CGF,
+                                    {SanitizerKind::SO_FixedPointDivideByZero},
+                                    SanitizerHandler::DivremOverflow);
+    if (CGF.SanOpts.has(SanitizerKind::FixedPointDivideByZero)) {
+      Value *Zero = llvm::Constant::getNullValue(RHS->getType());
+      const std::pair<Value *, SanitizerKind::SanitizerOrdinal> Check = {
+          Builder.CreateICmpNE(RHS, Zero),
+          SanitizerKind::SO_FixedPointDivideByZero};
+      EmitBinOpCheck(Check, op);
+    }
     Result = FPBuilder.CreateDiv(LHS, LHSFixedSema, RHS, RHSFixedSema);
     break;
+  }
   case BO_ShlAssign:
   case BO_Shl:
     Result = FPBuilder.CreateShl(LHS, LHSFixedSema, RHS);
diff --git a/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp 
b/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp
index e1a07d215b549..e1f9827bfed0f 100644
--- a/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp
+++ b/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp
@@ -4,6 +4,8 @@
 // RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=builtin %s -o - 
| FileCheck --check-prefixes=CHECK-UBSAN,CHECK-BUILTIN %s
 // RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=array-bounds %s 
-o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-ARRAY-BOUNDS %s
 // RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=enum %s -o - | 
FileCheck --check-prefixes=CHECK-UBSAN,CHECK-ENUM %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu 
-fsanitize=fixed-point-divide-by-zero %s -o - | FileCheck 
--check-prefixes=CHECK-UBSAN,CHECK-FIXED-POINT-DIVIDE-BY-ZERO,CHECK-FIXED-POINT 
%s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=fixed-point %s 
-o - | FileCheck --check-prefixes=CHECK-UBSAN,CHECK-FIXED-POINT %s
 // RUN: %clang -E -target x86_64-unknown-linux-gnu 
-fsanitize=float-cast-overflow %s -o - | FileCheck 
--check-prefixes=CHECK-UBSAN,CHECK-FLOAT-CAST-OVERFLOW %s
 // RUN: %clang -E -target x86_64-unknown-linux-gnu 
-fsanitize=integer-divide-by-zero %s -o - | FileCheck 
--check-prefixes=CHECK-UBSAN,CHECK-INTEGER-DIVIDE-BY-ZERO %s
 // RUN: %clang -E -target x86_64-unknown-linux-gnu 
-fsanitize=nonnull-attribute %s -o - | FileCheck 
--check-prefixes=CHECK-UBSAN,CHECK-NONNULL-ATTRIBUTE %s
@@ -65,6 +67,18 @@ int EnumSanitizerEnabled();
 int EnumSanitizerDisabled();
 #endif
 
+#if __has_feature(fixed_point_divide_by_zero_sanitizer)
+int FixedPointDivideByZeroSanitizerEnabled();
+#else
+int FixedPointDivideByZeroSanitizerDisabled();
+#endif
+
+#if __has_feature(fixed_point_sanitizer)
+int FixedPointSanitizerEnabled();
+#else
+int FixedPointSanitizerDisabled();
+#endif
+
 #if __has_feature(float_cast_overflow_sanitizer)
 int FloatCastOverflowSanitizerEnabled();
 #else
@@ -161,6 +175,8 @@ int FunctionSanitizerDisabled();
 // CHECK-BUILTIN: BuiltinSanitizerEnabled
 // CHECK-ARRAY-BOUNDS: ArrayBoundsSanitizerEnabled
 // CHECK-ENUM: EnumSanitizerEnabled
+// CHECK-FIXED-POINT-DIVIDE-BY-ZERO: FixedPointDivideByZeroSanitizerEnabled
+// CHECK-FIXED-POINT: FixedPointSanitizerEnabled
 // CHECK-FLOAT-CAST-OVERFLOW: FloatCastOverflowSanitizerEnabled
 // CHECK-INTEGER-DIVIDE-BY-ZERO: IntegerDivideByZeroSanitizerEnabled
 // CHECK-NONNULL-ATTRIBUTE: NonnullAttributeSanitizerEnabled
diff --git a/compiler-rt/lib/ubsan/ubsan_checks.inc 
b/compiler-rt/lib/ubsan/ubsan_checks.inc
index b1d09a9024e7e..9fe0291cab9bc 100644
--- a/compiler-rt/lib/ubsan/ubsan_checks.inc
+++ b/compiler-rt/lib/ubsan/ubsan_checks.inc
@@ -36,6 +36,8 @@ UBSAN_CHECK(UnsignedIntegerOverflow, 
"unsigned-integer-overflow",
 UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero",
             "integer-divide-by-zero")
 UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero", "float-divide-by-zero")
+UBSAN_CHECK(FixedPointDivideByZero, "fixed-point-divide-by-zero",
+            "fixed-point-divide-by-zero")
 UBSAN_CHECK(InvalidBuiltin, "invalid-builtin-use", "invalid-builtin-use")
 UBSAN_CHECK(InvalidObjCCast, "invalid-objc-cast", "invalid-objc-cast")
 UBSAN_CHECK(ImplicitUnsignedIntegerTruncation,
diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp 
b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
index 63319f46734a4..c266298c86295 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -294,6 +294,8 @@ static void handleDivremOverflowImpl(OverflowData *Data, 
ValueHandle LHS,
     ET = ErrorType::SignedIntegerOverflow;
   else if (Data->Type.isIntegerTy())
     ET = ErrorType::IntegerDivideByZero;
+  else if (Data->Type.isFixedPointTy())
+    ET = ErrorType::FixedPointDivideByZero;
   else
     ET = ErrorType::FloatDivideByZero;
 
diff --git a/compiler-rt/lib/ubsan/ubsan_value.h 
b/compiler-rt/lib/ubsan/ubsan_value.h
index ee523cf5ddda5..812cde9558076 100644
--- a/compiler-rt/lib/ubsan/ubsan_value.h
+++ b/compiler-rt/lib/ubsan/ubsan_value.h
@@ -110,6 +110,8 @@ class TypeDescriptor {
     /// of the type for the signed _BitInt(N) type stored after zero bit after
     /// TypeName as 32-bit unsigned integer.
     TK_BitInt = 0x0002,
+    /// A fixed-point type. The value representation is currently empty.
+    TK_FixedPoint = 0x0003,
     /// Any other type. The value representation is unspecified.
     TK_Unknown = 0xffff
   };
@@ -164,6 +166,8 @@ class TypeDescriptor {
     CHECK(isFloatTy());
     return TypeInfo;
   }
+
+  bool isFixedPointTy() const { return getKind() == TK_FixedPoint; }
 };
 
 /// \brief An opaque handle to a value.
diff --git a/compiler-rt/test/ubsan/TestCases/FixedPoint/divide-by-zero.cpp 
b/compiler-rt/test/ubsan/TestCases/FixedPoint/divide-by-zero.cpp
new file mode 100644
index 0000000000000..a7631491e37fa
--- /dev/null
+++ b/compiler-rt/test/ubsan/TestCases/FixedPoint/divide-by-zero.cpp
@@ -0,0 +1,10 @@
+// RUN: %clangxx -DOP="0.0R / 0" -ffixed-point 
-fsanitize=fixed-point-divide-by-zero %s -o %t1 && %run %t1 2>&1 | FileCheck %s
+// RUN: %clangxx -DOP="0.5R / 0" -ffixed-point 
-fsanitize=fixed-point-divide-by-zero %s -o %t2 && %run %t2 2>&1 | FileCheck %s
+// RUN: %clangxx -DOP="0.0R / 0.0R" -ffixed-point 
-fsanitize=fixed-point-divide-by-zero %s -o %t3 && %run %t3 2>&1 | FileCheck %s
+// RUN: %clangxx -DOP="0.5R / 0.0R" -ffixed-point 
-fsanitize=fixed-point-divide-by-zero %s -o %t4 && %run %t4 2>&1 | FileCheck %s
+// RUN: %clangxx -fsanitize=undefined -DOP="_Fract a = 0.5R; a /= 0" 
-ffixed-point -fsanitize=fixed-point-divide-by-zero %s -o %t5 && %run %t5 2>&1 
| FileCheck %s
+
+int main() {
+  // CHECK: divide-by-zero.cpp:[[@LINE+1]]:3: runtime error: division by zero
+  OP;
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to