llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Kees Cook (kees)

<details>
<summary>Changes</summary>

Provide a way to introspect expressions to see if they are assignable, which 
becomes very useful in macros that want to perform additional work on arguments 
that are lvalues. GCC is adding this builtin as well: 
https://forge.sourceware.org/pinskia/gcc-TEST/commits/branch/is_lvalue

---
Full diff: https://github.com/llvm/llvm-project/pull/132524.diff


8 Files Affected:

- (modified) clang/docs/LanguageExtensions.rst (+41) 
- (modified) clang/docs/ReleaseNotes.rst (+1) 
- (modified) clang/include/clang/Basic/Builtins.td (+7) 
- (modified) clang/lib/AST/Decl.cpp (+4) 
- (modified) clang/lib/AST/ExprConstant.cpp (+10) 
- (modified) clang/lib/Sema/SemaChecking.cpp (+1) 
- (added) clang/test/CodeGen/builtin-is-modifiable-lvalue.c (+50) 
- (added) clang/test/Sema/builtin-is-modifiable-lvalue.c (+17) 


``````````diff
diff --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index d4771775c9739..a45e246a22725 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -4067,6 +4067,47 @@ assignment can happen automatically.
 to a variable, have its address taken, or passed into or returned from a
 function, because doing so violates bounds safety conventions.
 
+``__builtin_is_modifiable_lvalue``
+----------------------------------
+
+``__builtin_is_modifiable_lvalue`` returns true when the argument is an 
assignable lvalue.
+
+The argument does not have any side-effects resolved.
+
+**Syntax**:
+
+.. code-block:: c
+
+  bool __builtin_is_modifiable_lvalue(T expr)
+
+**Examples**:
+
+.. code-block:: c
+
+  #define __force_lvalue_expr(x)  \
+          __builtin_choose_expr(__builtin_is_modifiable_lvalue(x), \
+                                x, (void *){ NULL })
+
+  #define __free_and_null(__do_free, x)   \
+  ({                                      \
+          typeof(x) *__ptr = &(x);        \
+          __do_free(*__ptr);              \
+          *__ptr = NULL;                  \
+  })
+  #define __free_and_maybe_null(__how, x)                          \
+          __builtin_choose_expr(__builtin_is_modifiable_lvalue(x), \
+                  __free_and_null(__how, __force_lvalue_expr(x)),  \
+                  __kfree(x))
+  #define kfree(x)    __free_and_maybe_null(__kfree, x)
+
+**Description**:
+
+When building complex preprocessor macros, it can be helpful to be able
+to determine if a given argument is an lvalue to make choices about
+how to resolve the given arguments. For example, performing assignments
+against an lvalue in a macro becomes possible, but values returned from
+function calls can be ignored.
+
 Multiprecision Arithmetic Builtins
 ----------------------------------
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 159991e8db981..d43aa2e481b6a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -158,6 +158,7 @@ Non-comprehensive list of changes in this release
 
 - Support parsing the `cc` operand modifier and alias it to the `c` modifier 
(#GH127719).
 - Added `__builtin_elementwise_exp10`.
+- Added `__builtin_is_modifiable_lvalue` to identify assignable arguments in 
macros.
 
 New Compiler Flags
 ------------------
diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 72a5e495c4059..b4c979d1267b3 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -964,6 +964,13 @@ def IsConstantEvaluated : LangBuiltin<"CXX_LANG"> {
   let Prototype = "bool()";
 }
 
+def IsLValue : Builtin {
+  let Spellings = ["__builtin_is_modifiable_lvalue"];
+  let Attributes = [NoThrow, CustomTypeChecking, UnevaluatedArguments,
+                    Constexpr];
+  let Prototype = "bool(...)";
+}
+
 def IsWithinLifetime : LangBuiltin<"CXX_LANG"> {
   let Spellings = ["__builtin_is_within_lifetime"];
   let Attributes = [NoThrow, CustomTypeChecking, Consteval];
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index e8aeacf24374f..8ea40aacea3a3 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3676,6 +3676,10 @@ unsigned FunctionDecl::getBuiltinID(bool 
ConsiderWrapperFunctions) const {
       BuiltinID == Builtin::BI__builtin_counted_by_ref)
     return 0;
 
+  if (getASTContext().getLangOpts().CPlusPlus &&
+      BuiltinID == Builtin::BI__builtin_is_modifiable_lvalue)
+    return 0;
+
   const ASTContext &Context = getASTContext();
   if (!Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID))
     return BuiltinID;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 0165a0a3b0df3..0f48f09a70412 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -12985,6 +12985,16 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const 
CallExpr *E,
     assert(Src.isInt());
     return Success((Src.getInt() & (Alignment - 1)) == 0 ? 1 : 0, E);
   }
