diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2c5769f8469e..28e42591e133 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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 diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 719c3a9312ec..241281505ebf 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -64,6 +64,7 @@ add_clang_library(clangSema SemaLoongArch.cpp SemaM68k.cpp SemaMIPS.cpp + SemaMSKernel.cpp SemaMSP430.cpp SemaModule.cpp SemaNVPTX.cpp diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 6ac620184347..5cbad054e803 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -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, diff --git a/clang/lib/Sema/SemaMSKernel.cpp b/clang/lib/Sema/SemaMSKernel.cpp new file mode 100644 index 000000000000..573cd7ab529d --- /dev/null +++ b/clang/lib/Sema/SemaMSKernel.cpp @@ -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 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(Current)) { + Comp.isBrackets = false; + Comp.U.IdentInfo = ME->getMemberDecl()->getIdentifier(); + Components.push_back(Comp); + Current = ME->getBase(); + } else if (auto *ASE = dyn_cast(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(E); + if (!UO) + return {}; + ExprResult NewUO = TransformUnaryOperator(*this, UO); + return NewUO.isUsable() ? NewUO : ExprResult(); +} diff --git a/clang/test/CodeGen/MSKernel/containing-record.cpp b/clang/test/CodeGen/MSKernel/containing-record.cpp new file mode 100644 index 000000000000..e6b41f86f2f7 --- /dev/null +++ b/clang/test/CodeGen/MSKernel/containing-record.cpp @@ -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); +} + diff --git a/clang/test/CodeGen/MSKernel/nullptr.cl b/clang/test/CodeGen/MSKernel/nullptr.cl new file mode 100644 index 000000000000..9b4b66639bd3 --- /dev/null +++ b/clang/test/CodeGen/MSKernel/nullptr.cl @@ -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; + + diff --git a/clang/test/CodeGen/MSKernel/offsetof.cpp b/clang/test/CodeGen/MSKernel/offsetof.cpp new file mode 100644 index 000000000000..864a78a70d64 --- /dev/null +++ b/clang/test/CodeGen/MSKernel/offsetof.cpp @@ -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); +} +