Skip to content

Commit

Permalink
[CIR][CIRGen][TBAA] Initial TBAA support for union, enum and bitint
Browse files Browse the repository at this point in the history
  • Loading branch information
PikachuHyA committed Dec 20, 2024
1 parent 363440c commit 34dc621
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 1 deletion.
2 changes: 2 additions & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ struct MissingFeatures {
static bool tbaa() { return false; }
static bool tbaaStruct() { return false; }
static bool tbaaTagForStruct() { return false; }
static bool tbaaTagForEnum() { return false; }
static bool tbaaTagForBitInt() { return false; }
static bool tbaaVTablePtr() { return false; }
static bool tbaaIncompleteType() { return false; }
static bool tbaaMergeTBAAInfo() { return false; }
Expand Down
55 changes: 54 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenTBAA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "mlir/Interfaces/DataLayoutInterfaces.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/Support/ErrorHandling.h"
Expand Down Expand Up @@ -61,6 +62,58 @@ static bool isValidBaseType(clang::QualType qty) {
return false;
}

cir::TBAAAttr CIRGenTBAA::getTypeInfoHelper(clang::QualType qty) {
const clang::Type *ty = astContext.getCanonicalType(qty).getTypePtr();
// Handle builtin types.
if (mlir::isa<BuiltinType>(ty)) {
return cir::TBAAScalarAttr::get(mlirContext, types.ConvertType(qty));
}
// C++1z [basic.lval]p10: "If a program attempts to access the stored value of
// an object through a glvalue of other than one of the following types the
// behavior is undefined: [...] a char, unsigned char, or std::byte type."
if (ty->isStdByteType())
return getChar();

// Handle pointers and references.
//
// C has a very strict rule for pointer aliasing. C23 6.7.6.1p2:
// For two pointer types to be compatible, both shall be identically
// qualified and both shall be pointers to compatible types.
//
// This rule is impractically strict; we want to at least ignore CVR
// qualifiers. Distinguishing by CVR qualifiers would make it UB to
// e.g. cast a `char **` to `const char * const *` and dereference it,
// which is too common and useful to invalidate. C++'s similar types
// rule permits qualifier differences in these nested positions; in fact,
// C++ even allows that cast as an implicit conversion.
//
// Other qualifiers could theoretically be distinguished, especially if
// they involve a significant representation difference. We don't
// currently do so, however.
if (ty->isPointerType() || ty->isReferenceType()) {
if (!codeGenOpts.PointerTBAA) {
return cir::TBAAScalarAttr::get(mlirContext, types.ConvertType(qty));
}
llvm_unreachable("NYI");
}
// Accesses to arrays are accesses to objects of their element types.
if (codeGenOpts.NewStructPathTBAA && ty->isArrayType()) {
llvm_unreachable("NYI");
}
// Enum types are distinct types. In C++ they have "underlying types",
// however they aren't related for TBAA.
if (const EnumType *ety = dyn_cast<EnumType>(ty)) {
assert(!cir::MissingFeatures::tbaaTagForEnum());
return tbaa_NYI(mlirContext);
}
if (const auto *eit = dyn_cast<BitIntType>(ty)) {
assert(!cir::MissingFeatures::tbaaTagForBitInt());
return tbaa_NYI(mlirContext);
}
// For now, handle any other kind of type conservatively.
return getChar();
}

cir::TBAAAttr CIRGenTBAA::getTypeInfo(clang::QualType qty) {
// At -O0 or relaxed aliasing, TBAA is not emitted for regular types.
if (codeGenOpts.OptimizationLevel == 0 || codeGenOpts.RelaxedAliasing) {
Expand Down Expand Up @@ -93,7 +146,7 @@ cir::TBAAAttr CIRGenTBAA::getTypeInfo(clang::QualType qty) {
// cache, which invalidates all its previously obtained iterators. So we
// first generate the node for the type and then add that node to the
// cache.
auto typeNode = cir::TBAAScalarAttr::get(mlirContext, types.ConvertType(qty));
auto typeNode = getTypeInfoHelper(qty);
return metadataCache[ty] = typeNode;
}

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenTBAA.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ class CIRGenTBAA {

cir::TBAAAttr getChar();

// An internal helper function to generate metadata used
// to describe accesses to objects of the given type.
cir::TBAAAttr getTypeInfoHelper(clang::QualType qty);

public:
CIRGenTBAA(mlir::MLIRContext *mlirContext, clang::ASTContext &astContext,
CIRGenTypes &types, mlir::ModuleOp moduleOp,
Expand Down
13 changes: 13 additions & 0 deletions clang/test/CIR/CodeGen/tbaa-bitinit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -O1
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s


// CIR: #tbaa[[tbaa_NYI:.*]] = #cir.tbaa

_BitInt(33) a;
_BitInt(31) b;
void c() {
// CIR: %{{.*}} = cir.load %{{.*}} : !cir.ptr<!cir.int<s, 33>>, !cir.int<s, 33> tbaa(#tbaa[[tbaa_NYI]])
// CIR: cir.store %{{.*}}, %{{.*}} : !cir.int<s, 31>, !cir.ptr<!cir.int<s, 31>> tbaa(#tbaa[[tbaa_NYI]])
b = a;
}
49 changes: 49 additions & 0 deletions clang/test/CIR/CodeGen/tbaa-enum.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -O1
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll -O1 -disable-llvm-passes
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll -O1 -disable-llvm-passes -relaxed-aliasing
// RUN: FileCheck --check-prefix=NO-TBAA --input-file=%t.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll -O0 -disable-llvm-passes
// RUN: FileCheck --check-prefix=NO-TBAA --input-file=%t.ll %s

// NO-TBAA-NOT: !tbaa

// CIR: #tbaa[[tbaa_NYI:.*]] = #cir.tbaa
// CIR: #tbaa[[INT:.*]] = #cir.tbaa_scalar<type = !u32i>
// CIR: #tbaa[[INT_PTR:.*]] = #cir.tbaa_scalar<type = !cir.ptr<!u32i>>

typedef unsigned int uint32_t;
typedef enum {
RED_AUTO_32,
GREEN_AUTO_32,
BLUE_AUTO_32
} EnumAuto32;

uint32_t g0(EnumAuto32 *E, uint32_t *val) {
// CIR-LABEL: cir.func @g0
// CIR: %[[C5:.*]] = cir.const #cir.int<5> : !s32i
// CIR: %[[U_C5:.*]] = cir.cast(integral, %[[C5]] : !s32i), !u32i
// CIR: %[[VAL_PTR:.*]] = cir.load deref %{{.*}} : !cir.ptr<!cir.ptr<!u32i>>, !cir.ptr<!u32i> tbaa(#tbaa[[INT_PTR]])
// CIR: cir.store %[[U_C5]], %[[VAL_PTR]] : !u32i, !cir.ptr<!u32i> tbaa(#tbaa[[INT]])
// CIR: %[[C0:.*]] = cir.const #cir.int<0> : !s32i
// CIR: %[[U_C0:.*]] = cir.cast(integral, %[[C0]] : !s32i), !u32i
// CIR: %[[E_PTR:.*]] = cir.load deref %{{.*}} : !cir.ptr<!cir.ptr<!u32i>>, !cir.ptr<!u32i> tbaa(#tbaa[[INT_PTR]])
// CIR: cir.store %[[U_C0]], %[[E_PTR]] : !u32i, !cir.ptr<!u32i> tbaa(#tbaa[[tbaa_NYI]])
// CIR: %[[RET_PTR:.*]] = cir.load deref %{{.*}} : !cir.ptr<!cir.ptr<!u32i>>, !cir.ptr<!u32i> tbaa(#tbaa[[INT_PTR]])
// CIR: %[[RET:.*]] = cir.load %[[RET_PTR]] : !cir.ptr<!u32i>, !u32i tbaa(#tbaa[[INT]])
// CIR: cir.store %[[RET]], %{{.*}} : !u32i, !cir.ptr<!u32i>

// LLVM-LABEL: define{{.*}} i32 @g0(
// LLVM: store i32 5, ptr %{{.*}}, align 4, !tbaa [[TAG_i32:!.*]]
// LLVM: store i32 0, ptr %{{.*}}, align 4
// LLVM: load i32, ptr %{{.*}}, align 4, !tbaa [[TAG_i32]]
*val = 5;
*E = RED_AUTO_32;
return *val;
}

// LLVM: [[TYPE_char:!.*]] = !{!"omnipotent char", [[TAG_c_tbaa:!.*]],
// LLVM: [[TAG_c_tbaa]] = !{!"Simple C/C++ TBAA"}
// LLVM: [[TAG_i32]] = !{[[TYPE_i32:!.*]], [[TYPE_i32]], i64 0}
// LLVM: [[TYPE_i32]] = !{!"int", [[TYPE_char]], i64 0}
33 changes: 33 additions & 0 deletions clang/test/CIR/CodeGen/tbaa-union.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -O1
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll -O1
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll -O1 -relaxed-aliasing
// RUN: FileCheck --check-prefix=NO-TBAA --input-file=%t.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll -O0
// RUN: FileCheck --check-prefix=NO-TBAA --input-file=%t.ll %s

// NO-TBAA-NOT: !tbaa
// CIR: #tbaa[[CHAR:.*]] = #cir.tbaa_scalar<type = !cir.int<s, 1>>
// CIR: #tbaa[[Struct_S_PTR:.*]] = #cir.tbaa_scalar<type = !cir.ptr<!ty_S>>
typedef struct {
union {
int a, b;
};
int c;
} S;

void foo(S *s) {
// CIR-LABEL: cir.func @foo
// CIR: %[[C1:.*]] = cir.const #cir.int<1> : !s32i loc(#loc6)
// CIR: %{{.*}} = cir.load %{{.*}} : !cir.ptr<!cir.ptr<!ty_S>>, !cir.ptr<!ty_S> tbaa(#tbaa[[Struct_S_PTR]])
// CIR: cir.store %[[C1]], %{{.*}} : !s32i, !cir.ptr<!s32i> tbaa(#tbaa[[CHAR]])

// LLVM-LABEL: void @foo
// LLVM: store i32 1, ptr %{{.*}}, align 4, !tbaa ![[TBAA_TAG:.*]]
s->a = 1;
}

// LLVM: ![[TBAA_TAG]] = !{![[CHAR:.*]], ![[CHAR]], i64 0}
// LLVM: ![[CHAR]] = !{!"omnipotent char", ![[ROOT:.*]], i64 0}
// LLVM: ![[ROOT]] = !{!"Simple C/C++ TBAA"}

0 comments on commit 34dc621

Please sign in to comment.