+  case Builtin::BI__builtin_is_modifiable_lvalue: {
+    const Expr *Arg = E->getArg(0);
+    SpeculativeEvaluationRAII SpeculativeEval(Info);
+    IgnoreSideEffectsRAII Fold(Info);
+
+    SourceLocation OrigLoc = Arg->getExprLoc();
+    bool IsLValue = Arg->IgnoreCasts()->isModifiableLvalue(
+                        Info.Ctx, &OrigLoc) == Expr::MLV_Valid;
+    return Success(IsLValue, E);
+  }
   case Builtin::BI__builtin_align_up: {
     APValue Src;
     APSInt Alignment;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 12a8894cc7f47..6551052a8539a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2325,6 +2325,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, 
unsigned BuiltinID,
     if (BuiltinComplex(TheCall))
       return ExprError();
     break;
+  case Builtin::BI__builtin_is_modifiable_lvalue:
   case Builtin::BI__builtin_constant_p: {
     if (checkArgCount(TheCall, 1))
       return true;
diff --git a/clang/test/CodeGen/builtin-is-modifiable-lvalue.c 
b/clang/test/CodeGen/builtin-is-modifiable-lvalue.c
new file mode 100644
index 0000000000000..3e6e0c8106a07
--- /dev/null
+++ b/clang/test/CodeGen/builtin-is-modifiable-lvalue.c
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm < %s| FileCheck %s
+
+void report(int value);
+
+__attribute__((__noinline__)) int passthru(int a)
+{
+  return a;
+}
+
+int global;
+const int global_ro;
+
+int checkme(int arg, const int arg_ro)
+{
+  int autovar = 7;
+
+  // CHECK: call void @report(i32 noundef 0)
+  report(__builtin_is_modifiable_lvalue(5));
+  // CHECK: call void @report(i32 noundef 0)
+  report(__builtin_is_modifiable_lvalue(checkme));
+  // CHECK: call void @report(i32 noundef 1)
+  report(__builtin_is_modifiable_lvalue(arg));
+  // CHECK: call void @report(i32 noundef 0)
+  report(__builtin_is_modifiable_lvalue(arg + 5));
+  // CHECK: call void @report(i32 noundef 0)
+  report(__builtin_is_modifiable_lvalue(arg_ro));
+  // CHECK: call void @report(i32 noundef 1)
+  report(__builtin_is_modifiable_lvalue(autovar));
+  // CHECK: call void @report(i32 noundef 1)
+  report(__builtin_is_modifiable_lvalue(global));
+  // CHECK: call void @report(i32 noundef 0)
+  report(__builtin_is_modifiable_lvalue(global_ro));
+  // CHECK: call void @report(i32 noundef 1)
+  report(__builtin_is_modifiable_lvalue((unsigned char)arg));
+  // CHECK: call void @report(i32 noundef 0)
+  report(__builtin_is_modifiable_lvalue(passthru(arg)));
+  // CHECK: call void @report(i32 noundef 0)
+  report(__builtin_is_modifiable_lvalue(""));
+  // CHECK: load
+  arg++;
+  // CHECK: call void @report(i32 noundef 0)
+  // CHECK-NOT: load
+  report(__builtin_is_modifiable_lvalue(arg++));
+  // CHECK: call void @report(i32 noundef 0)
+  // CHECK-NOT: load
+  report(__builtin_is_modifiable_lvalue(++arg));
+
+  // CHECK: load
+  return arg;
+}
diff --git a/clang/test/Sema/builtin-is-modifiable-lvalue.c 
b/clang/test/Sema/builtin-is-modifiable-lvalue.c
new file mode 100644
index 0000000000000..acf8bb6604413
--- /dev/null
+++ b/clang/test/Sema/builtin-is-modifiable-lvalue.c
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c99 -fsyntax-only -verify %s
+
+int test0(void *ptr) {
+  return __builtin_is_modifiable_lvalue();      // expected-error {{too few 
arguments to function call, expected 1, have 0}}
+}
+
+int test1(void *ptr) {
+  return __builtin_is_modifiable_lvalue(ptr);   // ok
+}
+
+int test2(void *ptr) {
+  return __builtin_is_modifiable_lvalue(ptr, 5); // expected-error {{too many 
arguments to function call, expected 1, have 2}}
+}
+
+int test_trash(void *ptr) {
+  return __builtin_is_modifiable_lvalue(trash);         // expected-error 
{{use of undeclared identifier}}
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/132524
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to