Skip to content

Commit 5ba8ecb

Browse files
committed
[Clang][OpenMP] Find the type omp_allocator_handle_t from identifier table
In Clang, in order to determine the type of `omp_allocator_handle_t`, Clang checks the type of those predefined allocators. The first one it checks is `omp_null_allocator`. If the language is C, and the system is 64-bit, what Clang gets is a `int`, instead of an enum of size 8, given the fact how we define `omp_allocator_handle_t` in `omp.h`. If the allocator is captured by a region, let's say a parallel region, the allocator will be privatized. Because Clang deems `omp_allocator_handle_t` as an `int`, it will first cast the value returned by the runtime library (for `libomp` it is a `void *`) to `int`, and then in the outlined function, it casts back to `omp_allocator_handle_t`. This two casts completely shaves the first 32-bit of the pointer value returned from `libomp`, and when the private "new" pointer is fed to another runtime function `__kmpc_allocate()`, it causes segment fault. That is the root cause of PR54082. I have no idea why `-fno-pic` could hide this bug. In this patch, we detect `omp_allocator_handle_t` using roughly the same method as `omp_event_handle_t`, by looking it up into the identifier table. Fix #54082. Reviewed By: ABataev Differential Revision: https://reviews.llvm.org/D142297
1 parent 5ed6d99 commit 5ba8ecb

File tree

4 files changed

+216
-25
lines changed

4 files changed

+216
-25
lines changed

