diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -103,6 +103,10 @@ previous placeholder value. Clang continues to accept ``-std=c2x`` and ``-std=gnu2x`` as aliases for C23 and GNU C23, respectively. +- Clang now adds which defines several macros for performing + checked integer arithmetic. The macro ``__STDC_VERSION_STDCKDINT_H__`` + is an integer constant expression with a value equivalent to ``202311L``. + Non-comprehensive list of changes in this release ------------------------------------------------- diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -12,6 +12,7 @@ stdarg.h stdatomic.h stdbool.h + stdckdint.h stddef.h __stddef_max_align_t.h stdint.h diff --git a/clang/lib/Headers/stdckdint.h b/clang/lib/Headers/stdckdint.h new file mode 100644 --- /dev/null +++ b/clang/lib/Headers/stdckdint.h @@ -0,0 +1,20 @@ +/*===---- stdckdint.h - Standard header for checking integer----------------=== + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __STDCKDINT_H +#define __STDCKDINT_H + +/* C23 7.20.1 Defines several macros for performing checked integer arithmetic*/ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#define ckd_add(R, A, B) __builtin_add_overflow((A), (B), (R)) +#define ckd_sub(R, A, B) __builtin_sub_overflow((A), (B), (R)) +#define ckd_mul(R, A, B) __builtin_mul_overflow((A), (B), (R)) +#endif + +#endif /* __STDCKDINT_H */ diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -385,6 +385,7 @@ .Case("stdarg.h", true) .Case("stdatomic.h", true) .Case("stdbool.h", true) + .Case("stdckdint.h", true) .Case("stddef.h", true) .Case("stdint.h", true) .Case("tgmath.h", true) diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -231,7 +231,8 @@ .Cases("assert.h", "complex.h", "ctype.h", "errno.h", "fenv.h", true) .Cases("float.h", "inttypes.h", "iso646.h", "limits.h", "locale.h", true) .Cases("math.h", "setjmp.h", "signal.h", "stdalign.h", "stdarg.h", true) - .Cases("stdatomic.h", "stdbool.h", "stddef.h", "stdint.h", "stdio.h", true) + .Cases("stdatomic.h", "stdbool.h", "stdckdint.h", "stddef.h", + "stdint.h", "stdio.h", true) .Cases("stdlib.h", "stdnoreturn.h", "string.h", "tgmath.h", "threads.h", true) .Cases("time.h", "uchar.h", "wchar.h", "wctype.h", true) diff --git a/clang/test/C/C2x/n2359.c b/clang/test/C/C2x/n2359.c --- a/clang/test/C/C2x/n2359.c +++ b/clang/test/C/C2x/n2359.c @@ -34,3 +34,8 @@ // expected-error@-1 {{"__STDC_VERSION_STDINT_H__ not defined"}} #endif +#include +#ifndef __STDC_VERSION_STDCKDINT_H__ +#error "__STDC_VERSION_STDCKDINT_H__ not defined" +// expected-error@-1 {{"__STDC_VERSION_STDCKDINT_H__ not defined"}} +#endif diff --git a/clang/test/C/C2x/n2683.c b/clang/test/C/C2x/n2683.c new file mode 100644 --- /dev/null +++ b/clang/test/C/C2x/n2683.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -verify -ffreestanding -std=c23 %s + +/* WG14 N2683: Clang 18 + * Define several macros for performing checked integer arithmetic + */ +#include + +void test_semantic() { + _BitInt(33) a33 = 1; + char char_var = 'd'; // The ascii value of `d` is 100 + bool bool_var = 1; + enum week{Mon, Tue, Wed}; + enum week day = Mon; + int result; + + bool flag_add = ckd_add(&result, a33, char_var); + bool flag_sub = ckd_sub(&result, bool_var, day); + bool flag_mul = ckd_mul(&result, day, char_var); +} + +void test_invalid_input() { + _BitInt(33) a33 = 1; + char char_var = 'd'; // The ascii value of `d` is 100 + bool bool_var = 1; + enum week{Mon, Tue, Wed}; + enum week day = Mon; + char plain_char[] = {U'牛'}; /* expected-warning {{implicit conversion from 'unsigned int' to 'char' changes value from 29275 to 91}} */ + bool flag_invalid = ckd_sub(&plain_char, char_var, day); /* expected-error {{result argument to overflow builtin must be a pointer to a non-const integer ('char (*)[1]' invalid)}} */ + bool flag_noptr = ckd_add(day, a33, bool_var); /* expected-error {{result argument to overflow builtin must be a pointer to a non-const integer ('enum week' invalid)}} */ +} diff --git a/clang/test/C/C2x/n2683_2.c b/clang/test/C/C2x/n2683_2.c new file mode 100644 --- /dev/null +++ b/clang/test/C/C2x/n2683_2.c @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -emit-llvm -o - -std=c23 %s | FileCheck %s +// expected-no-diagnostics + +#include +#include +// CHECK-LABEL: define dso_local void @test_add_overflow_to64() #0 { +// CHECK: entry: +// CHECK: %result64 = alloca i64, align 8 +// CHECK: %flag_add = alloca i8, align 1 +// CHECK: store i64 0, ptr %result64, align 8 +// CHECK: %0 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 2147483647, i64 1) +// CHECK: %1 = extractvalue { i64, i1 } %0, 1 +// CHECK: %2 = extractvalue { i64, i1 } %0, 0 +// CHECK: store i64 %2, ptr %result64, align 8 +// CHECK: %frombool = zext i1 %1 to i8 +// CHECK: store i8 %frombool, ptr %flag_add, align 1 +// CHECK: ret void +// CHECK: } +void test_add_overflow_to64() { + int64_t result64 = 0; + bool flag_add = ckd_add(&result64, INT32_MAX, 1); +} + +// CHECK-LABEL: define dso_local void @test_sub_overflow() #0 { +// CHECK: entry: +// CHECK: %result32 = alloca i32, align 4 +// CHECK: %flag_sub = alloca i8, align 1 +// CHECK: store i32 0, ptr %result32, align 4 +// CHECK: %0 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 2147483647, i32 -1) +// CHECK: %1 = extractvalue { i32, i1 } %0, 1 +// CHECK: %2 = extractvalue { i32, i1 } %0, 0 +// CHECK: store i32 %2, ptr %result32, align 4 +// CHECK: %frombool = zext i1 %1 to i8 +// CHECK: store i8 %frombool, ptr %flag_sub, align 1 +// CHECK: ret void +// CHECK: } +void test_sub_overflow() { + int32_t result32 = 0; + bool flag_sub = ckd_sub(&result32, INT32_MAX, -1); +} + +// CHECK-LABEL: define dso_local void @test_mul_normal() #0 { +// CHECK: entry: +// CHECK: %a = alloca i32, align 4 +// CHECK: %result = alloca i32, align 4 +// CHECK: %flag_mul = alloca i8, align 1 +// CHECK: store i32 3, ptr %a, align 4 +// CHECK: store i32 0, ptr %result, align 4 +// CHECK: %0 = load i32, ptr %a, align 4 +// CHECK: %1 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %0, i32 2) +// CHECK: %2 = extractvalue { i32, i1 } %1, 1 +// CHECK: %3 = extractvalue { i32, i1 } %1, 0 +// CHECK: store i32 %3, ptr %result, align 4 +// CHECK: %frombool = zext i1 %2 to i8 +// CHECK: store i8 %frombool, ptr %flag_mul, align 1 +// CHECK: ret void +// CHECK: } +void test_mul_normal() { + int a = 3; + int result = 0; + bool flag_mul = ckd_mul(&result, a, 2); +} diff --git a/clang/test/Headers/stdckdint.c b/clang/test/Headers/stdckdint.c new file mode 100644 --- /dev/null +++ b/clang/test/Headers/stdckdint.c @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -emit-llvm -verify -fgnuc-version=4.2.1 -std=c23 %s -o - | FileCheck %s +// expected-no-diagnostics +#include + +// CHECK-LABEL: define dso_local zeroext i1 @test_ckd_add() #0 { +// CHECK: entry: +// CHECK: %result = alloca i32, align 4 +// CHECK: %0 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 -1073741826, i32 -1073741826) +// CHECK: %1 = extractvalue { i32, i1 } %0, 1 +// CHECK: %2 = extractvalue { i32, i1 } %0, 0 +// CHECK: store i32 %2, ptr %result, align 4 +// CHECK: ret i1 %1 +// CHECK: } +// CHECK: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +// CHECK: declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32) #1 +bool test_ckd_add() { + int result; + return ckd_add(&result, -1073741826, -1073741826); +} + +// CHECK-LABEL: define dso_local zeroext i1 @test_ckd_sub() #0 { +// CHECK: entry: +// CHECK: %result = alloca i32, align 4 +// CHECK: %0 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 -1073741826, i32 1073741826) +// CHECK: %1 = extractvalue { i32, i1 } %0, 1 +// CHECK: %2 = extractvalue { i32, i1 } %0, 0 +// CHECK: store i32 %2, ptr %result, align 4 +// CHECK: ret i1 %1 +// CHECK: } +// CHECK: declare { i32, i1 } @llvm.ssub.with.overflow.i32(i32, i32) #1 +bool test_ckd_sub() { + int result; + return ckd_sub(&result, -1073741826, 1073741826); +} + +// CHECK-LABEL: define dso_local zeroext i1 @test_ckd_mul() #0 { +// CHECK: entry: +// CHECK: %result = alloca i32, align 4 +// CHECK: %0 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 -1073741826, i32 2) +// CHECK: %1 = extractvalue { i32, i1 } %0, 1 +// CHECK: %2 = extractvalue { i32, i1 } %0, 0 +// CHECK: store i32 %2, ptr %result, align 4 +// CHECK: ret i1 %1 +// CHECK: } +// CHECK: declare { i32, i1 } @llvm.smul.with.overflow.i32(i32, i32) #1 +bool test_ckd_mul() { + int result; + return ckd_mul(&result, -1073741826, 2); +} diff --git a/clang/test/Modules/Inputs/System/usr/include/module.map b/clang/test/Modules/Inputs/System/usr/include/module.map --- a/clang/test/Modules/Inputs/System/usr/include/module.map +++ b/clang/test/Modules/Inputs/System/usr/include/module.map @@ -14,6 +14,11 @@ header "stdbool.h" } + // In both directories (compiler support version wins, does not forward) + module stdckdint { + header "stdckdint.h" + } + // In both directories (compiler support version wins, forwards) module stdint { header "stdint.h" diff --git a/clang/www/c_status.html b/clang/www/c_status.html --- a/clang/www/c_status.html +++ b/clang/www/c_status.html @@ -886,6 +886,11 @@ N2672 Yes + + Towards Integer Safety + N2683 + Clang 18 + Adding Fundamental Type for N-bit Integers