Skip to content

Commit ec03323

Browse files
aeubankststellar
authored andcommitted
[clang] Fix some clang->llvm type cache invalidation issues
Take the following as an example struct z { z (*p)(); }; z f(); When we attempt to get the LLVM type of f, we recurse into z. z itself has a function pointer with the same type as f. Given the recursion, Clang simply treats z::p as a pointer to an empty struct `{}*`. The LLVM type of f is as expected. So we have two different potential LLVM types for a given Clang type. If we store one of those into the cache, when we access the cache with a different context (e.g. we are/aren't recursing on z) we may get an incorrect result. There is some attempt to clear the cache in these cases, but it doesn't seem to handle all cases. This change makes it so we only use the cache when we are not in any sort of function context, i.e. `noRecordsBeingLaidOut() && FunctionsBeingProcessed.empty()`, which are the cases where we may decide to choose a different LLVM type for a given Clang type. LLVM types for builtin types are never recursive so they're always ok. This allows us to clear the type cache less often (as seen with the removal of one of the calls to `TypeCache.clear()`). We still need to clear it when we use a placeholder type then replace it later with the final type and other dependent types need to be recalculated. I've added a check that the cached type matches what we compute. It triggered in this test case without the fix. It's currently not check-clang clean so it's not on by default for something like expensive checks builds. This change uncovered another issue where the LLVM types for an argument and its local temporary don't match. For example in type-cache-3, when expanding z::dc's argument into a temporary alloca, we ConvertType() the type of z::p which is `void ({}*)*`, which doesn't match the alloca GEP type of `{}*`. No noticeable compile time changes: https://llvm-compile-time-tracker.com/compare.php?from=3918dd6b8acf8c5886b9921138312d1c638b2937&to=50bdec9836ed40e38ece0657f3058e730adffc4c&stat=instructions Fixes #53465. Reviewed By: rnk Differential Revision: https://reviews.llvm.org/D118744 (cherry picked from commit 45084ea)
1 parent 10d4425 commit ec03323

File tree

6 files changed

+102
-15
lines changed

6 files changed

+102
-15
lines changed

clang/lib/CodeGen/CGBuilder.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
#ifndef LLVM_CLANG_LIB_CODEGEN_CGBUILDER_H
1010
#define LLVM_CLANG_LIB_CODEGEN_CGBUILDER_H
1111

12-
#include "llvm/IR/DataLayout.h"
13-
#include "llvm/IR/IRBuilder.h"
1412
#include "Address.h"
1513
#include "CodeGenTypeCache.h"
14+
#include "llvm/IR/DataLayout.h"
15+
#include "llvm/IR/IRBuilder.h"
16+
#include "llvm/IR/Type.h"
1617

1718
namespace clang {
1819
namespace CodeGen {

clang/lib/CodeGen/CGCall.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "llvm/IR/InlineAsm.h"
3939
#include "llvm/IR/IntrinsicInst.h"
4040
#include "llvm/IR/Intrinsics.h"
41+
#include "llvm/IR/Type.h"
4142
#include "llvm/Transforms/Utils/Local.h"
4243
using namespace clang;
4344
using namespace CodeGen;
@@ -1056,10 +1057,19 @@ void CodeGenFunction::ExpandTypeFromArgs(QualType Ty, LValue LV,
10561057
// Call EmitStoreOfScalar except when the lvalue is a bitfield to emit a
10571058
// primitive store.
10581059
assert(isa<NoExpansion>(Exp.get()));
1059-
if (LV.isBitField())
1060-
EmitStoreThroughLValue(RValue::get(&*AI++), LV);
1061-
else
1062-
EmitStoreOfScalar(&*AI++, LV);
1060+
llvm::Value *Arg = &*AI++;
1061+
if (LV.isBitField()) {
1062+
EmitStoreThroughLValue(RValue::get(Arg), LV);
1063+
} else {
1064+
// TODO: currently there are some places are inconsistent in what LLVM
1065+
// pointer type they use (see D118744). Once clang uses opaque pointers
1066+
// all LLVM pointer types will be the same and we can remove this check.
1067+
if (Arg->getType()->isPointerTy()) {
1068+
Address Addr = LV.getAddress(*this);
1069+
Arg = Builder.CreateBitCast(Arg, Addr.getElementType());
1070+
}
1071+
EmitStoreOfScalar(Arg, LV);
1072+
}
10631073
}
10641074
}
10651075

clang/lib/CodeGen/CodeGenTypes.cpp

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,20 @@
2525
#include "llvm/IR/DataLayout.h"
2626
#include "llvm/IR/DerivedTypes.h"
2727
#include "llvm/IR/Module.h"
28+
2829
using namespace clang;
2930
using namespace CodeGen;
3031

32+
#ifndef NDEBUG
33+
#include "llvm/Support/CommandLine.h"
34+
// TODO: turn on by default when defined(EXPENSIVE_CHECKS) once check-clang is
35+
// -verify-type-cache clean.
36+
static llvm::cl::opt<bool> VerifyTypeCache(
37+
"verify-type-cache",
38+
llvm::cl::desc("Verify that the type cache matches the computed type"),
39+
llvm::cl::init(false), llvm::cl::Hidden);
40+
#endif
41+
3142
CodeGenTypes::CodeGenTypes(CodeGenModule &cgm)
3243
: CGM(cgm), Context(cgm.getContext()), TheModule(cgm.getModule()),
3344
Target(cgm.getTarget()), TheCXXABI(cgm.getCXXABI()),
@@ -382,9 +393,6 @@ llvm::Type *CodeGenTypes::ConvertFunctionTypeInternal(QualType QFT) {
382393

383394
RecordsBeingLaidOut.erase(Ty);
384395

385-
if (SkippedLayout)
386-
TypeCache.clear();
387-
388396
if (RecordsBeingLaidOut.empty())
389397
while (!DeferredRecords.empty())
390398
ConvertRecordDeclType(DeferredRecords.pop_back_val());
@@ -415,11 +423,29 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
415423
if (const RecordType *RT = dyn_cast<RecordType>(Ty))
416424
return ConvertRecordDeclType(RT->getDecl());
417425

418-
// See if type is already cached.
419-
llvm::DenseMap<const Type *, llvm::Type *>::iterator TCI = TypeCache.find(Ty);
420-
// If type is found in map then use it. Otherwise, convert type T.
421-
if (TCI != TypeCache.end())
422-
return TCI->second;
426+
// The LLVM type we return for a given Clang type may not always be the same,
427+
// most notably when dealing with recursive structs. We mark these potential
428+
// cases with ShouldUseCache below. Builtin types cannot be recursive.
429+
// TODO: when clang uses LLVM opaque pointers we won't be able to represent
430+
// recursive types with LLVM types, making this logic much simpler.
431+
llvm::Type *CachedType = nullptr;
432+
bool ShouldUseCache =
433+
Ty->isBuiltinType() ||
434+
(noRecordsBeingLaidOut() && FunctionsBeingProcessed.empty());
435+
if (ShouldUseCache) {
436+
llvm::DenseMap<const Type *, llvm::Type *>::iterator TCI =
437+
TypeCache.find(Ty);
438+
if (TCI != TypeCache.end())
439+
CachedType = TCI->second;
440+
if (CachedType) {
441+
#ifndef NDEBUG
442+
if (!VerifyTypeCache)
443+
return CachedType;
444+
#else
445+
return CachedType;
446+
#endif
447+
}
448+
}
423449

424450
// If we don't have it in the cache, convert it now.
425451
llvm::Type *ResultType = nullptr;
@@ -797,7 +823,15 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
797823

798824
assert(ResultType && "Didn't convert a type?");
799825

800-
TypeCache[Ty] = ResultType;
826+
#ifndef NDEBUG
827+
if (CachedType) {
828+
assert(CachedType == ResultType &&
829+
"Cached type doesn't match computed type");
830+
}
831+
#endif
832+
833+
if (ShouldUseCache)
834+
TypeCache[Ty] = ResultType;
801835
return ResultType;
802836
}
803837

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %clang_cc1 -mllvm -verify-type-cache -emit-llvm %s -o - -triple i386-pc-windows-msvc19.16.0 | FileCheck %s
2+
// REQUIRES: asserts, x86-registered-target
3+
4+
// CHECK: call void @"?dc@z@@SAXU1@@Z"
5+
struct z {
6+
static void dc(z);
7+
void (*p)(z);
8+
};
9+
10+
void f() {
11+
z::dc({});
12+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_cc1 -mllvm -verify-type-cache -emit-llvm %s -o - -triple i386-pc-windows-msvc19.16.0 | FileCheck %s
2+
// REQUIRES: asserts, x86-registered-target
3+
4+
// CHECK-LABEL: define {{.*}}@"?f@@YAXXZ"(
5+
// CHECK: call void @"?dc@z@@SAXU1@@Z"
6+
7+
// CHECK-LABEL: define {{.*}}@"?dc@z@@SAXU1@@Z"(
8+
// CHECK: store void ({}*)* %{{.*}}, void ({}*)** %{{.*}}
9+
struct z {
10+
static void dc(z) {}
11+
void (*p)(z);
12+
};
13+
14+
void f() {
15+
z::dc({});
16+
}

clang/test/CodeGenCXX/type-cache.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %clang_cc1 -mllvm -verify-type-cache -emit-llvm %s -o - -triple i386-pc-windows-msvc19.16.0 | FileCheck %s
2+
// REQUIRES: asserts, x86-registered-target
3+
4+
// CHECK: call {}* @"?f@@YA?AUz@@XZ"()
5+
6+
struct z {
7+
z (*p)();
8+
};
9+
10+
z f();
11+
12+
void g() {
13+
f();
14+
}

0 commit comments

Comments
 (0)