Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -11942,6 +11942,10 @@ class Sema final : public SemaBase {
DeclarationName Name);
bool RebuildNestedNameSpecifierInCurrentInstantiation(CXXScopeSpec &SS);

/// Do MS kernel specific AST transformations, e.g replace
/// &((type*)0)->member with __builtin_offsetof(type, member)
ExprResult TransformForMSKernel(Expr *UOp);

ExprResult RebuildExprInCurrentInstantiation(Expr *E);

/// Rebuild the template parameters now that we know we're in a current
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ add_clang_library(clangSema
SemaLoongArch.cpp
SemaM68k.cpp
SemaMIPS.cpp
SemaMSKernel.cpp
SemaMSP430.cpp
SemaModule.cpp
SemaNVPTX.cpp
Expand Down
17 changes: 16 additions & 1 deletion clang/lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3364,9 +3364,24 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc,
// -Wcast-qual
DiagnoseCastQual(Op.Self, Op.SrcExpr, Op.DestType);

return Op.complete(CStyleCastExpr::Create(
ExprResult Cast = Op.complete(CStyleCastExpr::Create(
Context, Op.ResultType, Op.ValueKind, Op.Kind, Op.SrcExpr.get(),
&Op.BasePath, CurFPFeatureOverrides(), CastTypeInfo, LPLoc, RPLoc));
if (!Cast.isUsable() || Op.Kind != CK_PointerToIntegral ||
!Context.getLangOpts().Kernel)
return Cast;

ExprResult Transformed =
TransformForMSKernel(Op.SrcExpr.get()->IgnoreParenImpCasts());
if (!Transformed.isUsable())
return Cast;

ExprResult NewCast =
BuildCStyleCastExpr(LPLoc, CastTypeInfo, RPLoc, Transformed.get());
if (!NewCast.isUsable())
return NewCast;
Expr *RealExpr = NewCast.get();
return PseudoObjectExpr::Create(Context, Cast.get(), {&RealExpr, 1}, 0);
}

ExprResult Sema::BuildCXXFunctionalCastExpr(TypeSourceInfo *CastTypeInfo,
Expand Down
71 changes: 71 additions & 0 deletions clang/lib/Sema/SemaMSKernel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "TreeTransform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaConsumer.h"

using namespace clang;

namespace {
ExprResult TransformUnaryOperator(Sema &SemaRef, UnaryOperator *UO) {
if (UO->getOpcode() != UO_AddrOf)
return {};

SmallVector<Sema::OffsetOfComponent, 4> Components;
Expr *Current = UO->getSubExpr()->IgnoreParens();
while (true) {
// Strip away the "noise" added by array decays or parentheses
Current = Current->IgnoreParenImpCasts();
Sema::OffsetOfComponent Comp;
Comp.LocStart = Current->getBeginLoc();
Comp.LocEnd = Current->getEndLoc();

if (auto *ME = dyn_cast<MemberExpr>(Current)) {
Comp.isBrackets = false;
Comp.U.IdentInfo = ME->getMemberDecl()->getIdentifier();
Components.push_back(Comp);
Current = ME->getBase();
} else if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Current)) {
Comp.isBrackets = true;
// In offsetof, the index must be an expression
Comp.U.E = ASE->getIdx();
Components.push_back(Comp);
Current = ASE->getBase();
} else {
// No more members or subscripts
break;
}
}
// Verify we ended at a Null Pointer Cast
Expr *Base = Current->IgnoreParenCasts();
if (!Components.empty() &&
Base->isNullPointerConstant(SemaRef.Context,
Expr::NPC_ValueDependentIsNotNull)) {
// Don't treat &((MyStruct*)0)[1] as an offsetof expression
if (Components.back().isBrackets)
return {};
// Targets like amdgcn, where nullptr != 0, are ignored
if (SemaRef.Context.getTargetNullPointerValue(Current->getType()))
return {};
std::reverse(Components.begin(), Components.end());

// Get the root structure type
TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(
Current->getType()->getPointeeType(), Current->getBeginLoc());
return SemaRef.BuildBuiltinOffsetOf(UO->getBeginLoc(), TInfo, Components,
UO->getEndLoc());
}

return {};
}
} // end anonymous namespace

ExprResult clang::Sema::TransformForMSKernel(Expr *E) {
auto *UO = dyn_cast_or_null<UnaryOperator>(E);
if (!UO)
return {};
ExprResult NewUO = TransformUnaryOperator(*this, UO);
return NewUO.isUsable() ? NewUO : ExprResult();
}
71 changes: 71 additions & 0 deletions clang/test/CodeGen/MSKernel/containing-record.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Normally this file can't be compiled due to
// nullptr cast in offset calculation
// RUN: not %clang_cc1 -triple x86_64-pc-win32 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
// Kernel variant works OK.
// RUN: %clang_cc1 -triple x86_64-pc-win32 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
// We use PseudoObjectExpr, so reconstruction of source from AST should work as expected
// RUN: %clang_cc1 -triple x86_64-pc-win32 -fms-kernel -ast-print %s -o - | FileCheck %s --check-prefix=AST-PRINT

// AST-ORIG: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
// AST-ORIG: FunctionDecl
// AST-ORIG-NEXT: ParmVarDecl
// AST-ORIG-NEXT: CompoundStmt
// AST-ORIG-NEXT: ReturnStmt
// AST-ORIG-NEXT: ParenExpr
// AST-ORIG-NEXT: CStyleCastExpr
// AST-ORIG-NEXT: ParenExpr
// AST-ORIG-NEXT: BinaryOperator
// AST-ORIG-NEXT: CStyleCastExpr
// AST-ORIG-NEXT: ImplicitCastExpr
// AST-ORIG-NEXT: ParenExpr
// AST-ORIG-NEXT: DeclRefExpr
// AST-ORIG-NEXT: CStyleCastExpr
// AST-ORIG-NEXT: ParenExpr
// AST-ORIG-NEXT: UnaryOperator
// AST-ORIG-NEXT: MemberExpr
// AST-ORIG-NEXT: ParenExpr
// AST-ORIG-NEXT: CStyleCastExpr
// AST-ORIG-NEXT: ImplicitCastExpr
// AST-ORIG-NEXT: IntegerLiteral

// AST-NEW: FunctionDecl
// AST-NEW-NEXT: ParmVarDecl
// AST-NEW-NEXT: CompoundStmt
// AST-NEW-NEXT: ReturnStmt
// AST-NEW-NEXT: ParenExpr
// AST-NEW-NEXT: CStyleCastExpr
// AST-NEW-NEXT: ParenExpr
// AST-NEW-NEXT: BinaryOperator
// AST-NEW-NEXT: CStyleCastExpr
// AST-NEW-NEXT: ImplicitCastExpr
// AST-NEW-NEXT: ParenExpr
// AST-NEW-NEXT: DeclRefExpr
// AST-NEW-NEXT: PseudoObjectExpr
// AST-NEW-NEXT: CStyleCastExpr
// AST-NEW-NEXT: ParenExpr
// AST-NEW-NEXT: UnaryOperator
// AST-NEW-NEXT: MemberExpr
// AST-NEW-NEXT: ParenExpr
// AST-NEW-NEXT: CStyleCastExpr
// AST-NEW-NEXT: ImplicitCastExpr
// AST-NEW-NEXT: IntegerLiteral
// AST-NEW-NEXT: CStyleCastExpr
// AST-NEW-NEXT: OffsetOfExpr

// AST-PRINT: constexpr MyStruct *get_container_ub(int *p) {
// AST-PRINT-NEXT: return ((MyStruct *)((PCHAR)(p) - (ULONG_PTR)(&((MyStruct *)0)->b)));
// AST-PRINT-NEXT: }

typedef char* PCHAR;
typedef unsigned long long ULONG_PTR;

#define CONTAINING_RECORD_UB(address, type, field) ((type *)( \
(PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))


