diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 2268df70927a7..e94cb6fb274e2 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5037,6 +5037,24 @@ def ArithmeticFence : LangBuiltin<"ALL_LANGUAGES"> { let Prototype = "void(...)"; } +def AddNUW : Builtin { + let Spellings = ["__builtin_add_nuw"]; + let Attributes = [CustomTypeChecking, NoThrow]; + let Prototype = "void(...)"; +} + +def AddNSW : Builtin { + let Spellings = ["__builtin_add_nsw"]; + let Attributes = [CustomTypeChecking, NoThrow]; + let Prototype = "void(...)"; +} + +def AddNW : Builtin { + let Spellings = ["__builtin_add_nuw_nsw"]; + let Attributes = [CustomTypeChecking, NoThrow]; + let Prototype = "void(...)"; +} + def CountedByRef : Builtin { let Spellings = ["__builtin_counted_by_ref"]; let Attributes = [NoThrow, CustomTypeChecking]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 239f16769ace9..bdb63ab1daa2b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12527,6 +12527,9 @@ def err_builtin_matrix_stride_too_small: Error< def err_builtin_matrix_invalid_dimension: Error< "%0 dimension is outside the allowed range [1, %1]">; +def err_builtin_add_nxw_invalid_arg : Error< + "both arguments must be of integral type but %ordinal0 argument is %1">; + def warn_mismatched_import : Warning< "import %select{module|name}0 (%1) does not match the import %select{module|name}0 (%2) of the " "previous declaration">, diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index ba78de049ce96..0f87408d8fb0d 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -6616,6 +6616,17 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, auto Str = CGM.GetAddrOfConstantCString(Name, ""); return RValue::get(Str.getPointer()); } + + case Builtin::BI__builtin_add_nuw: + case Builtin::BI__builtin_add_nsw: + case Builtin::BI__builtin_add_nuw_nsw: { + bool NXW = BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_add_nuw_nsw; + bool NUW = BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_add_nuw || NXW; + bool NSW = BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_add_nsw || NXW; + llvm::Value *X = EmitScalarExpr(E->getArg(0)); + llvm::Value *Y = EmitScalarExpr(E->getArg(1)); + return RValue::get(Builder.CreateAdd(X, Y, "add", NUW, NSW)); + } } // If this is an alias for a lib function (e.g. __builtin_sin), emit diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 9cac9cf5c4df7..dc8526271237e 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2105,6 +2105,34 @@ static bool BuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) { return false; } +static bool BuiltinAddNoWrap(Sema &S, CallExpr *TheCall) { + if (S.checkArgCount(TheCall, 2)) + return true; + ExprResult OrigArg0 = TheCall->getArg(0); + ExprResult OrigArg1 = TheCall->getArg(1); + ExprResult Arg0 = OrigArg0; + ExprResult Arg1 = OrigArg1; + + QualType Res = S.UsualArithmeticConversions(Arg0, Arg1, TheCall->getExprLoc(), + Sema::ACK_Arithmetic); + if (Arg0.isInvalid() || Arg1.isInvalid()) + return true; + + if (Res.isNull() || !Res->isIntegralType(S.Context)) { + Expr *E0 = OrigArg0.get(); + Expr *E1 = OrigArg1.get(); + auto BadArg = !E0->getType()->isIntegralType(S.Context) ? E0 : E1; + return S.Diag(BadArg->getBeginLoc(), diag::err_builtin_add_nxw_invalid_arg) + << (BadArg == E0 ? 1 : 2) << BadArg->getType() + << BadArg->getSourceRange(); + } + + TheCall->setArg(0, Arg0.get()); + TheCall->setArg(1, Arg1.get()); + TheCall->setType(Res); + return false; +} + ExprResult Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, CallExpr *TheCall) { @@ -2975,6 +3003,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, if (BuiltinCountedByRef(TheCall)) return ExprError(); break; + case Builtin::BI__builtin_add_nuw: + case Builtin::BI__builtin_add_nsw: + case Builtin::BI__builtin_add_nuw_nsw: + if (BuiltinAddNoWrap(*this, TheCall)) + return ExprError(); + break; } if (getLangOpts().HLSL && HLSL().CheckBuiltinFunctionCall(BuiltinID, TheCall)) diff --git a/clang/test/CodeGen/builtins-int-wrap.c b/clang/test/CodeGen/builtins-int-wrap.c new file mode 100644 index 0000000000000..30f9de17869bc --- /dev/null +++ b/clang/test/CodeGen/builtins-int-wrap.c @@ -0,0 +1,111 @@ +// Test CodeGen for nuw/nsw builtins. + +// RUN: %clang_cc1 -triple "x86_64-unknown-unknown" -emit-llvm -x c %s -o - | FileCheck %s + +//------------------------------------------------------------------------------ +// int +//------------------------------------------------------------------------------ +int test_add_nuw(int x, int y) { return __builtin_add_nuw(x, y); } +// CHECK-LABEL: @test_add_nuw +// CHECK: [[RV:%.+]] = add nuw i32 +// CHECK: ret i32 [[RV]] + +int test_add_nsw(int x, int y) { return __builtin_add_nsw(x, y); } +// CHECK-LABEL: @test_add_nsw +// CHECK: [[RV:%.+]] = add nsw i32 +// CHECK: ret i32 [[RV]] + +int test_add_nuw_nsw(int x, int y) { return __builtin_add_nuw_nsw(x, y); } +// CHECK-LABEL: @test_add_nuw_nsw +// CHECK: [[RV:%.+]] = add nuw nsw i32 +// CHECK: ret i32 [[RV]] + +//------------------------------------------------------------------------------ +// long int +//------------------------------------------------------------------------------ +long int test_add_nuw_l(long int x, long int y) { return __builtin_add_nuw(x, y); } +// CHECK-LABEL: @test_add_nuw_l +// CHECK: [[RV:%.+]] = add nuw i64 +// CHECK: ret i64 [[RV]] + +long int test_add_nsw_l(long int x, long int y) { return __builtin_add_nsw(x, y); } +// CHECK-LABEL: @test_add_nsw_l +// CHECK: [[RV:%.+]] = add nsw i64 +// CHECK: ret i64 [[RV]] + +long int test_add_nuw_nsw_l(long int x, long int y) { return __builtin_add_nuw_nsw(x, y); } +// CHECK-LABEL: @test_add_nuw_nsw_l +// CHECK: [[RV:%.+]] = add nuw nsw i64 +// CHECK: ret i64 [[RV]] + +//------------------------------------------------------------------------------ +// long int +//------------------------------------------------------------------------------ +long long int test_add_nuw_ll(long long int x, long long int y) { return __builtin_add_nuw(x, y); } +// CHECK-LABEL: @test_add_nuw_ll +// CHECK: [[RV:%.+]] = add nuw i64 +// CHECK: ret i64 [[RV]] + +long long int test_add_nsw_ll(long long int x, long long int y) { return __builtin_add_nsw(x, y); } +// CHECK-LABEL: @test_add_nsw_ll +// CHECK: [[RV:%.+]] = add nsw i64 +// CHECK: ret i64 [[RV]] + +long long int test_add_nuw_nsw_ll(long long int x, long long int y) { return __builtin_add_nuw_nsw(x, y); } +// CHECK-LABEL: @test_add_nuw_nsw_ll +// CHECK: [[RV:%.+]] = add nuw nsw i64 +// CHECK: ret i64 [[RV]] + +//------------------------------------------------------------------------------ +// unsigned int +//------------------------------------------------------------------------------ +unsigned int test_add_nuw_u(unsigned int x, unsigned int y) { return __builtin_add_nuw(x, y); } +// CHECK-LABEL: @test_add_nuw_u +// CHECK: [[RV:%.+]] = add nuw i32 +// CHECK: ret i32 [[RV]] + +unsigned int test_add_nsw_u(unsigned int x, unsigned int y) { return __builtin_add_nsw(x, y); } +// CHECK-LABEL: @test_add_nsw_u +// CHECK: [[RV:%.+]] = add nsw i32 +// CHECK: ret i32 [[RV]] + +unsigned int test_add_nuw_nsw_u(unsigned int x, unsigned int y) { return __builtin_add_nuw_nsw(x, y); } +// CHECK-LABEL: @test_add_nuw_nsw_u +// CHECK: [[RV:%.+]] = add nuw nsw i32 +// CHECK: ret i32 [[RV]] + +//------------------------------------------------------------------------------ +// unsigned long int +//------------------------------------------------------------------------------ +unsigned long int test_add_nuw_ul(unsigned long int x, unsigned long int y) { return __builtin_add_nuw(x, y); } +// CHECK-LABEL: @test_add_nuw_ul +// CHECK: [[RV:%.+]] = add nuw i64 +// CHECK: ret i64 [[RV]] + +unsigned long int test_add_nsw_ul(unsigned long int x, unsigned long int y) { return __builtin_add_nsw(x, y); } +// CHECK-LABEL: @test_add_nsw_ul +// CHECK: [[RV:%.+]] = add nsw i64 +// CHECK: ret i64 [[RV]] + +unsigned long int test_add_nuw_nsw_ul(unsigned long int x, unsigned long int y) { return __builtin_add_nuw_nsw(x, y); } +// CHECK-LABEL: @test_add_nuw_nsw_ul +// CHECK: [[RV:%.+]] = add nuw nsw i64 +// CHECK: ret i64 [[RV]] + +//------------------------------------------------------------------------------ +// unsigned long long int +//------------------------------------------------------------------------------ +unsigned long long int test_add_nuw_ull(unsigned long long int x, unsigned long long int y) { return __builtin_add_nuw(x, y); } +// CHECK-LABEL: @test_add_nuw_ull +// CHECK: [[RV:%.+]] = add nuw i64 +// CHECK: ret i64 [[RV]] + +unsigned long long int test_add_nsw_ull(unsigned long long int x, unsigned long long int y) { return __builtin_add_nsw(x, y); } +// CHECK-LABEL: @test_add_nsw_ull +// CHECK: [[RV:%.+]] = add nsw i64 +// CHECK: ret i64 [[RV]] + +unsigned long long int test_add_nuw_nsw_ull(unsigned long long int x, unsigned long long int y) { return __builtin_add_nuw_nsw(x, y); } +// CHECK-LABEL: @test_add_nuw_nsw_ull +// CHECK: [[RV:%.+]] = add nuw nsw i64 +// CHECK: ret i64 [[RV]] diff --git a/clang/test/Sema/builtins-int-wrap.c b/clang/test/Sema/builtins-int-wrap.c new file mode 100644 index 0000000000000..a59851ace3a97 --- /dev/null +++ b/clang/test/Sema/builtins-int-wrap.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-unknown %s + + +struct Foo { int x; }; + +int test_add_nuw(int x, double y, struct Foo z) { + __builtin_add_nuw(x, y); // expected-error {{both arguments must be of integral type but 2nd argument is 'double'}} + __builtin_add_nuw(y, x); // expected-error {{both arguments must be of integral type but 1st argument is 'double'}} + __builtin_add_nuw(x, z); // expected-error {{both arguments must be of integral type but 2nd argument is 'struct Foo'}} +} + +int test_add_nsw(int x, double y, struct Foo z) { + __builtin_add_nsw(x, y); // expected-error {{both arguments must be of integral type but 2nd argument is 'double'}} + __builtin_add_nsw(y, x); // expected-error {{both arguments must be of integral type but 1st argument is 'double'}} + __builtin_add_nsw(x, z); // expected-error {{both arguments must be of integral type but 2nd argument is 'struct Foo'}} +} + +int test_add_nuw_nsw(int x, double y, struct Foo z) { + __builtin_add_nuw_nsw(x, y); // expected-error {{both arguments must be of integral type but 2nd argument is 'double'}} + __builtin_add_nuw_nsw(y, x); // expected-error {{both arguments must be of integral type but 1st argument is 'double'}} + __builtin_add_nuw_nsw(x, z); // expected-error {{both arguments must be of integral type but 2nd argument is 'struct Foo'}} +}