Author: eleviant
Date: 2026-05-29T15:22:31+02:00
New Revision: 0fd11e4a83e862fcb48690555015f7da44131075

URL: 
https://github.com/llvm/llvm-project/commit/0fd11e4a83e862fcb48690555015f7da44131075
DIFF: 
https://github.com/llvm/llvm-project/commit/0fd11e4a83e862fcb48690555015f7da44131075.diff

LOG: Honor two's complement signed overflow with -fms-compatibility (#198538)

This matches MSVC behavior, where signed integer overflow follows
two's-complement semantics

Added: 
    clang/test/CodeGen/wrapv.c

Modified: 
    clang/docs/UsersManual.rst
    clang/include/clang/Driver/CommonArgs.h
    clang/include/clang/Options/Options.td
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Driver/ToolChains/CommonArgs.cpp
    clang/lib/Driver/ToolChains/Flang.cpp
    clang/lib/Frontend/CompilerInvocation.cpp
    clang/test/CodeGen/ms-intrinsics.c
    clang/test/Driver/clang_wrapv_opts.c
    compiler-rt/test/ubsan/TestCases/Integer/shift.cpp
    compiler-rt/test/ubsan/TestCases/Misc/abs.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 4c4c4c4aa9706..3392a210f0bb0 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -4189,6 +4189,11 @@ Changing the MSVC compatibility version makes clang 
behave more like that
 version of MSVC. For example, ``-fms-compatibility-version=19`` will enable
 C++14 features and define ``char16_t`` and ``char32_t`` as builtin types.
 
+For compatibility with existing MSVC behavior, ``-fms-compatibility`` also
+implicitly enables several other options, including ``-fno-strict-aliasing``,
+``-fwrapv`` and ``-fdelayed-template-parsing``. When MSVC compatibility is
+set to a version earlier than 19, it also enables ``-fno-threadsafe-statics``.
+
 .. _cxx:
 
 C++ Language Features

diff  --git a/clang/include/clang/Driver/CommonArgs.h 
b/clang/include/clang/Driver/CommonArgs.h
index 0af1b89425227..34b3d302fe189 100644
--- a/clang/include/clang/Driver/CommonArgs.h
+++ b/clang/include/clang/Driver/CommonArgs.h
@@ -283,7 +283,8 @@ void renderGlobalISelOptions(const Driver &D, const 
llvm::opt::ArgList &Args,
                              const llvm::Triple &Triple);
 
 void renderCommonIntegerOverflowOptions(const llvm::opt::ArgList &Args,
-                                        llvm::opt::ArgStringList &CmdArgs);
+                                        llvm::opt::ArgStringList &CmdArgs,
+                                        bool IsMSVCCompat);
 
 bool shouldEnableVectorizerAtOLevel(const llvm::opt::ArgList &Args,
                                     bool isSlpVec);

diff  --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 5dab4af7618fc..025e8e7d7d761 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4815,7 +4815,7 @@ def fwrapv : Flag<["-"], "fwrapv">, Group<f_Group>,
   Visibility<[ClangOption, CLOption, CC1Option, FlangOption, FC1Option]>,
   HelpText<"Treat signed integer overflow as two's complement">;
 def fno_wrapv : Flag<["-"], "fno-wrapv">, Group<f_Group>,
-  Visibility<[ClangOption, CLOption, FlangOption]>;
+  Visibility<[ClangOption, CC1Option, CLOption, FlangOption]>;
 def fwrapv_pointer : Flag<["-"], "fwrapv-pointer">, Group<f_Group>,
   Visibility<[ClangOption, CLOption, CC1Option, FlangOption, FC1Option]>,
   HelpText<"Treat pointer overflow as two's complement">;

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index df49877d4bf62..207b9e519a8ea 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7144,10 +7144,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
 
   Args.AddLastArg(CmdArgs, options::OPT_ftrap_function_EQ);
 
-  // Handle -f[no-]wrapv and -f[no-]strict-overflow, which are used by both
-  // clang and flang.
-  renderCommonIntegerOverflowOptions(Args, CmdArgs);
-
   Args.AddLastArg(CmdArgs, options::OPT_ffinite_loops,
                   options::OPT_fno_finite_loops);
 
@@ -7419,6 +7415,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
       CmdArgs.push_back("-fms-define-stdc");
   }
 