struct MyStruct { int a; int b; int c; };

constexpr MyStruct* get_container_ub(int* p) {
return CONTAINING_RECORD_UB(p, MyStruct, b);
}

25 changes: 25 additions & 0 deletions clang/test/CodeGen/MSKernel/nullptr.cl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// On amdgcn nullptr is -1. We don't rewrite AST in such case
// 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

typedef struct {
private char *p1;
local char *p2;
constant char *p3;
global char *p4;
generic char *p5;
} StructTy1;

typedef struct {
constant char *p3;
global char *p4;
generic char *p5;
} StructTy2;


// CHECK: @fold_int5 ={{.*}} local_unnamed_addr addrspace(1) global i32 3, align 4
int fold_int5 = (int) &((private StructTy1*)0)->p2;

// CHECK: @fold_int5_local ={{.*}} local_unnamed_addr addrspace(1) global i32 3, align 4
int fold_int5_local = (int) &((local StructTy1*)0)->p2;


39 changes: 39 additions & 0 deletions clang/test/CodeGen/MSKernel/offsetof.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Check that sum_offsets1 and sum_offsets2 are identical
// with and without -fms-kernel
// RUN: %clang_cc1 -fms-kernel -fms-extensions -O2 -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -fms-extensions -O2 -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s

// CHECK: define dso_local noundef i64 @"?sum_offsets1@@YA_KXZ"()
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i64 632

// CHECK: define dso_local noundef i64 @"?sum_offsets2@@YA_KXZ"()
// CHECK-NEXT: entry:
// CHECK-NEXT ret i64 632

typedef unsigned long long ULONG_PTR;

#define MY_OFFSETOF(type, field) (ULONG_PTR)(&((type *)0)->field)

namespace foo {
typedef struct MyStruct {
char b;
struct X {
long m0[10], m1;
} x[10];
long c;
} MyStruct;
}

__declspec(noinline) ULONG_PTR sum_offsets1() {
return MY_OFFSETOF(foo::MyStruct, x[1].m0[2]) +
MY_OFFSETOF(foo::MyStruct, x[2].m1) +
MY_OFFSETOF(foo::MyStruct, c);
}

__declspec(noinline) ULONG_PTR sum_offsets2() {
return __builtin_offsetof(foo::MyStruct, x[1].m0[2]) +
__builtin_offsetof(foo::MyStruct, x[2].m1) +
__builtin_offsetof(foo::MyStruct, c);
}