https://github.com/AnastasiyaChernikova created 
https://github.com/llvm/llvm-project/pull/173394

This commit introduces initial frontend support for the C restrict type 
qualifier in Clang. The implementation adds infrastructure to generate metadata 
for restrict-qualified pointers, enabling potential alias analysis and 
optimization opportunities. Key changes include:

    A new compiler flag -frestrict-experimental to enable the feature (disabled 
by default).

    Support for generating LLVM metadata (!scope) that encodes restrict 
qualification levels and unique identifiers for restrict pointers.

    Recursive handling of restrict qualifiers in nested pointer types.

    Scope-aware metadata generation that tracks variables across different 
lexical scopes.

    The feature is currently limited to pure C code (not C++ or Objective-C).

The metadata attached to allocations provides information about which pointer 
levels are restrict-qualified, allowing downstream passes to reason about 
pointer aliasing.

>From b0dc79f9a746e6a21be776745d665ad39e7d504b Mon Sep 17 00:00:00 2001
From: AnastasiyaChernikova <[email protected]>
Date: Tue, 23 Dec 2025 16:54:22 +0000
Subject: [PATCH] [clang] Initial frontend C-restrict support

This commit introduces initial frontend support for the C restrict type 
qualifier in Clang. The implementation adds infrastructure to generate metadata 
for restrict-qualified pointers, enabling potential alias analysis and 
optimization opportunities.
Key changes include:

    A new compiler flag -frestrict-experimental to enable the feature (disabled 
by default).

    Support for generating LLVM metadata (!scope) that encodes restrict 
qualification levels and unique identifiers for restrict pointers.

    Recursive handling of restrict qualifiers in nested pointer types.

    Scope-aware metadata generation that tracks variables across different 
lexical scopes.

    The feature is currently limited to pure C code (not C++ or Objective-C).

The metadata attached to allocations provides information about which pointer 
levels are restrict-qualified, allowing downstream passes to reason about 
pointer aliasing.
---
 clang/include/clang/Basic/CodeGenOptions.def  |   3 +
 clang/include/clang/Basic/LangOptions.h       |   4 +
 clang/include/clang/Options/Options.td        |   6 +
 clang/lib/CodeGen/CGDecl.cpp                  | 133 +++++++-
 clang/lib/CodeGen/CGStmt.cpp                  |  57 ++++
 clang/lib/CodeGen/CodeGenFunction.h           |  20 ++
 clang/test/CodeGen/restrict/basic_restrict.c  | 284 ++++++++++++++++++
 clang/test/CodeGen/restrict/restrict_scopes.c | 120 ++++++++
 llvm/include/llvm/IR/FixedMetadataKinds.def   |   1 +
 llvm/include/llvm/IR/Intrinsics.td            |   3 +
 10 files changed, 630 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CodeGen/restrict/basic_restrict.c
 create mode 100644 clang/test/CodeGen/restrict/restrict_scopes.c

diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index a059803c433e3..22fad341b0fff 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -477,6 +477,9 @@ ENUM_CODEGENOPT(ZeroCallUsedRegs, ZeroCallUsedRegsKind,
 /// non-deleting destructors. (No effect on Microsoft ABI.)
 CODEGENOPT(CtorDtorReturnThis, 1, 0, Benign)
 
+/// Whether we should support restrict.
+CODEGENOPT(RestrictExperimental, 1, 0, Benign)
+
 /// Enables emitting Import Call sections on supported targets that can be used
 /// by the Windows kernel to enable import call optimization.
 CODEGENOPT(ImportCallOptimization, 1, 0, Benign)
diff --git a/clang/include/clang/Basic/LangOptions.h 
b/clang/include/clang/Basic/LangOptions.h
index 4fa2dcffc75b6..dc47e898d4e7e 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -742,6 +742,10 @@ class LangOptions : public LangOptionsBase {
            DefaultVisiblityExportMapping::All;
   }
 
+  bool IsCLanguageOnly() const {
+    return ((C99 || C11 || C17 || C23 || C2y) && !ObjC && !OpenMP);
+  }
+
   bool hasGlobalAllocationFunctionVisibility() const {
     return getGlobalAllocationFunctionVisibility() !=
            VisibilityForcedKinds::Source;
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 8cd31a3be109a..1504b66796b87 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2415,6 +2415,12 @@ def fmemory_profile_use_EQ : Joined<["-"], 
"fmemory-profile-use=">,
 // modes.
 let Visibility = [ClangOption, CC1Option, CLOption] in {
 
+defm restrict_experimental: BoolFOption<"restrict-experimental",
+  CodeGenOpts<"RestrictExperimental">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
+  NegFlag<SetFalse, [], [ClangOption, CC1Option], "Disable">,
+  BothFlags<[], [ClangOption, CC1Option], " restrict experimental support.">>;
+
 def fsanitize_EQ : CommaJoined<["-"], "fsanitize=">, Group<f_clang_Group>,
                    MetaVarName<"<check>">,
                    HelpText<"Turn on runtime checks for various forms of 
undefined "
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8b1cd83af2396..87eb93b7bd50d 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1476,6 +1476,127 @@ static bool shouldExtendLifetime(const ASTContext 
&Context,
   return true;
 }
 
+/// Adds metadata for restrict-qualified pointers at any nesting level.
+///
+/// This function processes pointer types recursively to generate metadata
+/// for restrict-qualified pointers. The metadata encodes which pointer levels
+/// are restrict qualified and assigns unique identifiers to each restrict
+/// qualification level.
+///
+/// For example, consider the type `int * restrict * restrict` in foo():
+///   - First level: pointer to restrict pointer (gets unique code 1)
+///   - Second level: restrict pointer to int (gets another unique code 1)
+///
+/// The metadata structure is attached to the alloca instruction and consists
+/// of:
+///   1. Full variable name (function name + scope encoding)
+///   2. List of restrict codes for each pointer level:
+///      - Non-zero: unique identifier for restrict-qualified pointer
+///      - Zero: non-restrict qualified pointer at that level
+///
+/// For example, consider function `foo` with variable declarations:
+///   void foo() {
+///     int * restrict p1;       // Single-level restrict pointer
+///     int * restrict *p2;      // restrict at first level, non-restrict at
+///     second int * restrict * restrict p3;  // restrict at both levels
+///   }
+///
+/// Generated metadata would be:
+///   p1: !{!"foo_1", !{1}}          // One restrict level, code 1
+///   p2: !{!"foo_1", !{2, 0}}       // First level restrict (1), second
+///   non-restrict (0) p3: !{!"foo_1", !{3, 1}}       // First level restrict
+///   (1), second restrict (2)
+///
+/// The unique codes allow distinguishing different restrict pointers at the
+/// same nesting level within the same function and scope.
+///
+void CodeGenFunction::AddPointerMetodataForRestrict(RawAddress AllocaAddr,
+                                                    QualType Ty,
+                                                    llvm::StringRef FullName) {
+  auto *AllocaInst = cast<llvm::AllocaInst>(AllocaAddr.getPointer());
+
+  unsigned RestictNesting = 0;
+
+  llvm::SmallVector<llvm::Metadata *, 8> RestrictMDs;
+  llvm::LLVMContext &Ctx = AllocaInst->getContext();
+
+  while (const PointerType *PT = Ty->getAs<PointerType>()) {
+    if (Ty.isRestrictQualified()) {
+      if (RestictNesting >= RestrictCodes.size())
+        RestrictCodes.push_back(1);
+      unsigned Code = RestrictCodes[RestictNesting];
+      llvm::Constant *C =
+          llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), Code);
+      RestrictMDs.push_back(llvm::ConstantAsMetadata::get(C));
+      RestrictCodes[RestictNesting]++;
+    } else {
+      if (PT->getPointeeType()->getAs<RecordType>())
+        AddMetodataForRestrict(AllocaAddr, PT->getPointeeType(), FullName);
+      llvm::Constant *C =
+          llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), 0);
+      RestrictMDs.push_back(llvm::ConstantAsMetadata::get(C));
+    }
+    RestictNesting++;
+    Ty = PT->getPointeeType();
+  }
+  llvm::MDNode *RestrictList = llvm::MDNode::get(Ctx, RestrictMDs);
+  llvm::MDString *NameMD = llvm::MDString::get(Ctx, FullName.str());
+  llvm::MDNode *Node = llvm::MDNode::get(Ctx, {NameMD, RestrictList});
+  AllocaInst->setMetadata(llvm::LLVMContext::MD_scope, Node);
+}
+
+bool CodeGenFunction::IsRestrictExperimentalSupportEnabled() const {
+  return (getLangOpts().IsCLanguageOnly() &&
+          (CGM.getCodeGenOpts().RestrictExperimental));
+}
+
+/// Constructs metadata for variables in the current lexical scope.
+///
+/// This function builds a unique name for each variable by combining the
+/// function name with the current scope encoding, then delegates to
+/// AddMetodataForRestrict to generate actual restrict metadata.
+///
+/// Example with nested scopes in function `foo`:
+///   void foo() {           // Scope encoding: "1"
+///     int *a;              // Full name: foo_1
+///     {                    // Scope encoding: "10"
+///       int *b;            // Full name: foo_10
+///       {                  // Scope encoding: "100"
+///         int *c;          // Full name: foo_100
+///       }
+///     }
+///     {                    // Scope encoding: "101"
+///       int *d;            // Full name: foo_101
+///     }
+///   }
+///
+/// The scope encoding follows a hierarchical pattern:
+///   - Function body: "1"
+///   - First child scope: "10"
+///   - Second child scope: "101"
+///   - Child of "10": "100"
+///
+/// This ensures each variable gets a unique identifier even
+/// if they have the same name in different scopes.
+///
+void CodeGenFunction::ConstructMetodataForScope(RawAddress AllocaAddr,
+                                                QualType Ty,
+                                                llvm::StringRef CurScopeCode) {
+  assert(CurFuncDecl != nullptr);
+
+  llvm::SmallString<32> FullName(
+      CurFuncDecl->getAsFunction()->getNameAsString().append("_"));
+  FullName.append(CurScopeCode);
+  AddMetodataForRestrict(AllocaAddr, Ty, FullName.str());
+}
+
+void CodeGenFunction::AddMetodataForRestrict(RawAddress AllocaAddr, QualType 
Ty,
+                                             llvm::StringRef FullName) {
+  if (!IsRestrictExperimentalSupportEnabled() || !Ty->getAs<PointerType>())
+    return;
+  AddPointerMetodataForRestrict(AllocaAddr, Ty, FullName);
+}
+
 /// EmitAutoVarAlloca - Emit the alloca and debug information for a
 /// local variable.  Does not emit initialization or destruction.
 CodeGenFunction::AutoVarEmission