+  // Handle -f[no-]wrapv and -f[no-]strict-overflow, which are used by both
+  // clang and flang.
+  renderCommonIntegerOverflowOptions(Args, CmdArgs, IsMSVCCompat);
+
   // -fms-anonymous-structs is disabled by default.
   // Determine whether to enable Microsoft named anonymous struct/union 
support.
   // This implements "last flag wins" semantics for -fms-anonymous-structs,

diff  --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp 
b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 6f0ac7d5159c1..d0dab119fa6d8 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -3392,8 +3392,9 @@ void tools::renderGlobalISelOptions(const Driver &D, 
const ArgList &Args,
 }
 
 void tools::renderCommonIntegerOverflowOptions(const ArgList &Args,
-                                               ArgStringList &CmdArgs) {
-  bool use_fwrapv = false;
+                                               ArgStringList &CmdArgs,
+                                               bool IsMSVCCompat) {
+  bool use_fwrapv = IsMSVCCompat;
   bool use_fwrapv_pointer = false;
   for (const Arg *A : Args.filtered(
            options::OPT_fstrict_overflow, options::OPT_fno_strict_overflow,
@@ -3426,6 +3427,8 @@ void tools::renderCommonIntegerOverflowOptions(const 
ArgList &Args,
 
   if (use_fwrapv)
     CmdArgs.push_back("-fwrapv");
+  if (!use_fwrapv && IsMSVCCompat)
+    CmdArgs.push_back("-fno-wrapv");
   if (use_fwrapv_pointer)
     CmdArgs.push_back("-fwrapv-pointer");
 }

diff  --git a/clang/lib/Driver/ToolChains/Flang.cpp 
b/clang/lib/Driver/ToolChains/Flang.cpp
index 4c722a2e021eb..892a455167205 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -1269,7 +1269,7 @@ void Flang::ConstructJob(Compilation &C, const JobAction 
&JA,
   }
 
   renderGlobalISelOptions(D, Args, CmdArgs, Triple);
-  renderCommonIntegerOverflowOptions(Args, CmdArgs);
+  renderCommonIntegerOverflowOptions(Args, CmdArgs, false);
 
   assert((Output.isFilename() || Output.isNothing()) && "Invalid output.");
   if (Output.isFilename()) {

diff  --git a/clang/lib/Frontend/CompilerInvocation.cpp 
b/clang/lib/Frontend/CompilerInvocation.cpp
index 60749104252af..9fc695a74a3c7 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -3797,7 +3797,10 @@ void CompilerInvocationBase::GenerateLangArgs(const 
LangOptions &Opts,
     GenerateArg(Consumer, OPT_ftrapv);
     GenerateArg(Consumer, OPT_ftrapv_handler, Opts.OverflowHandler);
   } else if (Opts.SignedOverflowBehavior == LangOptions::SOB_Defined) {
-    GenerateArg(Consumer, OPT_fwrapv);
+    if (!Opts.MSVCCompat)
+      GenerateArg(Consumer, OPT_fwrapv);
+  } else if (Opts.MSVCCompat) {
+    GenerateArg(Consumer, OPT_fno_wrapv);
   }
   if (Opts.PointerOverflowDefined)
     GenerateArg(Consumer, OPT_fwrapv_pointer);
@@ -4212,9 +4215,9 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, 
ArgList &Args,
     // Set the handler, if one is specified.
     Opts.OverflowHandler =
         std::string(Args.getLastArgValue(OPT_ftrapv_handler));
-  }
-  else if (Args.hasArg(OPT_fwrapv))
+  } else if (Args.hasFlag(OPT_fwrapv, OPT_fno_wrapv, Opts.MSVCCompat)) {
     Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined);
+  }
   if (Args.hasArg(OPT_fwrapv_pointer))
     Opts.PointerOverflowDefined = true;
 

diff  --git a/clang/test/CodeGen/ms-intrinsics.c 
b/clang/test/CodeGen/ms-intrinsics.c
index 271aced5e0b7c..8b719ef8cc1fd 100644
--- a/clang/test/CodeGen/ms-intrinsics.c
+++ b/clang/test/CodeGen/ms-intrinsics.c
@@ -504,8 +504,8 @@ unsigned char test_InterlockedCompareExchange128(
 }
 // CHECK-64: define{{.*}}i8 
@test_InterlockedCompareExchange128(ptr{{.*}}%Destination, i64{{[a-z_ 
]*}}%ExchangeHigh, i64{{[a-z_ ]*}}%ExchangeLow, 
ptr{{.*}}%ComparandResult){{.*}}{
 // CHECK-64: %incdec.ptr = getelementptr inbounds nuw i8, ptr %Destination, 
i64 8
-// CHECK-64: %inc = add nsw i64 %ExchangeHigh, 1
-// CHECK-64: %inc1 = add nsw i64 %ExchangeLow, 1
+// CHECK-64: %inc = add i64 %ExchangeHigh, 1
+// CHECK-64: %inc1 = add i64 %ExchangeLow, 1
 // CHECK-64: %incdec.ptr2 = getelementptr inbounds nuw i8, ptr 
%ComparandResult, i64 8
 // CHECK-64: [[EH:%[0-9]+]] = zext i64 %inc to i128
 // CHECK-64: [[EL:%[0-9]+]] = zext i64 %inc1 to i128

diff  --git a/clang/test/CodeGen/wrapv.c b/clang/test/CodeGen/wrapv.c
new file mode 100644
index 0000000000000..f1d18cfafd2a0
--- /dev/null
+++ b/clang/test/CodeGen/wrapv.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s --check-prefix=NOWRAP
+// RUN: %clang_cc1 -fno-wrapv -emit-llvm %s -o - | FileCheck %s 
--check-prefix=NOWRAP
+// RUN: %clang_cc1 -fno-wrapv -fms-compatibility -emit-llvm %s -o - | 
FileCheck %s --check-prefix=NOWRAP
+// RUN: %clang_cc1 -fwrapv -emit-llvm %s -o - | FileCheck %s 
--check-prefix=WRAP
+// RUN: %clang_cc1 -fms-compatibility -emit-llvm %s -o - | FileCheck %s 
--check-prefix=WRAP
+// RUN: %clang_cc1 -ftrapv -fms-compatibility -emit-llvm %s -o - | FileCheck 
%s --check-prefix=TRAP
+// RUN: %clang_cc1 -ftrapv -fwrapv -emit-llvm %s -o - | FileCheck %s 
--check-prefix=TRAP
+
+// NOWRAP: %add = add nsw i32 %0, 1
+// WRAP: %add = add i32 %0, 1
+// TRAP: llvm.sadd.with.overflow
+
+int add1(int x) {
+  return x + 1;
+}

diff  --git a/clang/test/Driver/clang_wrapv_opts.c 
b/clang/test/Driver/clang_wrapv_opts.c
index 295d8deb0d99d..012d8f5131d5f 100644
--- a/clang/test/Driver/clang_wrapv_opts.c
+++ b/clang/test/Driver/clang_wrapv_opts.c
@@ -18,3 +18,9 @@
 
 // RUN: %clang -### -S -fno-wrapv-pointer -fno-strict-overflow -fno-wrapv 
-Werror %s 2>&1 | FileCheck -check-prefix=CHECK4-POINTER %s 
--implicit-check-not="-fwrapv"
 // CHECK4-POINTER: "-fwrapv-pointer"
+
+// RUN: %clang -### -S --target=x86_64-windows-msvc -Werror %s 2>&1 | 
FileCheck -check-prefix=CHECK5 %s
+// CHECK5: "-fwrapv"
+
+// RUN: %clang -### -S -fno-wrapv --target=x86_64-windows-msvc -Werror %s 2>&1 
| FileCheck -check-prefix=CHECK6 %s
+// CHECK6: "-fno-wrapv"

diff  --git a/compiler-rt/test/ubsan/TestCases/Integer/shift.cpp 
b/compiler-rt/test/ubsan/TestCases/Integer/shift.cpp
index 50db16dac18ec..3c4f9692b7a33 100644
--- a/compiler-rt/test/ubsan/TestCases/Integer/shift.cpp
+++ b/compiler-rt/test/ubsan/TestCases/Integer/shift.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift-base 
-fno-sanitize-recover=shift %s -o %t1 && not %run %t1 2>&1 | FileCheck %s 
--check-prefix=CHECK-LSH_OVERFLOW
-// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift 
-fno-sanitize-recover=shift %s -o %t2 && not %run %t2 2>&1 | FileCheck %s 
--check-prefix=CHECK-LSH_OVERFLOW
+// RUN: %clangxx -DLSH_OVERFLOW -fno-wrapv -DOP='<<' -fsanitize=shift-base 
-fno-sanitize-recover=shift %s -o %t1 && not %run %t1 2>&1 | FileCheck %s 
--check-prefix=CHECK-LSH_OVERFLOW
+// RUN: %clangxx -DLSH_OVERFLOW -fno-wrapv -DOP='<<=' -fsanitize=shift 
-fno-sanitize-recover=shift %s -o %t2 && not %run %t2 2>&1 | FileCheck %s 
--check-prefix=CHECK-LSH_OVERFLOW
 // RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift-exponent 
-fno-sanitize-recover=shift %s -o %t3 && not %run %t3 2>&1 | FileCheck %s 
--check-prefix=CHECK-TOO_LOW
 // RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift 
-fno-sanitize-recover=shift %s -o %t4 && not %run %t4 2>&1 | FileCheck %s 
--check-prefix=CHECK-TOO_LOW
 // RUN: %clangxx -DTOO_LOW -DOP='<<=' -fsanitize=shift 
-fno-sanitize-recover=shift %s -o %t5 && not %run %t5 2>&1 | FileCheck %s 
--check-prefix=CHECK-TOO_LOW
@@ -9,8 +9,8 @@
 // RUN: %clangxx -DTOO_HIGH -DOP='<<=' -fsanitize=shift 
-fno-sanitize-recover=shift %s -o %t9 && not %run %t9 2>&1 | FileCheck %s 
--check-prefix=CHECK-TOO_HIGH
 // RUN: %clangxx -DTOO_HIGH -DOP='>>=' -fsanitize=shift 
-fno-sanitize-recover=shift %s -o %t10 && not %run %t10 2>&1 | FileCheck %s 
--check-prefix=CHECK-TOO_HIGH
 
-// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift-exponent 
-fno-sanitize-recover=shift %s -o %t12 && %run %t12
-// RUN: %clangxx -DLSH_OVERFLOW -DOP='>>' -fsanitize=shift-exponent 
-fno-sanitize-recover=shift %s -o %t13 && %run %t13
+// RUN: %clangxx -DLSH_OVERFLOW -fno-wrapv -DOP='<<' -fsanitize=shift-exponent 
-fno-sanitize-recover=shift %s -o %t12 && %run %t12
+// RUN: %clangxx -DLSH_OVERFLOW -fno-wrapv -DOP='>>' -fsanitize=shift-exponent 
-fno-sanitize-recover=shift %s -o %t13 && %run %t13
 // RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift-base 
-fno-sanitize-recover=shift %s -o %t14 && %run %t14
 // RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift-base 
-fno-sanitize-recover=shift %s -o %t15 && %run %t15
 // RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift-base 
-fno-sanitize-recover=shift %s -o %t16 && %run %t16

diff  --git a/compiler-rt/test/ubsan/TestCases/Misc/abs.cpp 
b/compiler-rt/test/ubsan/TestCases/Misc/abs.cpp
index 5f4beec1e6294..30c9891f224f6 100644
--- a/compiler-rt/test/ubsan/TestCases/Misc/abs.cpp
+++ b/compiler-rt/test/ubsan/TestCases/Misc/abs.cpp
@@ -1,7 +1,7 @@
-// RUN: %clangxx -fsanitize=signed-integer-overflow -w %s -O3 -o %t
+// RUN: %clangxx -fno-wrapv -fsanitize=signed-integer-overflow -w %s -O3 -o %t
 // RUN: %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER
 
-// RUN: %clangxx -fsanitize=signed-integer-overflow 
-fno-sanitize-recover=signed-integer-overflow -w %s -O3 -o %t.abort
+// RUN: %clangxx -fno-wrapv -fsanitize=signed-integer-overflow 
-fno-sanitize-recover=signed-integer-overflow -w %s -O3 -o %t.abort
 // RUN: not %run %t.abort 2>&1 | FileCheck %s --check-prefix=ABORT
 
 #include <limits.h>


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

Reply via email to