-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[clang] Add builtins for add with nuw and/or nsw
#130354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: None (macurtis-amd) ChangesTo be used for OpenMP runtime library development where lack of control over nuw/nsw prevents analysis by ScalarEvolution and subsequently loop unrolling. Full diff: https://github.com/llvm/llvm-project/pull/130354.diff 3 Files Affected:
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 2268df70927a7..ba9250516ec1b 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5037,6 +5037,66 @@ def ArithmeticFence : LangBuiltin<"ALL_LANGUAGES"> {
let Prototype = "void(...)";
}
+class SNUWTemplate :
+ Template<["int", "long int", "long long int"],
+ ["_nuw", "l_nuw", "ll_nuw"]>;
+
+class SNSWTemplate :
+ Template<["int", "long int", "long long int"],
+ ["_nsw", "l_nsw", "ll_nsw"]>;
+
+class SNXWTemplate :
+ Template<["int", "long int", "long long int"],
+ ["_nuw_nsw", "l_nuw_nsw", "ll_nuw_nsw"]>;
+
+def SAddNUW : Builtin, SNUWTemplate {
+ let Spellings = ["__builtin_sadd"];
+ let Attributes = [NoThrow];
+ let Prototype = "T(T const, T const)";
+}
+
+def SAddNSW : Builtin, SNSWTemplate {
+ let Spellings = ["__builtin_sadd"];
+ let Attributes = [NoThrow];
+ let Prototype = "T(T const, T const)";
+}
+
+def SAddNXW : Builtin, SNXWTemplate {
+ let Spellings = ["__builtin_sadd"];
+ let Attributes = [NoThrow];
+ let Prototype = "T(T const, T const)";
+}
+
+class UNUWTemplate :
+ Template<["unsigned int", "unsigned long int", "unsigned long long int"],
+ ["_nuw", "l_nuw", "ll_nuw"]>;
+
+class UNSWTemplate :
+ Template<["unsigned int", "unsigned long int", "unsigned long long int"],
+ ["_nsw", "l_nsw", "ll_nsw"]>;
+
+class UNXWTemplate :
+ Template<["unsigned int", "unsigned long int", "unsigned long long int"],
+ ["_nuw_nsw", "l_nuw_nsw", "ll_nuw_nsw"]>;
+
+def UAddNUW : Builtin, UNUWTemplate {
+ let Spellings = ["__builtin_uadd"];
+ let Attributes = [NoThrow];
+ let Prototype = "T(T const, T const)";
+}
+
+def UAddNSW : Builtin, UNSWTemplate {
+ let Spellings = ["__builtin_uadd"];
+ let Attributes = [NoThrow];
+ let Prototype = "T(T const, T const)";
+}
+
+def UAddNXW : Builtin, UNXWTemplate {
+ let Spellings = ["__builtin_uadd"];
+ let Attributes = [NoThrow];
+ let Prototype = "T(T const, T const)";
+}
+
def CountedByRef : Builtin {
let Spellings = ["__builtin_counted_by_ref"];
let Attributes = [NoThrow, CustomTypeChecking];
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index b86bb242755be..227cffe8ae2f7 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6616,6 +6616,54 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
auto Str = CGM.GetAddrOfConstantCString(Name, "");
return RValue::get(Str.getPointer());
}
+
+ case Builtin::BI__builtin_sadd_nuw:
+ case Builtin::BI__builtin_saddl_nuw:
+ case Builtin::BI__builtin_saddll_nuw:
+ case Builtin::BI__builtin_uadd_nuw:
+ case Builtin::BI__builtin_uaddl_nuw:
+ case Builtin::BI__builtin_uaddll_nuw:
+
+ case Builtin::BI__builtin_sadd_nsw:
+ case Builtin::BI__builtin_saddl_nsw:
+ case Builtin::BI__builtin_saddll_nsw:
+ case Builtin::BI__builtin_uadd_nsw:
+ case Builtin::BI__builtin_uaddl_nsw:
+ case Builtin::BI__builtin_uaddll_nsw:
+
+ case Builtin::BI__builtin_sadd_nuw_nsw:
+ case Builtin::BI__builtin_saddl_nuw_nsw:
+ case Builtin::BI__builtin_saddll_nuw_nsw:
+ case Builtin::BI__builtin_uadd_nuw_nsw:
+ case Builtin::BI__builtin_uaddl_nuw_nsw:
+ case Builtin::BI__builtin_uaddll_nuw_nsw: {
+ bool NUW = false;
+ bool NSW = false;
+ switch (BuiltinIDIfNoAsmLabel) {
+ case Builtin::BI__builtin_sadd_nuw:
+ case Builtin::BI__builtin_saddl_nuw:
+ case Builtin::BI__builtin_saddll_nuw:
+ case Builtin::BI__builtin_uadd_nuw:
+ case Builtin::BI__builtin_uaddl_nuw:
+ case Builtin::BI__builtin_uaddll_nuw:
+ NUW = true;
+ break;
+ case Builtin::BI__builtin_sadd_nsw:
+ case Builtin::BI__builtin_saddl_nsw:
+ case Builtin::BI__builtin_saddll_nsw:
+ case Builtin::BI__builtin_uadd_nsw:
+ case Builtin::BI__builtin_uaddl_nsw:
+ case Builtin::BI__builtin_uaddll_nsw:
+ NSW = true;
+ break;
+ default:
+ NUW = NSW = true;
+ break;
+ }
+ 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/test/CodeGen/builtins-int-wrap.c b/clang/test/CodeGen/builtins-int-wrap.c
new file mode 100644
index 0000000000000..16eb92681fcc1
--- /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_sadd_nuw(int x, int y) { return __builtin_sadd_nuw(x, y); }
+// CHECK-LABEL: @test_sadd_nuw
+// CHECK: [[RV:%.+]] = add nuw i32
+// CHECK: ret i32 [[RV]]
+
+int test_sadd_nsw(int x, int y) { return __builtin_sadd_nsw(x, y); }
+// CHECK-LABEL: @test_sadd_nsw
+// CHECK: [[RV:%.+]] = add nsw i32
+// CHECK: ret i32 [[RV]]
+
+int test_sadd_nuw_nsw(int x, int y) { return __builtin_sadd_nuw_nsw(x, y); }
+// CHECK-LABEL: @test_sadd_nuw_nsw
+// CHECK: [[RV:%.+]] = add nuw nsw i32
+// CHECK: ret i32 [[RV]]
+
+//------------------------------------------------------------------------------
+// long int
+//------------------------------------------------------------------------------
+long int test_saddl_nuw(long int x, long int y) { return __builtin_saddl_nuw(x, y); }
+// CHECK-LABEL: @test_saddl_nuw
+// CHECK: [[RV:%.+]] = add nuw i64
+// CHECK: ret i64 [[RV]]
+
+long int test_saddl_nsw(long int x, long int y) { return __builtin_saddl_nsw(x, y); }
+// CHECK-LABEL: @test_saddl_nsw
+// CHECK: [[RV:%.+]] = add nsw i64
+// CHECK: ret i64 [[RV]]
+
+long int test_saddl_nuw_nsw(long int x, long int y) { return __builtin_saddl_nuw_nsw(x, y); }
+// CHECK-LABEL: @test_saddl_nuw_nsw
+// CHECK: [[RV:%.+]] = add nuw nsw i64
+// CHECK: ret i64 [[RV]]
+
+//------------------------------------------------------------------------------
+// long int
+//------------------------------------------------------------------------------
+long long int test_saddll_nuw(long long int x, long long int y) { return __builtin_saddll_nuw(x, y); }
+// CHECK-LABEL: @test_saddll_nuw
+// CHECK: [[RV:%.+]] = add nuw i64
+// CHECK: ret i64 [[RV]]
+
+long long int test_saddll_nsw(long long int x, long long int y) { return __builtin_saddll_nsw(x, y); }
+// CHECK-LABEL: @test_saddll_nsw
+// CHECK: [[RV:%.+]] = add nsw i64
+// CHECK: ret i64 [[RV]]
+
+long long int test_saddll_nuw_nsw(long long int x, long long int y) { return __builtin_saddll_nuw_nsw(x, y); }
+// CHECK-LABEL: @test_saddll_nuw_nsw
+// CHECK: [[RV:%.+]] = add nuw nsw i64
+// CHECK: ret i64 [[RV]]
+
+//------------------------------------------------------------------------------
+// unsigned int
+//------------------------------------------------------------------------------
+unsigned int test_uadd_nuw(unsigned int x, unsigned int y) { return __builtin_uadd_nuw(x, y); }
+// CHECK-LABEL: @test_uadd_nuw
+// CHECK: [[RV:%.+]] = add nuw i32
+// CHECK: ret i32 [[RV]]
+
+unsigned int test_uadd_nsw(unsigned int x, unsigned int y) { return __builtin_uadd_nsw(x, y); }
+// CHECK-LABEL: @test_uadd_nsw
+// CHECK: [[RV:%.+]] = add nsw i32
+// CHECK: ret i32 [[RV]]
+
+unsigned int test_uadd_nuw_nsw(unsigned int x, unsigned int y) { return __builtin_uadd_nuw_nsw(x, y); }
+// CHECK-LABEL: @test_uadd_nuw_nsw
+// CHECK: [[RV:%.+]] = add nuw nsw i32
+// CHECK: ret i32 [[RV]]
+
+//------------------------------------------------------------------------------
+// unsigned long int
+//------------------------------------------------------------------------------
+unsigned long int test_uaddl_nuw(unsigned long int x, unsigned long int y) { return __builtin_uaddl_nuw(x, y); }
+// CHECK-LABEL: @test_uaddl_nuw
+// CHECK: [[RV:%.+]] = add nuw i64
+// CHECK: ret i64 [[RV]]
+
+unsigned long int test_uaddl_nsw(unsigned long int x, unsigned long int y) { return __builtin_uaddl_nsw(x, y); }
+// CHECK-LABEL: @test_uaddl_nsw
+// CHECK: [[RV:%.+]] = add nsw i64
+// CHECK: ret i64 [[RV]]
+
+unsigned long int test_uaddl_nuw_nsw(unsigned long int x, unsigned long int y) { return __builtin_uaddl_nuw_nsw(x, y); }
+// CHECK-LABEL: @test_uaddl_nuw_nsw
+// CHECK: [[RV:%.+]] = add nuw nsw i64
+// CHECK: ret i64 [[RV]]
+
+//------------------------------------------------------------------------------
+// unsigned long long int
+//------------------------------------------------------------------------------
+unsigned long long int test_uaddll_nuw(unsigned long long int x, unsigned long long int y) { return __builtin_uaddll_nuw(x, y); }
+// CHECK-LABEL: @test_uaddll_nuw
+// CHECK: [[RV:%.+]] = add nuw i64
+// CHECK: ret i64 [[RV]]
+
+unsigned long long int test_uaddll_nsw(unsigned long long int x, unsigned long long int y) { return __builtin_uaddll_nsw(x, y); }
+// CHECK-LABEL: @test_uaddll_nsw
+// CHECK: [[RV:%.+]] = add nsw i64
+// CHECK: ret i64 [[RV]]
+
+unsigned long long int test_uaddll_nuw_nsw(unsigned long long int x, unsigned long long int y) { return __builtin_uaddll_nuw_nsw(x, y); }
+// CHECK-LABEL: @test_uaddll_nuw_nsw
+// CHECK: [[RV:%.+]] = add nuw nsw i64
+// CHECK: ret i64 [[RV]]
|
| def SAddNUW : Builtin, SNUWTemplate { | ||
| let Spellings = ["__builtin_sadd"]; | ||
| let Attributes = [NoThrow]; | ||
| let Prototype = "T(T const, T const)"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let Prototype = "T(T const, T const)"; | |
| let Prototype = "T(T, T)"; |
These don't do anything anyways, so we might as well drop them.
| let Prototype = "void(...)"; | ||
| } | ||
|
|
||
| class SNUWTemplate : |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just make these builtins generic instead? There are like a dozen builtin families that first only got specific versions for different types and in the end got a generic version, because the generic version is just so much more useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't see any infrastructure to handle the varying return type. How would I express that the return type is the same as the first argument type?
Never mind. I think I figured it out. Working on updated patch with generic builtin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Latest revision reworks the builtins as generic.
|
|
||
| class SNUWTemplate : | ||
| Template<["int", "long int", "long long int"], | ||
| ["_nuw", "l_nuw", "ll_nuw"]>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a huge fan of nuw and nsw, since they don't tell me anything without having to look up what they stand for. It's also not clear to me how signed vs. unsigned wrapping is relevant when you only have either signed or unsigned types, but never a combination.
I'd be much happier with a __buitin_non_wrapping_add or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intent is to map directly to llvm IR in order to give library writers full control of the generated add instruction.
So IMO deviating from the IR terminology would only add confusion.
Similarly, I think making assumptions or restrictions about when nuw and nsw are applicable might frustrate users who, for whatever reason, just want clang to generate some specific add instruction.
|
Needs RFC; I'm not sure what the motivation is here, and this interacts closely with https://discourse.llvm.org/t/rfc-clang-canonical-wrapping-and-non-wrapping-types/84356/ |
We are trying to resolve a specific optimization problem where code like this trips up ScalarEvolution because it thinks the add might wrap, even though we have knowledge that it will not.
Does the above change your thoughts on this? I think this is solving a very different problem, is much more surgical in nature, and could coexist with such a feature without confusion/harm. |
Rework non-wrapping add builtins to be generic
57c81a2 to
58f679c
Compare
To be used for OpenMP runtime library development where lack of control over nuw/nsw prevents analysis by ScalarEvolution and subsequently loop unrolling.