Skip to content

Commit 4bd5e2b

Browse files
committed
Rewrite offsetof with __builtin_offsetof with -fms-kernel
Patch rewrites portion of AST if it is detected as offset calculation, e.g (size_t)&(((MyStruct*)0)->field). This expression is being rewritten by this patch as (size_t)__builtin_offsetof(MyStruct, field). Patch may affect compiler warnings, see test cases for details.
1 parent d989c24 commit 4bd5e2b

7 files changed

Lines changed: 227 additions & 1 deletion

File tree

clang/include/clang/Sema/Sema.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11942,6 +11942,10 @@ class Sema final : public SemaBase {
1194211942
DeclarationName Name);
1194311943
bool RebuildNestedNameSpecifierInCurrentInstantiation(CXXScopeSpec &SS);
1194411944

11945+
/// Do MS kernel specific AST transformations, e.g replace
11946+
/// &((type*)0)->member with __builtin_offsetof(type, member)
11947+
ExprResult TransformForMSKernel(Expr *UOp);
11948+
1194511949
ExprResult RebuildExprInCurrentInstantiation(Expr *E);
1194611950

1194711951
/// Rebuild the template parameters now that we know we're in a current

clang/lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ add_clang_library(clangSema
6464
SemaLoongArch.cpp
6565
SemaM68k.cpp
6666
SemaMIPS.cpp
67+
SemaMSKernel.cpp
6768
SemaMSP430.cpp
6869
SemaModule.cpp
6970
SemaNVPTX.cpp

clang/lib/Sema/SemaCast.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3364,9 +3364,24 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc,
33643364
// -Wcast-qual
33653365
DiagnoseCastQual(Op.Self, Op.SrcExpr, Op.DestType);
33663366

3367-
return Op.complete(CStyleCastExpr::Create(
3367+
ExprResult Cast = Op.complete(CStyleCastExpr::Create(
33683368
Context, Op.ResultType, Op.ValueKind, Op.Kind, Op.SrcExpr.get(),
33693369
&Op.BasePath, CurFPFeatureOverrides(), CastTypeInfo, LPLoc, RPLoc));
3370+
if (!Cast.isUsable() || Op.Kind != CK_PointerToIntegral ||
3371+
!Context.getLangOpts().Kernel)
3372+
return Cast;
3373+
3374+
ExprResult Transformed =
3375+
TransformForMSKernel(Op.SrcExpr.get()->IgnoreParenImpCasts());
3376+
if (!Transformed.isUsable())
3377+
return Cast;
3378+
3379+
ExprResult NewCast =
3380+
BuildCStyleCastExpr(LPLoc, CastTypeInfo, RPLoc, Transformed.get());
3381+
if (!NewCast.isUsable())
3382+
return NewCast;
3383+
Expr *RealExpr = NewCast.get();
3384+
return PseudoObjectExpr::Create(Context, Cast.get(), {&RealExpr, 1}, 0);
33703385
}
33713386

33723387
ExprResult Sema::BuildCXXFunctionalCastExpr(TypeSourceInfo *CastTypeInfo,

clang/lib/Sema/SemaMSKernel.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include "TreeTransform.h"
2+
#include "clang/AST/ASTConsumer.h"
3+
#include "clang/AST/ASTContext.h"
4+
#include "clang/AST/Expr.h"
5+
#include "clang/AST/RecursiveASTVisitor.h"
6+
#include "clang/Sema/Sema.h"
7+
#include "clang/Sema/SemaConsumer.h"
8+
9+
using namespace clang;
10+
11+
namespace {
12+
ExprResult TransformUnaryOperator(Sema &SemaRef, UnaryOperator *UO) {
13+
if (UO->getOpcode() != UO_AddrOf)
14+
return {};
15+
16+
SmallVector<Sema::OffsetOfComponent, 4> Components;
17+
Expr *Current = UO->getSubExpr()->IgnoreParens();
18+
while (true) {
19+
// Strip away the "noise" added by array decays or parentheses
20+
Current = Current->IgnoreParenImpCasts();
21+
Sema::OffsetOfComponent Comp;
22+
Comp.LocStart = Current->getBeginLoc();
23+
Comp.LocEnd = Current->getEndLoc();
24+
25+
if (auto *ME = dyn_cast<MemberExpr>(Current)) {
26+
Comp.isBrackets = false;
27+
Comp.U.IdentInfo = ME->getMemberDecl()->getIdentifier();
28+
Components.push_back(Comp);
29+
Current = ME->getBase();
30+
} else if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Current)) {
31+
Comp.isBrackets = true;
32+
// In offsetof, the index must be an expression
33+
Comp.U.E = ASE->getIdx();
34+
Components.push_back(Comp);
35+
Current = ASE->getBase();
36+
} else {
37+
// No more members or subscripts
38+
break;
39+
}
40+
}
41+
// Verify we ended at a Null Pointer Cast
42+
Expr *Base = Current->IgnoreParenCasts();
43+
if (!Components.empty() &&
44+
Base->isNullPointerConstant(SemaRef.Context,
45+
Expr::NPC_ValueDependentIsNotNull)) {
46+
// Don't treat &((MyStruct*)0)[1] as an offsetof expression
47+
if (Components.back().isBrackets)
48+
return {};
49+
// Targets like amdgcn, where nullptr != 0, are ignored
50+
if (SemaRef.Context.getTargetNullPointerValue(Current->getType()))
51+
return {};
52+
std::reverse(Components.begin(), Components.end());
53+
54+
// Get the root structure type
55+
TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(
56+
Current->getType()->getPointeeType(), Current->getBeginLoc());
57+
return SemaRef.BuildBuiltinOffsetOf(UO->getBeginLoc(), TInfo, Components,
58+
UO->getEndLoc());
59+
}
60+
61+
return {};
62+
}
63+
} // end anonymous namespace
64+
65+
ExprResult clang::Sema::TransformForMSKernel(Expr *E) {
66+
auto *UO = dyn_cast_or_null<UnaryOperator>(E);
67+
if (!UO)
68+
return {};
69+
ExprResult NewUO = TransformUnaryOperator(*this, UO);
70+
return NewUO.isUsable() ? NewUO : ExprResult();
71+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Normally this file can't be compiled due to
2+
// nullptr cast in offset calculation
3+
// RUN: not %clang_cc1 -triple x86_64-pc-win32 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
4+
// Kernel variant works OK.
5+
// RUN: %clang_cc1 -triple x86_64-pc-win32 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
6+
// We use PseudoObjectExpr, so reconstruction of source from AST should work as expected
7+
// RUN: %clang_cc1 -triple x86_64-pc-win32 -fms-kernel -ast-print %s -o - | FileCheck %s --check-prefix=AST-PRINT
8+
9+
// AST-ORIG: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
10+
// AST-ORIG: FunctionDecl
11+
// AST-ORIG-NEXT: ParmVarDecl
12+
// AST-ORIG-NEXT: CompoundStmt
13+
// AST-ORIG-NEXT: ReturnStmt
14+
// AST-ORIG-NEXT: ParenExpr
15+
// AST-ORIG-NEXT: CStyleCastExpr
16+
// AST-ORIG-NEXT: ParenExpr
17+
// AST-ORIG-NEXT: BinaryOperator
18+
// AST-ORIG-NEXT: CStyleCastExpr
19+
// AST-ORIG-NEXT: ImplicitCastExpr
20+
// AST-ORIG-NEXT: ParenExpr
21+
// AST-ORIG-NEXT: DeclRefExpr
22+
// AST-ORIG-NEXT: CStyleCastExpr
23+
// AST-ORIG-NEXT: ParenExpr
24+
// AST-ORIG-NEXT: UnaryOperator
25+
// AST-ORIG-NEXT: MemberExpr
26+
// AST-ORIG-NEXT: ParenExpr
27+
// AST-ORIG-NEXT: CStyleCastExpr
28+
// AST-ORIG-NEXT: ImplicitCastExpr
29+
// AST-ORIG-NEXT: IntegerLiteral
30+
31+
// AST-NEW: FunctionDecl
32+
// AST-NEW-NEXT: ParmVarDecl
33+
// AST-NEW-NEXT: CompoundStmt
34+
// AST-NEW-NEXT: ReturnStmt
35+
// AST-NEW-NEXT: ParenExpr
36+
// AST-NEW-NEXT: CStyleCastExpr
37+
// AST-NEW-NEXT: ParenExpr
38+
// AST-NEW-NEXT: BinaryOperator
39+
// AST-NEW-NEXT: CStyleCastExpr
40+
// AST-NEW-NEXT: ImplicitCastExpr
41+
// AST-NEW-NEXT: ParenExpr
42+
// AST-NEW-NEXT: DeclRefExpr
43+
// AST-NEW-NEXT: PseudoObjectExpr
44+
// AST-NEW-NEXT: CStyleCastExpr
45+
// AST-NEW-NEXT: ParenExpr
46+
// AST-NEW-NEXT: UnaryOperator
47+
// AST-NEW-NEXT: MemberExpr
48+
// AST-NEW-NEXT: ParenExpr
49+
// AST-NEW-NEXT: CStyleCastExpr
50+
// AST-NEW-NEXT: ImplicitCastExpr
51+
// AST-NEW-NEXT: IntegerLiteral
52+
// AST-NEW-NEXT: CStyleCastExpr
53+
// AST-NEW-NEXT: OffsetOfExpr
54+
55+
// AST-PRINT: constexpr MyStruct *get_container_ub(int *p) {
56+
// AST-PRINT-NEXT: return ((MyStruct *)((PCHAR)(p) - (ULONG_PTR)(&((MyStruct *)0)->b)));
57+
// AST-PRINT-NEXT: }
58+
59+
typedef char* PCHAR;
60+
typedef unsigned long long ULONG_PTR;
61+
62+
#define CONTAINING_RECORD_UB(address, type, field) ((type *)( \
63+
(PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
64+
65+
66+
struct MyStruct { int a; int b; int c; };
67+
68+
constexpr MyStruct* get_container_ub(int* p) {
69+
return CONTAINING_RECORD_UB(p, MyStruct, b);
70+
}
71+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// On amdgcn nullptr is -1. We don't rewrite AST in such case
2+
// RUN: %clang_cc1 -fms-kernel -no-enable-noundef-analysis %s -cl-std=CL2.0 -include opencl-c.h -triple amdgcn -emit-llvm -o - | FileCheck %s
3+
4+
typedef struct {
5+
private char *p1;
6+
local char *p2;
7+
constant char *p3;
8+
global char *p4;
9+
generic char *p5;
10+
} StructTy1;
11+
12+
typedef struct {
13+
constant char *p3;
14+
global char *p4;
15+
generic char *p5;
16+
} StructTy2;
17+
18+
19+
// CHECK: @fold_int5 ={{.*}} local_unnamed_addr addrspace(1) global i32 3, align 4
20+
int fold_int5 = (int) &((private StructTy1*)0)->p2;
21+
22+
// CHECK: @fold_int5_local ={{.*}} local_unnamed_addr addrspace(1) global i32 3, align 4
23+
int fold_int5_local = (int) &((local StructTy1*)0)->p2;
24+
25+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Check that sum_offsets1 and sum_offsets2 are identical
2+
// with and without -fms-kernel
3+
// RUN: %clang_cc1 -fms-kernel -fms-extensions -O2 -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s
4+
// RUN: %clang_cc1 -fms-extensions -O2 -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s
5+
6+
// CHECK: define dso_local noundef i64 @"?sum_offsets1@@YA_KXZ"()
7+
// CHECK-NEXT: entry:
8+
// CHECK-NEXT: ret i64 632
9+
10+
// CHECK: define dso_local noundef i64 @"?sum_offsets2@@YA_KXZ"()
11+
// CHECK-NEXT: entry:
12+
// CHECK-NEXT ret i64 632
13+
14+
typedef unsigned long long ULONG_PTR;
15+
16+
#define MY_OFFSETOF(type, field) (ULONG_PTR)(&((type *)0)->field)
17+
18+
namespace foo {
19+
typedef struct MyStruct {
20+
char b;
21+
struct X {
22+
long m0[10], m1;
23+
} x[10];
24+
long c;
25+
} MyStruct;
26+
}
27+
28+
__declspec(noinline) ULONG_PTR sum_offsets1() {
29+
return MY_OFFSETOF(foo::MyStruct, x[1].m0[2]) +
30+
MY_OFFSETOF(foo::MyStruct, x[2].m1) +
31+
MY_OFFSETOF(foo::MyStruct, c);
32+
}
33+
34+
__declspec(noinline) ULONG_PTR sum_offsets2() {
35+
return __builtin_offsetof(foo::MyStruct, x[1].m0[2]) +
36+
__builtin_offsetof(foo::MyStruct, x[2].m1) +
37+
__builtin_offsetof(foo::MyStruct, c);
38+
}
39+

0 commit comments

Comments
 (0)