@@ -1604,6 +1725,11 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
                                  allocaAlignment, D.getName(),
                                  /*ArraySize=*/nullptr, &AllocaAddr);
 
+      if (IsRestrictExperimentalSupportEnabled()) {
+        llvm::StringRef CurScopeCode = ScopeCodes.back().first;
+        ConstructMetodataForScope(AllocaAddr, Ty, CurScopeCode);
+      }
+
       // Don't emit lifetime markers for MSVC catch parameters. The lifetime of
       // the catch parameter starts in the catchpad instruction, and we can't
       // insert code in those basic blocks.
@@ -2700,8 +2826,11 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, 
ParamValue Arg,
     // For truly ABI indirect arguments -- those that are not `byval` -- store
     // the address of the argument on the stack to preserve debug information.
     ABIArgInfo ArgInfo = CurFnInfo->arguments()[ArgNo - 1].info;
-    if (ArgInfo.isIndirect())
+    if (ArgInfo.isIndirect()) {
       UseIndirectDebugAddress = !ArgInfo.getIndirectByVal();
+      if (IsRestrictExperimentalSupportEnabled())
+        ConstructMetodataForScope(AllocaPtr, Ty);
+    }
     if (UseIndirectDebugAddress) {
       auto PtrTy = getContext().getPointerType(Ty);
       AllocaPtr = CreateMemTemp(PtrTy, getContext().getTypeAlignInChars(PtrTy),
@@ -2750,6 +2879,8 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, 
ParamValue Arg,
       // Otherwise, create a temporary to hold the value.
       DeclPtr = CreateMemTemp(Ty, getContext().getDeclAlign(&D),
                               D.getName() + ".addr", &AllocaPtr);
+      if (IsRestrictExperimentalSupportEnabled())
+        ConstructMetodataForScope(AllocaPtr, Ty);
     }
     DoStore = true;
   }
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index c050fd41ac0e9..59f493128cf5b 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -581,11 +581,67 @@ Address CodeGenFunction::EmitCompoundStmt(const 
CompoundStmt &S, bool GetLast,
   return EmitCompoundStmtWithoutScope(S, GetLast, AggSlot);
 }
 
+/// Generates unique encoding for compound statement scopes.
+///
+/// This function creates a hierarchical encoding scheme for lexical scopes
+/// to uniquely identify each compound statement's position in the AST.
+/// The encoding follows a pattern where:
+/// - The root scope (function body) is encoded as "1"
+/// - Child scopes append "0" followed by '1' characters representing
+///   the child's position among its siblings
+///
+/// Example scope hierarchy and encodings:
+///   Function foo body: "1"
+///     First child block: "10"
+///       First nested block: "100"
+///       Second nested block: "1001"
+///     Second child block: "101"
+///       First nested block: "1010"
+///
+///
+/// This encoding scheme ensures:
+///   1. Uniqueness: Each scope in the function has a distinct encoding
+///   2. Hierarchy: Parent-child relationships are preserved in the encoding
+///   3. Stability: The encoding doesn't change if unrelated scopes are added
+///
+/// The encoding is stored in ScopeCodes stack and used by
+/// ConstructMetodataForScope to generate unique names for restrict metadata.
+/// This prevents conflicts when the same variable name appears in different
+/// scopes.
+///
+void CodeGenFunction::EmitUniqueEncoding(const CompoundStmt &S) {
+  if (!IsRestrictExperimentalSupportEnabled())
+    return;
+  llvm::SmallString<32> CurCode;
+
+  if (ScopeCodes.empty()) {
+    CurCode = "1";
+  } else {
+    const CompoundStmt *Parent = ScopeCodes.back().second;
+    unsigned ChildIndex = ScopeChildCount[Parent]++;
+    CurCode = ScopeCodes.back().first;
+    CurCode += "0";
+    CurCode.append(ChildIndex, '1');
+  }
+
+  ScopeCodes.emplace_back(CurCode, &S);
+}
+
+void CodeGenFunction::FreeNodeUniqueEncoding() {
+
+  if (!IsRestrictExperimentalSupportEnabled())
+    return;
+  assert(!ScopeCodes.empty());
+  ScopeCodes.pop_back();
+}
+
 Address
 CodeGenFunction::EmitCompoundStmtWithoutScope(const CompoundStmt &S,
                                               bool GetLast,
                                               AggValueSlot AggSlot) {
 
+  EmitUniqueEncoding(S);
+
   for (CompoundStmt::const_body_iterator I = S.body_begin(),
                                          E = S.body_end() - GetLast;
        I != E; ++I)
@@ -627,6 +683,7 @@ CodeGenFunction::EmitCompoundStmtWithoutScope(const 
CompoundStmt &S,
                        /*IsInit*/ false);
     }
   }
+  FreeNodeUniqueEncoding();
 
   return RetAlloca;
 }