clang/lib/Sema/SemaOpenMP.cpp

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3280,13 +3280,15 @@ getAllocatorKind(Sema &S, DSAStackTy *Stack, Expr *Allocator) {
32803280
Allocator->containsUnexpandedParameterPack())
32813281
return OMPAllocateDeclAttr::OMPUserDefinedMemAlloc;
32823282
auto AllocatorKindRes = OMPAllocateDeclAttr::OMPUserDefinedMemAlloc;
3283+
llvm::FoldingSetNodeID AEId;
32833284
const Expr *AE = Allocator->IgnoreParenImpCasts();
3285+
AE->IgnoreImpCasts()->Profile(AEId, S.getASTContext(), /*Canonical=*/true);
32843286
for (int I = 0; I < OMPAllocateDeclAttr::OMPUserDefinedMemAlloc; ++I) {
32853287
auto AllocatorKind = static_cast<OMPAllocateDeclAttr::AllocatorTypeTy>(I);
32863288
const Expr *DefAllocator = Stack->getAllocator(AllocatorKind);
3287-
llvm::FoldingSetNodeID AEId, DAEId;
3288-
AE->Profile(AEId, S.getASTContext(), /*Canonical=*/true);
3289-
DefAllocator->Profile(DAEId, S.getASTContext(), /*Canonical=*/true);
3289+
llvm::FoldingSetNodeID DAEId;
3290+
DefAllocator->IgnoreImpCasts()->Profile(DAEId, S.getASTContext(),
3291+
/*Canonical=*/true);
32903292
if (AEId == DAEId) {
32913293
AllocatorKindRes = AllocatorKind;
32923294
break;
@@ -16496,10 +16498,22 @@ OMPClause *Sema::ActOnOpenMPSimdlenClause(Expr *Len, SourceLocation StartLoc,
1649616498
/// Tries to find omp_allocator_handle_t type.
1649716499
static bool findOMPAllocatorHandleT(Sema &S, SourceLocation Loc,
1649816500
DSAStackTy *Stack) {
16499-
QualType OMPAllocatorHandleT = Stack->getOMPAllocatorHandleT();
16500-
if (!OMPAllocatorHandleT.isNull())
16501+
if (!Stack->getOMPAllocatorHandleT().isNull())
1650116502
return true;
16502-
// Build the predefined allocator expressions.
16503+
16504+
// Set the allocator handle type.
16505+
IdentifierInfo *II = &S.PP.getIdentifierTable().get("omp_allocator_handle_t");
16506+
ParsedType PT = S.getTypeName(*II, Loc, S.getCurScope());
16507+
if (!PT.getAsOpaquePtr() || PT.get().isNull()) {
16508+
S.Diag(Loc, diag::err_omp_implied_type_not_found)
16509+
<< "omp_allocator_handle_t";
16510+
return false;
16511+
}
16512+
QualType AllocatorHandleEnumTy = PT.get();
16513+
AllocatorHandleEnumTy.addConst();
16514+
Stack->setOMPAllocatorHandleT(AllocatorHandleEnumTy);
16515+
16516+
// Fill the predefined allocator map.
1650316517
bool ErrorFound = false;
1650416518
for (int I = 0; I < OMPAllocateDeclAttr::OMPUserDefinedMemAlloc; ++I) {
1650516519
auto AllocatorKind = static_cast<OMPAllocateDeclAttr::AllocatorTypeTy>(I);
@@ -16519,9 +16533,10 @@ static bool findOMPAllocatorHandleT(Sema &S, SourceLocation Loc,
1651916533
ErrorFound = true;
1652016534
break;
1652116535
}
16522-
if (OMPAllocatorHandleT.isNull())
16523-
OMPAllocatorHandleT = AllocatorType;
16524-
if (!S.getASTContext().hasSameType(OMPAllocatorHandleT, AllocatorType)) {
16536+
Res = S.PerformImplicitConversion(Res.get(), AllocatorHandleEnumTy,
16537+
Sema::AA_Initializing,
16538+
/* AllowExplicit */ true);
16539+
if (!Res.isUsable()) {
1652516540
ErrorFound = true;
1652616541
break;
1652716542
}
@@ -16532,8 +16547,7 @@ static bool findOMPAllocatorHandleT(Sema &S, SourceLocation Loc,
1653216547
<< "omp_allocator_handle_t";
1653316548
return false;
1653416549
}
16535-
OMPAllocatorHandleT.addConst();
16536-
Stack->setOMPAllocatorHandleT(OMPAllocatorHandleT);
16550+
1653716551
return true;
1653816552
}
1653916553

@@ -23656,17 +23670,26 @@ OMPClause *Sema::ActOnOpenMPUsesAllocatorClause(
2365623670
AllocatorExpr = D.Allocator->IgnoreParenImpCasts();
2365723671
auto *DRE = dyn_cast<DeclRefExpr>(AllocatorExpr);
2365823672
bool IsPredefinedAllocator = false;
23659-
if (DRE)
23660-
IsPredefinedAllocator = PredefinedAllocators.count(DRE->getDecl());
23661-
if (!DRE ||
23662-
!(Context.hasSameUnqualifiedType(
23663-
AllocatorExpr->getType(), DSAStack->getOMPAllocatorHandleT()) ||
23664-
Context.typesAreCompatible(AllocatorExpr->getType(),
23665-
DSAStack->getOMPAllocatorHandleT(),
23666-
/*CompareUnqualified=*/true)) ||
23667-
(!IsPredefinedAllocator &&
23668-
(AllocatorExpr->getType().isConstant(Context) ||
23669-
!AllocatorExpr->isLValue()))) {
23673+
if (DRE) {
23674+
OMPAllocateDeclAttr::AllocatorTypeTy AllocatorTy =
23675+
getAllocatorKind(*this, DSAStack, AllocatorExpr);
23676+
IsPredefinedAllocator =
23677+
AllocatorTy !=
23678+
OMPAllocateDeclAttr::AllocatorTypeTy::OMPUserDefinedMemAlloc;
23679+
}
23680+
QualType OMPAllocatorHandleT = DSAStack->getOMPAllocatorHandleT();
23681+
QualType AllocatorExprType = AllocatorExpr->getType();
23682+
bool IsTypeCompatible = IsPredefinedAllocator;
23683+
IsTypeCompatible = IsTypeCompatible ||
23684+
Context.hasSameUnqualifiedType(AllocatorExprType,
23685+
OMPAllocatorHandleT);
23686+
IsTypeCompatible =
23687+
IsTypeCompatible ||
23688+
Context.typesAreCompatible(AllocatorExprType, OMPAllocatorHandleT);
23689+
bool IsNonConstantLValue =
23690+
!AllocatorExprType.isConstant(Context) && AllocatorExpr->isLValue();
23691+
if (!DRE || !IsTypeCompatible ||
23692+
(!IsPredefinedAllocator && !IsNonConstantLValue)) {
2367023693
Diag(D.Allocator->getExprLoc(), diag::err_omp_var_expected)
2367123694
<< "omp_allocator_handle_t" << (DRE ? 1 : 0)
2367223695
<< AllocatorExpr->getType() << D.Allocator->getSourceRange();

clang/test/OpenMP/bug54082.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --include-generated-funcs --prefix-filecheck-ir-name _
2+
// RUN: %clang_cc1 -fopenmp -O1 -x c -triple x86_64-apple-darwin10 -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK
3+
4+
typedef enum omp_allocator_handle_t {
5+
omp_null_allocator = 0,
6+
omp_default_mem_alloc = 1,
7+
omp_large_cap_mem_alloc = 2,
8+
omp_const_mem_alloc = 3,
9+
omp_high_bw_mem_alloc = 4,
10+
omp_low_lat_mem_alloc = 5,
11+
omp_cgroup_mem_alloc = 6,
12+
omp_pteam_mem_alloc = 7,
13+
omp_thread_mem_alloc = 8,
14+
KMP_ALLOCATOR_MAX_HANDLE = __UINTPTR_MAX__
15+
} omp_allocator_handle_t;
16+
17+
typedef enum omp_memspace_handle_t {
18+
omp_default_mem_space = 0,
19+
omp_large_cap_mem_space = 1,
20+
omp_const_mem_space = 2,
21+
omp_high_bw_mem_space = 3,
22+
omp_low_lat_mem_space = 4,
23+
llvm_omp_target_host_mem_space = 100,
24+
llvm_omp_target_shared_mem_space = 101,
25+
llvm_omp_target_device_mem_space = 102,
26+
KMP_MEMSPACE_MAX_HANDLE = __UINTPTR_MAX__
27+
} omp_memspace_handle_t;
28+
29+
typedef enum {
30+
omp_atk_sync_hint = 1,
31+
omp_atk_alignment = 2,
32+
omp_atk_access = 3,
33+
omp_atk_pool_size = 4,
34+
omp_atk_fallback = 5,
35+
omp_atk_fb_data = 6,
36+
omp_atk_pinned = 7,
37+
omp_atk_partition = 8
38+
} omp_alloctrait_key_t;
39+
40+
typedef __UINTPTR_TYPE__ omp_uintptr_t;
41+
typedef __SIZE_TYPE__ size_t;
42+
43+
typedef struct {
44+
omp_alloctrait_key_t key;
45+
omp_uintptr_t value;
46+
} omp_alloctrait_t;
47+
48+
extern omp_allocator_handle_t
49+
omp_init_allocator(omp_memspace_handle_t memspace, int ntraits,
50+
const omp_alloctrait_t traits[]);
51+
52+
#define N 1024
53+
54+
void foo() {
55+
int *x;
56+
57+
omp_memspace_handle_t x_memspace = omp_default_mem_space;
58+
omp_alloctrait_t x_traits[1] = {omp_atk_alignment, 64};
59+
omp_allocator_handle_t x_alloc = omp_init_allocator(x_memspace, 1, x_traits);
60+
61+
#pragma omp parallel for allocate(x_alloc : x) private(x)
62+
for (int i = 0; i < N; i++) {
63+
(void)x;
64+
}
65+
}
66+
// CHECK-LABEL: define {{[^@]+}}@foo
67+
// CHECK-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] {
68+
// CHECK-NEXT: entry:
69+
// CHECK-NEXT: [[X_TRAITS:%.*]] = alloca [1 x %struct.omp_alloctrait_t], align 16
70+
// CHECK-NEXT: [[X_ALLOC:%.*]] = alloca i64, align 8
71+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr nonnull [[X_TRAITS]]) #[[ATTR5:[0-9]+]]
72+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 16 dereferenceable(16) [[X_TRAITS]], ptr noundef nonnull align 16 dereferenceable(16) @__const.foo.x_traits, i64 16, i1 false)
73+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr nonnull [[X_ALLOC]]) #[[ATTR5]]
74+
// CHECK-NEXT: [[CALL:%.*]] = call i64 @omp_init_allocator(i64 noundef 0, i32 noundef 1, ptr noundef nonnull [[X_TRAITS]]) #[[ATTR5]]
75+
// CHECK-NEXT: store i64 [[CALL]], ptr [[X_ALLOC]], align 8, !tbaa [[TBAA3:![0-9]+]]
76+
// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr nonnull @[[GLOB2:[0-9]+]], i32 1, ptr nonnull @.omp_outlined., ptr nonnull [[X_ALLOC]])
77+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr nonnull [[X_ALLOC]]) #[[ATTR5]]
78+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr nonnull [[X_TRAITS]]) #[[ATTR5]]
79+
// CHECK-NEXT: ret void
80+
//
81+
//
82+
// CHECK-LABEL: define {{[^@]+}}@.omp_outlined.
83+
// CHECK-SAME: (ptr noalias nocapture noundef readonly [[DOTGLOBAL_TID_:%.*]], ptr noalias nocapture readnone [[DOTBOUND_TID_:%.*]], ptr nocapture noundef nonnull readonly align 8 dereferenceable(8) [[X_ALLOC:%.*]]) #[[ATTR4:[0-9]+]] {
84+
// CHECK-NEXT: entry:
85+
// CHECK-NEXT: [[DOTOMP_LB:%.*]] = alloca i32, align 4
86+
// CHECK-NEXT: [[DOTOMP_UB:%.*]] = alloca i32, align 4
87+
// CHECK-NEXT: [[DOTOMP_STRIDE:%.*]] = alloca i32, align 4
88+
// CHECK-NEXT: [[DOTOMP_IS_LAST:%.*]] = alloca i32, align 4
89+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[DOTOMP_LB]]) #[[ATTR5]]
90+
// CHECK-NEXT: store i32 0, ptr [[DOTOMP_LB]], align 4, !tbaa [[TBAA6:![0-9]+]]
91+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[DOTOMP_UB]]) #[[ATTR5]]
92+
// CHECK-NEXT: store i32 1023, ptr [[DOTOMP_UB]], align 4, !tbaa [[TBAA6]]
93+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[DOTOMP_STRIDE]]) #[[ATTR5]]
94+
// CHECK-NEXT: store i32 1, ptr [[DOTOMP_STRIDE]], align 4, !tbaa [[TBAA6]]
95+
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[DOTOMP_IS_LAST]]) #[[ATTR5]]
96+
// CHECK-NEXT: store i32 0, ptr [[DOTOMP_IS_LAST]], align 4, !tbaa [[TBAA6]]
97+
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4, !tbaa [[TBAA6]]
98+
// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[X_ALLOC]], align 8, !tbaa [[TBAA3]]
99+
// CHECK-NEXT: [[CONV:%.*]] = inttoptr i64 [[TMP1]] to ptr
100+
// CHECK-NEXT: [[DOTX__VOID_ADDR:%.*]] = tail call ptr @__kmpc_alloc(i32 [[TMP0]], i64 8, ptr [[CONV]])
101+
// CHECK-NEXT: call void @__kmpc_for_static_init_4(ptr nonnull @[[GLOB1:[0-9]+]], i32 [[TMP0]], i32 34, ptr nonnull [[DOTOMP_IS_LAST]], ptr nonnull [[DOTOMP_LB]], ptr nonnull [[DOTOMP_UB]], ptr nonnull [[DOTOMP_STRIDE]], i32 1, i32 1)
102+
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4, !tbaa [[TBAA6]]
103+
// CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP2]], i32 1023)
104+
// CHECK-NEXT: store i32 [[COND]], ptr [[DOTOMP_UB]], align 4, !tbaa [[TBAA6]]
105+
// CHECK-NEXT: call void @__kmpc_for_static_fini(ptr nonnull @[[GLOB1]], i32 [[TMP0]])
106+
// CHECK-NEXT: [[TMP3:%.*]] = load i64, ptr [[X_ALLOC]], align 8, !tbaa [[TBAA3]]
107+
// CHECK-NEXT: [[CONV5:%.*]] = inttoptr i64 [[TMP3]] to ptr
108+
// CHECK-NEXT: call void @__kmpc_free(i32 [[TMP0]], ptr [[DOTX__VOID_ADDR]], ptr [[CONV5]])
109+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[DOTOMP_IS_LAST]]) #[[ATTR5]]
110+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[DOTOMP_STRIDE]]) #[[ATTR5]]
111+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[DOTOMP_UB]]) #[[ATTR5]]
112+
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[DOTOMP_LB]]) #[[ATTR5]]
113+
// CHECK-NEXT: ret void
114+
//

clang/test/OpenMP/target_uses_allocators.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#ifndef HEADER
77
#define HEADER
88

9-
enum omp_allocator_handle_t {
9+
typedef enum omp_allocator_handle_t {
1010
omp_null_allocator = 0,
1111
omp_default_mem_alloc = 1,
1212
omp_large_cap_mem_alloc = 2,
@@ -17,7 +17,7 @@ enum omp_allocator_handle_t {
1717
omp_pteam_mem_alloc = 7,
1818
omp_thread_mem_alloc = 8,
1919
KMP_ALLOCATOR_MAX_HANDLE = __UINTPTR_MAX__
20-
};
20+
} omp_allocator_handle_t;
2121

2222
// CHECK: define {{.*}}[[FIE:@.+]]()
2323
void fie(void) {
@@ -105,4 +105,4 @@ void fie(void) {
105105
// CHECK-NEXT: %.x..void.addr = call ptr @__kmpc_alloc(i32 %[[#R0]], i64 4, ptr inttoptr (i64 8 to ptr))
106106
// CHECK-NEXT: %[[#R1:]] = load i32, ptr %x.addr, align 4
107107
// CHECK-NEXT: store i32 %[[#R1]], ptr %.x..void.addr, align 4
108-
// CHECK-NEXT: call void @__kmpc_free(i32 %[[#R0]], ptr %.x..void.addr, ptr inttoptr (i64 8 to ptr))
108+
// CHECK-NEXT: call void @__kmpc_free(i32 %[[#R0]], ptr %.x..void.addr, ptr inttoptr (i64 8 to ptr))
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// This test is adapted from test_parallel_for_allocate.c in SOLLVE V&V.
2+
// https://github.com/SOLLVE/sollve_vv/blob/master/tests/5.0/parallel_for/test_parallel_for_allocate.c
3+
// RUN: %libomp-compile-and-run
4+
#include <omp.h>
5+
6+
#include <assert.h>
7+
#include <stdlib.h>
8+
9+
#define N 1024
10+
11+
int main(int argc, char *argv[]) {
12+
int errors = 0;
13+
int *x;
14+
int result[N][N];
15+
int successful_alloc = 0;
16+
17+
omp_memspace_handle_t x_memspace = omp_default_mem_space;
18+
omp_alloctrait_t x_traits[1] = {omp_atk_alignment, 64};
19+
omp_allocator_handle_t x_alloc = omp_init_allocator(x_memspace, 1, x_traits);
20+
21+
for (int i = 0; i < N; i++) {
22+
for (int j = 0; j < N; j++) {
23+
result[i][j] = -1;
24+
}
25+
}
26+
27+
#pragma omp parallel for allocate(x_alloc: x) private(x) shared(result)
28+
for (int i = 0; i < N; i++) {
29+
x = (int *)malloc(N * sizeof(int));
30+
if (x != NULL) {
31+
#pragma omp simd simdlen(16) aligned(x : 64)
32+
for (int j = 0; j < N; j++) {
33+
x[j] = j * i;
34+
}
35+
for (int j = 0; j < N; j++) {
36+
result[i][j] = x[j];
37+
}
38+
free(x);
39+
successful_alloc++;
40+
}
41+
}
42+
43+
errors += successful_alloc < 1;
44+
45+
for (int i = 0; i < N; i++) {
46+
for (int j = 0; j < N; j++) {
47+
errors += result[i][j] != i * j;
48+
}
49+
}
50+
51+
omp_destroy_allocator(x_alloc);
52+
53+
return errors;
54+
}

0 commit comments

Comments
 (0)