diff --git a/clang/lib/CodeGen/CodeGenFunction.h 
b/clang/lib/CodeGen/CodeGenFunction.h
index 855e43631f436..3228ab29c8c79 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -346,6 +346,11 @@ class CodeGenFunction : public CodeGenTypeCache {
   QualType FnRetTy;
   llvm::Function *CurFn = nullptr;
 
+  llvm::SmallVector<std::pair<llvm::SmallString<32>, const CompoundStmt *>, 8>
+      ScopeCodes;
+  llvm::DenseMap<const CompoundStmt *, unsigned> ScopeChildCount;
+  llvm::SmallVector<unsigned, 4> RestrictCodes;
+
   /// If a cast expression is being visited, this holds the current cast's
   /// expression.
   const CastExpr *CurCast = nullptr;
@@ -3418,6 +3423,17 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// This function can be called with a null (unreachable) insert point.
   void EmitAutoVarDecl(const VarDecl &D);
 
+  bool IsRestrictExperimentalSupportEnabled() const;
+
+  void ConstructMetodataForScope(RawAddress AllocaAddr, QualType Ty,
+                                 llvm::StringRef CurScopeCode = "1");
+
+  void AddMetodataForRestrict(RawAddress AllocaAddr, QualType Ty,
+                              llvm::StringRef FullName);
+
+  void AddPointerMetodataForRestrict(RawAddress AllocaAddr, QualType Ty,
+                                     llvm::StringRef FullName);
+
   class AutoVarEmission {
     friend class CodeGenFunction;
 
@@ -3597,6 +3613,10 @@ class CodeGenFunction : public CodeGenTypeCache {
 
   Address EmitCompoundStmt(const CompoundStmt &S, bool GetLast = false,
                            AggValueSlot AVS = AggValueSlot::ignored());
+
+  void EmitUniqueEncoding(const CompoundStmt &S);
+  void FreeNodeUniqueEncoding();
+
   Address
   EmitCompoundStmtWithoutScope(const CompoundStmt &S, bool GetLast = false,
                                AggValueSlot AVS = AggValueSlot::ignored());
diff --git a/clang/test/CodeGen/restrict/basic_restrict.c 
b/clang/test/CodeGen/restrict/basic_restrict.c
new file mode 100644
index 0000000000000..a7b312cb00635
--- /dev/null
+++ b/clang/test/CodeGen/restrict/basic_restrict.c
@@ -0,0 +1,284 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -disable-llvm-passes 
-frestrict-experimental -emit-llvm -o - %s | FileCheck %s
+
+#include <stdalign.h>
+
+// CHECK-LABEL: define dso_local void @test_basic_restrict(
+// CHECK:         [[P1_ADDR:%.*]] = alloca ptr, align 8, !scope 
[[META2:![0-9]+]]
+// CHECK-NEXT:    [[P2_ADDR:%.*]] = alloca ptr, align 8, !scope 
[[META4:![0-9]+]]
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca ptr, align 8, !scope 
[[META6:![0-9]+]]
+//
+void test_basic_restrict(int * restrict p1, int * restrict p2, int x[restrict 
5]) {
+}
+
+// CHECK-LABEL: define dso_local void @test_nested_restrict(
+// CHECK:         [[A:%.*]] = alloca ptr, align 8, !scope [[META13:![0-9]+]]
+// CHECK-NEXT:    [[B:%.*]] = alloca ptr, align 8, !scope [[META13]]
+// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8, !scope [[META13]]
+// CHECK-NEXT:    [[B_INT:%.*]] = alloca ptr, align 8, !scope 
[[META15:![0-9]+]]
+// CHECK-NEXT:    [[B_FLOAT:%.*]] = alloca ptr, align 8, !scope 
[[META16:![0-9]+]]
+// CHECK-NEXT:    [[I:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[C_DOUBLE:%.*]] = alloca ptr, align 8, !scope 
[[META17:![0-9]+]]
+// CHECK-NEXT:    [[C_LONG:%.*]] = alloca ptr, align 8, !scope 
[[META18:![0-9]+]]
+// CHECK-NEXT:    [[D_SHORT:%.*]] = alloca ptr, align 8, !scope 
[[META20:![0-9]+]]
+// CHECK-NEXT:    [[D_UNSIGNED:%.*]] = alloca ptr, align 8, !scope 
[[META22:![0-9]+]]
+// CHECK-NEXT:    [[E_STRUCT:%.*]] = alloca ptr, align 8, !scope 
[[META24:![0-9]+]]
+//
+void test_nested_restrict() {
+  int *a;
+  float *b;
+  char *c;
+
+  if (1) {
+    int * restrict b_int;
+    float * restrict b_float;
+  }
+
+  for (int i = 0; i < 10; i++) {
+    double * restrict c_double;
+    long * restrict c_long;
+  }
+
+  while (1) {
+    short * restrict d_short;
+    unsigned * restrict d_unsigned;
+    break;
+  }
+
+  {
+    struct Point {
+      int x;
+      int y;
+    } * restrict e_struct;
+  }
+}
+
+// CHECK-LABEL: define dso_local void @test_assignment(
+// CHECK:         [[R1_INT:%.*]] = alloca ptr, align 8, !scope 
[[META30:![0-9]+]]
+// CHECK-NEXT:    [[R2_FLOAT:%.*]] = alloca ptr, align 8, !scope 
[[META31:![0-9]+]]
+// CHECK-NEXT:    [[NORMAL_INT:%.*]] = alloca ptr, align 8, !scope 
[[META32:![0-9]+]]
+// CHECK-NEXT:    [[NORMAL_FLOAT:%.*]] = alloca ptr, align 8, !scope [[META32]]
+// CHECK-NEXT:    [[R3_INT:%.*]] = alloca ptr, align 8, !scope 
[[META33:![0-9]+]]
+// CHECK-NEXT:    [[R4_FLOAT:%.*]] = alloca ptr, align 8, !scope 
[[META34:![0-9]+]]
+//
+void test_assignment() {
+  int * restrict r1_int;
+  float * restrict r2_float;
+
+  {
+    int *normal_int;
+    float *normal_float;
+    normal_int = r1_int;
+    normal_float = r2_float;
+  }
+
+  {
+    int * restrict r3_int;
+    float * restrict r4_float;
+    r3_int = r1_int;
+    r4_float = r2_float;
+  }
+}
+
+// CHECK-LABEL: define dso_local void @test_mixed_qualifiers(
+// CHECK:         [[CONST_RESTRICT_INT:%.*]] = alloca ptr, align 8, !scope 
[[META37:![0-9]+]]
+// CHECK-NEXT:    [[CONST_RESTRICT_STRUCT:%.*]] = alloca ptr, align 8, !scope 
[[META38:![0-9]+]]
+// CHECK-NEXT:    [[VOLATILE_RESTRICT_INT:%.*]] = alloca ptr, align 8, !scope 
[[META39:![0-9]+]]
+// CHECK-NEXT:    [[CONST_VOLATILE_RESTRICT_INT:%.*]] = alloca ptr, align 8, 
!scope [[META40:![0-9]+]]
+// CHECK-NEXT:    [[VALUE:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[LOCAL_CONST_RESTRICT_FLOAT:%.*]] = alloca ptr, align 8, 
!scope [[META41:![0-9]+]]
+// CHECK-NEXT:    [[LOCAL_VOLATILE_RESTRICT_CHAR:%.*]] = alloca ptr, align 8, 
!scope [[META42:![0-9]+]]
+//
+void test_mixed_qualifiers() {
+    const int * restrict const_restrict_int;
+    const struct { int a; float b; } * restrict const_restrict_struct;
+
+    volatile int * restrict volatile_restrict_int;
+
+    const volatile int * restrict const_volatile_restrict_int;
+
+    int value = 42;
+    const_restrict_int = &value;
+
+    {
+        const float * restrict local_const_restrict_float;
+        volatile char * restrict local_volatile_restrict_char;
+    }
+}
+
+// CHECK-LABEL: define dso_local void @test_aligned_types(
+// CHECK:         [[ALIGNED_PTR:%.*]] = alloca ptr, align 16, !scope 
[[META43:![0-9]+]]
+// CHECK-NEXT:    [[ALIGNED_RESTRICT_PTR:%.*]] = alloca ptr, align 16, !scope 
[[META44:![0-9]+]]
+// CHECK-NEXT:    [[ALIGNED_STRUCT_RESTRICT:%.*]] = alloca ptr, align 64, 
!scope [[META45:![0-9]+]]
+//
+void test_aligned_types() {
+    alignas(16) int *aligned_ptr;
+    alignas(16) int * restrict aligned_restrict_ptr;
+
+    {
+        alignas(64) struct {
+            int a;
+            double b;
+        } * restrict aligned_struct_restrict;
+
+    }
+}
+
+// CHECK-LABEL: define dso_local void @test_loops(
+// CHECK:         [[I:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[P:%.*]] = alloca ptr, align 8, !scope [[META46:![0-9]+]]
+// CHECK-NEXT:    [[Q:%.*]] = alloca ptr, align 8, !scope [[META47:![0-9]+]]
+// CHECK-NEXT:    [[R:%.*]] = alloca ptr, align 8, !scope [[META48:![0-9]+]]
+// CHECK-NEXT:    [[COUNTER:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[W:%.*]] = alloca ptr, align 8, !scope [[META49:![0-9]+]]
+// CHECK-NEXT:    [[INNER:%.*]] = alloca ptr, align 8, !scope 
[[META50:![0-9]+]]
+// CHECK-NEXT:    [[D:%.*]] = alloca ptr, align 8, !scope [[META51:![0-9]+]]
+//
+void test_loops() {
+  for (int i = 0; i < 3; i++) {
+    int * restrict p;
+
+    if (i % 2 == 0) {
+      int *q;
+    } else {
+      int * restrict r;
+    }
+  }
+
+  int counter = 0;
+  while (counter < 2) {
+    int * restrict w;
+
+    {
+      int *inner;
+    }
+    counter++;
+  }
+
+  do {
+    int * restrict d;
+    counter--;
+  } while (counter > 0);
+}
+
+// CHECK-LABEL: define dso_local void @test_restrict_complex_pointers(
+// CHECK:         [[BASE:%.*]] = alloca ptr, align 8, !scope [[META55:![0-9]+]]
+// CHECK-NEXT:    [[NOT_POINTER:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[IF_RESTRICT:%.*]] = alloca ptr, align 8, !scope 
[[META57:![0-9]+]]
+// CHECK-NEXT:    [[IF_TRIPLE:%.*]] = alloca ptr, align 8, !scope 
[[META59:![0-9]+]]
+// CHECK-NEXT:    [[I:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[CLEANUP_DEST_SLOT:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[LOOP_RESTRICT:%.*]] = alloca ptr, align 8, !scope 
[[META61:![0-9]+]]
+// CHECK-NEXT:    [[LOOP_QUAD:%.*]] = alloca ptr, align 8, !scope 
[[META63:![0-9]+]]
+// CHECK-NEXT:    [[CASE_RESTRICT:%.*]] = alloca ptr, align 8, !scope 
[[META65:![0-9]+]]
+// CHECK-NEXT:    [[CASE_TRIPLE:%.*]] = alloca ptr, align 8, !scope 
[[META67:![0-9]+]]
+// CHECK-NEXT:    [[CASE_QUAD:%.*]] = alloca ptr, align 8, !scope 
[[META69:![0-9]+]]
+// CHECK-NEXT:    [[BLOCK_RESTRICT:%.*]] = alloca ptr, align 8, !scope 
[[META71:![0-9]+]]
+// CHECK-NEXT:    [[BLOCK_TRIPLE:%.*]] = alloca ptr, align 8, !scope 
[[META73:![0-9]+]]
+// CHECK-NEXT:    [[NESTED_RESTRICT:%.*]] = alloca ptr, align 8, !scope 
[[META75:![0-9]+]]
+// CHECK-NEXT:    [[NESTED_QUAD:%.*]] = alloca ptr, align 8, !scope 
[[META77:![0-9]+]]
+//
+void test_restrict_complex_pointers() {
+    int ** base;
+    int not_pointer;
+
+    if (1) {
+        int * restrict * restrict if_restrict;
+        int *** restrict if_triple;
+    }
+
+    for (int i = 0; i < 10; i++) {
+        int * restrict * restrict loop_restrict;
+        int **** restrict loop_quad;
+
+        switch (i % 3) {
+            case 0: {
+                int * restrict * restrict case_restrict;
+                break;
+            }
+            case 1: {
+                int *** restrict case_triple;
+                break;
+            }
+            case 2: {
+                int * restrict * restrict * case_quad;
+                break;
+            }
+        }
+    }
+
+    {
+        int * restrict * restrict block_restrict;
+        int *** restrict block_triple;
+
+        {
+            int * restrict * restrict * nested_restrict;
+            int **** restrict nested_quad;
+        }
+    }
+}
+
+//.
+// CHECK: [[META2]] = !{!"test_basic_restrict_1", [[META3:![0-9]+]]}
+// CHECK: [[META3]] = !{i64 1}
+// CHECK: [[META4]] = !{!"test_basic_restrict_1", [[META5:![0-9]+]]}
+// CHECK: [[META5]] = !{i64 2}
+// CHECK: [[META6]] = !{!"test_basic_restrict_1", [[META7:![0-9]+]]}
+// CHECK: [[META7]] = !{i64 3}
+// CHECK: [[META13]] = !{!"test_nested_restrict_1", [[META14:![0-9]+]]}
+// CHECK: [[META14]] = !{i64 0}
+// CHECK: [[META15]] = !{!"test_nested_restrict_10", [[META3]]}
+// CHECK: [[META16]] = !{!"test_nested_restrict_10", [[META5]]}
+// CHECK: [[META17]] = !{!"test_nested_restrict_101", [[META7]]}
+// CHECK: [[META18]] = !{!"test_nested_restrict_101", [[META19:![0-9]+]]}
+// CHECK: [[META19]] = !{i64 4}
+// CHECK: [[META20]] = !{!"test_nested_restrict_1011", [[META21:![0-9]+]]}
+// CHECK: [[META21]] = !{i64 5}
+// CHECK: [[META22]] = !{!"test_nested_restrict_1011", [[META23:![0-9]+]]}
+// CHECK: [[META23]] = !{i64 6}
+// CHECK: [[META24]] = !{!"test_nested_restrict_10111", [[META25:![0-9]+]]}
+// CHECK: [[META25]] = !{i64 7}
+// CHECK: [[META30]] = !{!"test_assignment_1", [[META3]]}
+// CHECK: [[META31]] = !{!"test_assignment_1", [[META5]]}
+// CHECK: [[META32]] = !{!"test_assignment_10", [[META14]]}
+// CHECK: [[META33]] = !{!"test_assignment_101", [[META7]]}
+// CHECK: [[META34]] = !{!"test_assignment_101", [[META19]]}
+// CHECK: [[META37]] = !{!"test_mixed_qualifiers_1", [[META3]]}
+// CHECK: [[META38]] = !{!"test_mixed_qualifiers_1", [[META5]]}
+// CHECK: [[META39]] = !{!"test_mixed_qualifiers_1", [[META7]]}
+// CHECK: [[META40]] = !{!"test_mixed_qualifiers_1", [[META19]]}
+// CHECK: [[META41]] = !{!"test_mixed_qualifiers_10", [[META21]]}
+// CHECK: [[META42]] = !{!"test_mixed_qualifiers_10", [[META23]]}
+// CHECK: [[META43]] = !{!"test_aligned_types_1", [[META14]]}
+// CHECK: [[META44]] = !{!"test_aligned_types_1", [[META3]]}
+// CHECK: [[META45]] = !{!"test_aligned_types_10", [[META5]]}
+// CHECK: [[META46]] = !{!"test_loops_10", [[META3]]}
+// CHECK: [[META47]] = !{!"test_loops_100", [[META14]]}
+// CHECK: [[META48]] = !{!"test_loops_1001", [[META5]]}
+// CHECK: [[META49]] = !{!"test_loops_101", [[META7]]}
+// CHECK: [[META50]] = !{!"test_loops_1010", [[META14]]}
+// CHECK: [[META51]] = !{!"test_loops_1011", [[META19]]}
+// CHECK: [[META55]] = !{!"test_restrict_complex_pointers_1", 
[[META56:![0-9]+]]}
+// CHECK: [[META56]] = !{i64 0, i64 0}
+// CHECK: [[META57]] = !{!"test_restrict_complex_pointers_10", 
[[META58:![0-9]+]]}
+// CHECK: [[META58]] = !{i64 1, i64 1}
+// CHECK: [[META59]] = !{!"test_restrict_complex_pointers_10", 
[[META60:![0-9]+]]}
+// CHECK: [[META60]] = !{i64 2, i64 0, i64 0}
+// CHECK: [[META61]] = !{!"test_restrict_complex_pointers_101", 
[[META62:![0-9]+]]}
+// CHECK: [[META62]] = !{i64 3, i64 2}
+// CHECK: [[META63]] = !{!"test_restrict_complex_pointers_101", 
[[META64:![0-9]+]]}
+// CHECK: [[META64]] = !{i64 4, i64 0, i64 0, i64 0}
+// CHECK: [[META65]] = !{!"test_restrict_complex_pointers_10100", 
[[META66:![0-9]+]]}
+// CHECK: [[META66]] = !{i64 5, i64 3}
+// CHECK: [[META67]] = !{!"test_restrict_complex_pointers_101001", 
[[META68:![0-9]+]]}
+// CHECK: [[META68]] = !{i64 6, i64 0, i64 0}
+// CHECK: [[META69]] = !{!"test_restrict_complex_pointers_1010011", 
[[META70:![0-9]+]]}
+// CHECK: [[META70]] = !{i64 0, i64 4, i64 1}
+// CHECK: [[META71]] = !{!"test_restrict_complex_pointers_1011", 
[[META72:![0-9]+]]}
+// CHECK: [[META72]] = !{i64 7, i64 5}
+// CHECK: [[META73]] = !{!"test_restrict_complex_pointers_1011", 
[[META74:![0-9]+]]}
+// CHECK: [[META74]] = !{i64 8, i64 0, i64 0}
+// CHECK: [[META75]] = !{!"test_restrict_complex_pointers_10110", 
[[META76:![0-9]+]]}
+// CHECK: [[META76]] = !{i64 0, i64 6, i64 2}
+// CHECK: [[META77]] = !{!"test_restrict_complex_pointers_10110", 
[[META78:![0-9]+]]}
+// CHECK: [[META78]] = !{i64 9, i64 0, i64 0, i64 0}
+//.
diff --git a/clang/test/CodeGen/restrict/restrict_scopes.c 
b/clang/test/CodeGen/restrict/restrict_scopes.c
new file mode 100644
index 0000000000000..65154ad28adee
--- /dev/null
+++ b/clang/test/CodeGen/restrict/restrict_scopes.c
@@ -0,0 +1,120 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -disable-llvm-passes 
-frestrict-experimental -emit-llvm -o - %s | FileCheck %s
+
+#include <stddef.h>
+
+struct ComplexStruct {
+    int id;
+    float data;
+    char *name;
+    struct ComplexStruct *next;
+};
+
+// CHECK-LABEL: define dso_local void @test_deep_nesting_mixed(
+// CHECK:         [[L1_INT:%.*]] = alloca ptr, align 8, !scope 
[[META2:![0-9]+]]
+// CHECK-NEXT:    [[L1_FLOAT:%.*]] = alloca ptr, align 8, !scope [[META2]]
+// CHECK-NEXT:    [[L1_STRUCT:%.*]] = alloca ptr, align 8, !scope [[META2]]
+// CHECK-NEXT:    [[L2_INT:%.*]] = alloca ptr, align 8, !scope 
[[META4:![0-9]+]]
+// CHECK-NEXT:    [[L2_FLOAT:%.*]] = alloca ptr, align 8, !scope 
[[META6:![0-9]+]]
+// CHECK-NEXT:    [[L2_STRUCT:%.*]] = alloca ptr, align 8, !scope 
[[META8:![0-9]+]]
+// CHECK-NEXT:    [[I:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[CLEANUP_DEST_SLOT:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[L3_DOUBLE:%.*]] = alloca ptr, align 8, !scope 
[[META10:![0-9]+]]
+// CHECK-NEXT:    [[L3_LONG_RESTRICT:%.*]] = alloca ptr, align 8, !scope 
[[META11:![0-9]+]]
+// CHECK-NEXT:    [[L3_COMPLEX:%.*]] = alloca ptr, align 8, !scope [[META10]]
+// CHECK-NEXT:    [[L4_CHAR_RESTRICT:%.*]] = alloca ptr, align 8, !scope 
[[META13:![0-9]+]]
+// CHECK-NEXT:    [[L4_USHORT:%.*]] = alloca ptr, align 8, !scope 
[[META15:![0-9]+]]
+// CHECK-NEXT:    [[L4_STRUCT_RESTRICT:%.*]] = alloca ptr, align 8, !scope 
[[META16:![0-9]+]]
+// CHECK-NEXT:    [[L5_INT_RESTRICT:%.*]] = alloca ptr, align 8, !scope 
[[META18:![0-9]+]]
+// CHECK-NEXT:    [[L5_FLOAT:%.*]] = alloca ptr, align 8, !scope 
[[META20:![0-9]+]]
+//
+void test_deep_nesting_mixed() {
+    int *l1_int;
+    float *l1_float;
+    struct ComplexStruct *l1_struct;
+
+
+    if (1) {
+        int * restrict l2_int;
+        float * restrict l2_float;
+        struct ComplexStruct * restrict l2_struct;
+
+        for (int i = 0; i < 5; i++) {
+            double *l3_double;
+            long * restrict l3_long_restrict;
+            struct ComplexStruct *l3_complex;
+            {
+                char * restrict l4_char_restrict;
+                unsigned short *l4_ushort;
+                struct ComplexStruct * restrict l4_struct_restrict;
+
+                switch (i) {
+                    case 0: {
+                        int * restrict l5_int_restrict;
+                        break;
+                    }
+                    default: {
+                        float *l5_float;
+                    }
+                }
+            }
+        }
+    }
+}
+
+// CHECK-LABEL: define dso_local void @test_same_name_different_types(
+// CHECK:         [[PTR:%.*]] = alloca ptr, align 8, !scope [[META27:![0-9]+]]
+// CHECK-NEXT:    [[PTR1:%.*]] = alloca ptr, align 8, !scope [[META28:![0-9]+]]
+// CHECK-NEXT:    [[PTR2:%.*]] = alloca ptr, align 8, !scope [[META29:![0-9]+]]
+// CHECK-NEXT:    [[PTR3:%.*]] = alloca ptr, align 8, !scope [[META30:![0-9]+]]
+// CHECK-NEXT:    [[PTR4:%.*]] = alloca ptr, align 8, !scope [[META31:![0-9]+]]
+// CHECK-NEXT:    [[PTR5:%.*]] = alloca ptr, align 8, !scope [[META32:![0-9]+]]
+//
+void test_same_name_different_types() {
+    int *ptr;
+    {
+        float * restrict ptr;
+
+        {
+            char *ptr;
+        }
+    }
+
+    {
+        struct { int val; } * restrict ptr;
+    }
+
+    {
+        double *ptr;
+        {
+            long * restrict ptr;
+        }
+    }
+}
+
+//.
+// CHECK: [[META2]] = !{!"test_deep_nesting_mixed_1", [[META3:![0-9]+]]}
+// CHECK: [[META3]] = !{i64 0}
+// CHECK: [[META4]] = !{!"test_deep_nesting_mixed_10", [[META5:![0-9]+]]}
+// CHECK: [[META5]] = !{i64 1}
+// CHECK: [[META6]] = !{!"test_deep_nesting_mixed_10", [[META7:![0-9]+]]}
+// CHECK: [[META7]] = !{i64 2}
+// CHECK: [[META8]] = !{!"test_deep_nesting_mixed_10", [[META9:![0-9]+]]}
+// CHECK: [[META9]] = !{i64 3}
+// CHECK: [[META10]] = !{!"test_deep_nesting_mixed_100", [[META3]]}
+// CHECK: [[META11]] = !{!"test_deep_nesting_mixed_100", [[META12:![0-9]+]]}
+// CHECK: [[META12]] = !{i64 4}
+// CHECK: [[META13]] = !{!"test_deep_nesting_mixed_1000", [[META14:![0-9]+]]}
+// CHECK: [[META14]] = !{i64 5}
+// CHECK: [[META15]] = !{!"test_deep_nesting_mixed_1000", [[META3]]}
+// CHECK: [[META16]] = !{!"test_deep_nesting_mixed_1000", [[META17:![0-9]+]]}
+// CHECK: [[META17]] = !{i64 6}
+// CHECK: [[META18]] = !{!"test_deep_nesting_mixed_100000", [[META19:![0-9]+]]}
+// CHECK: [[META19]] = !{i64 7}
+// CHECK: [[META20]] = !{!"test_deep_nesting_mixed_1000001", [[META3]]}
+// CHECK: [[META27]] = !{!"test_same_name_different_types_1", [[META3]]}
+// CHECK: [[META28]] = !{!"test_same_name_different_types_10", [[META5]]}
+// CHECK: [[META29]] = !{!"test_same_name_different_types_100", [[META3]]}
+// CHECK: [[META30]] = !{!"test_same_name_different_types_101", [[META7]]}
+// CHECK: [[META31]] = !{!"test_same_name_different_types_1011", [[META3]]}
+// CHECK: [[META32]] = !{!"test_same_name_different_types_10110", [[META9]]}
+//.
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def 
b/llvm/include/llvm/IR/FixedMetadataKinds.def
index 74746cced6f23..27c01fa0e6219 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -57,3 +57,4 @@ LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
 LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
 LLVM_FIXED_MD_KIND(MD_captures, "captures", 44)
 LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 45)
+LLVM_FIXED_MD_KIND(MD_scope, "scope", 46)
diff --git a/llvm/include/llvm/IR/Intrinsics.td 
b/llvm/include/llvm/IR/Intrinsics.td
index 35a4158a56da9..4d974ba03a1ee 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -957,6 +957,9 @@ def int_experimental_noalias_scope_decl
     : DefaultAttrsIntrinsic<[], [llvm_metadata_ty],
         [IntrInaccessibleMemOnly]>; // blocks LICM and some more
 
+def int_experimental_restrict
+    : DefaultAttrsIntrinsic<[], [llvm_ptr_ty, llvm_metadata_ty], []>;
+
 // Stack Protector Intrinsic - The stackprotector intrinsic writes the stack
 // guard to the correct place on the stack frame.
 def int_stackprotector : DefaultAttrsIntrinsic<[], [llvm_ptr_ty, llvm_ptr_ty], 
[]>;

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

Reply via email to