From 6355c90aaf0fbacd89df2445c5c66e760b89d16f Mon Sep 17 00:00:00 2001 From: orbiri Date: Fri, 26 Apr 2024 00:05:42 +0300 Subject: [PATCH] [CIR][CIRGen] Add dynamic builtin alloca intrinsics support (#547) This patch adds the CIRGen for the following builtin functions: - `alloca`; - `_alloca`; - `__builtin_alloca`; - `__builtin_alloca_uninitialized`. Missing support to add in the future: - Non-default auto initialization setting. The default is to not initialize the allocated buffer, which is simpler to implement. This commit is leaving the skeleton to implement this feature following clang's codegen pattern. - It may be possible that the frontend has set non-default address space for the alloca's return value. This is the case for OpenCL or AMDGPU codes for example. This is handled in clang codegen via address space cast, and is left for future implementation. This commit introduces a guard-rail around this behaviour. --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 30 +++++++++ clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 61 ++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 5 +- .../Dialect/Transforms/LoweringPrepare.cpp | 13 ++-- clang/test/CIR/CodeGen/builtin-alloca.c | 62 +++++++++++++++++++ clang/test/CIR/CodeGen/builtin-ms-alloca.c | 23 +++++++ 6 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 clang/test/CIR/CodeGen/builtin-alloca.c create mode 100644 clang/test/CIR/CodeGen/builtin-ms-alloca.c diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index f5635b7ca33b..a3df0ef0dcdc 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -160,6 +160,36 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create(loc, val, dst, _volatile, order); } + mlir::Value createAlloca(mlir::Location loc, mlir::cir::PointerType addrType, + mlir::Type type, llvm::StringRef name, + mlir::IntegerAttr alignment, + mlir::Value dynAllocSize) { + return create(loc, addrType, type, name, alignment, + dynAllocSize); + } + + mlir::Value createAlloca(mlir::Location loc, mlir::cir::PointerType addrType, + mlir::Type type, llvm::StringRef name, + clang::CharUnits alignment, + mlir::Value dynAllocSize) { + auto alignmentIntAttr = getSizeFromCharUnits(getContext(), alignment); + return createAlloca(loc, addrType, type, name, alignmentIntAttr, + dynAllocSize); + } + + mlir::Value createAlloca(mlir::Location loc, mlir::cir::PointerType addrType, + mlir::Type type, llvm::StringRef name, + mlir::IntegerAttr alignment) { + return create(loc, addrType, type, name, alignment); + } + + mlir::Value createAlloca(mlir::Location loc, mlir::cir::PointerType addrType, + mlir::Type type, llvm::StringRef name, + clang::CharUnits alignment) { + auto alignmentIntAttr = getSizeFromCharUnits(getContext(), alignment); + return createAlloca(loc, addrType, type, name, alignmentIntAttr); + } + mlir::Value createSub(mlir::Value lhs, mlir::Value rhs, bool hasNUW = false, bool hasNSW = false) { auto op = create(lhs.getLoc(), lhs.getType(), diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index bd6865ba8bc5..4061e54811a6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -71,6 +71,24 @@ buildBuiltinBitOp(CIRGenFunction &CGF, const CallExpr *E, return RValue::get(op); } +// Initialize the alloca with the given size and alignment according to the lang +// opts. Supporting only the trivial non-initialization for now. +static void initializeAlloca(CIRGenFunction &CGF, + [[maybe_unused]] mlir::Value AllocaAddr, + [[maybe_unused]] mlir::Value Size, + [[maybe_unused]] CharUnits AlignmentInBytes) { + + switch (CGF.getLangOpts().getTrivialAutoVarInit()) { + case LangOptions::TrivialAutoVarInitKind::Uninitialized: + // Nothing to initialize. + return; + case LangOptions::TrivialAutoVarInitKind::Zero: + case LangOptions::TrivialAutoVarInitKind::Pattern: + assert(false && "unexpected trivial auto var init kind NYI"); + return; + } +} + RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, const CallExpr *E, ReturnValueSlot ReturnValue) { @@ -642,6 +660,49 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Result = builder.createBoolToInt(Result, ResultType); return RValue::get(Result); } + + case Builtin::BIalloca: + case Builtin::BI_alloca: + case Builtin::BI__builtin_alloca_uninitialized: + case Builtin::BI__builtin_alloca: { + // Get alloca size input + mlir::Value Size = buildScalarExpr(E->getArg(0)); + + // The alignment of the alloca should correspond to __BIGGEST_ALIGNMENT__. + const TargetInfo &TI = getContext().getTargetInfo(); + const CharUnits SuitableAlignmentInBytes = + getContext().toCharUnitsFromBits(TI.getSuitableAlign()); + + // Emit the alloca op with type `u8 *` to match the semantics of + // `llvm.alloca`. We later bitcast the type to `void *` to match the + // semantics of C/C++ + // FIXME(cir): It may make sense to allow AllocaOp of type `u8` to return a + // pointer of type `void *`. This will require a change to the allocaOp + // verifier. + auto AllocaAddr = builder.createAlloca( + getLoc(E->getSourceRange()), builder.getUInt8PtrTy(), + builder.getUInt8Ty(), "bi_alloca", SuitableAlignmentInBytes, Size); + + // Initialize the allocated buffer if required. + if (BuiltinID != Builtin::BI__builtin_alloca_uninitialized) + initializeAlloca(*this, AllocaAddr, Size, SuitableAlignmentInBytes); + + // An alloca will always return a pointer to the alloca (stack) address + // space. This address space need not be the same as the AST / Language + // default (e.g. in C / C++ auto vars are in the generic address space). At + // the AST level this is handled within CreateTempAlloca et al., but for the + // builtin / dynamic alloca we have to handle it here. + assert(!UnimplementedFeature::addressSpace()); + LangAS AAS = getASTAllocaAddressSpace(); + LangAS EAS = E->getType()->getPointeeType().getAddressSpace(); + if (EAS != AAS) { + assert(false && "Non-default address space for alloca NYI"); + } + + // Bitcast the alloca to the expected type. + return RValue::get( + builder.createBitcast(AllocaAddr, builder.getVoidPtrTy())); + } } // If this is an alias for a lib function (e.g. __builtin_sin), emit diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index e0242268c562..dbb65d92290e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -2479,9 +2479,8 @@ mlir::Value CIRGenFunction::buildAlloca(StringRef name, mlir::Type ty, { mlir::OpBuilder::InsertionGuard guard(builder); builder.restoreInsertionPoint(ip); - addr = builder.create(loc, /*addr type*/ localVarPtrTy, - /*var type*/ ty, name, - alignIntAttr, arraySize); + addr = builder.createAlloca(loc, /*addr type*/ localVarPtrTy, + /*var type*/ ty, name, alignIntAttr, arraySize); if (currVarDecl) { auto alloca = cast(addr.getDefiningOp()); alloca.setAstAttr(ASTVarDeclAttr::get(builder.getContext(), currVarDecl)); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 059a5b82167f..e92e40b7ccd4 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -497,20 +497,16 @@ static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder, mlir::Value end = builder.create( loc, eltTy, begin, numArrayElementsConst); - auto tmpAddr = builder.create( + auto tmpAddr = builder.createAlloca( loc, /*addr type*/ builder.getPointerTo(eltTy), - /*var type*/ eltTy, "__array_idx", - builder.getSizeFromCharUnits(builder.getContext(), - clang::CharUnits::One()), - nullptr); + /*var type*/ eltTy, "__array_idx", clang::CharUnits::One()); builder.createStore(loc, begin, tmpAddr); auto loop = builder.createDoWhile( loc, /*condBuilder=*/ [&](mlir::OpBuilder &b, mlir::Location loc) { - auto currentElement = - b.create(loc, eltTy, tmpAddr.getResult()); + auto currentElement = b.create(loc, eltTy, tmpAddr); mlir::Type boolTy = mlir::cir::BoolType::get(b.getContext()); auto cmp = builder.create( loc, boolTy, mlir::cir::CmpOpKind::eq, currentElement, end); @@ -518,8 +514,7 @@ static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder, }, /*bodyBuilder=*/ [&](mlir::OpBuilder &b, mlir::Location loc) { - auto currentElement = - b.create(loc, eltTy, tmpAddr.getResult()); + auto currentElement = b.create(loc, eltTy, tmpAddr); CallOp ctorCall; op->walk([&](CallOp c) { ctorCall = c; }); diff --git a/clang/test/CIR/CodeGen/builtin-alloca.c b/clang/test/CIR/CodeGen/builtin-alloca.c new file mode 100644 index 000000000000..a02f328cc12f --- /dev/null +++ b/clang/test/CIR/CodeGen/builtin-alloca.c @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %s -o - | FileCheck %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s -check-prefix=LLVM + +typedef __SIZE_TYPE__ size_t; +void *alloca(size_t size); +void *_alloca(size_t size); + +void my_alloca(size_t n) +{ + int *c1 = alloca(n); +} +// CIR: cir.func @my_alloca([[ALLOCA_SIZE:%.*]]: !u64i +// CIR: cir.store [[ALLOCA_SIZE]], [[LOCAL_VAR_ALLOCA_SIZE:%.*]] : !u64i, cir.ptr +// CIR: [[TMP_ALLOCA_SIZE:%.*]] = cir.load [[LOCAL_VAR_ALLOCA_SIZE]] : cir.ptr , !u64i +// CIR: [[ALLOCA_RES:%.*]] = cir.alloca !u8i, cir.ptr , [[TMP_ALLOCA_SIZE]] : !u64i, ["bi_alloca"] {alignment = 16 : i64} +// CIR-NEXT: cir.cast(bitcast, [[ALLOCA_RES]] : !cir.ptr), !cir.ptr +// CIR: } + + +// LLVM: define void @my_alloca(i64 [[ALLOCA_SIZE:%.*]]) +// LLVM: store i64 [[ALLOCA_SIZE]], ptr [[LOCAL_VAR_ALLOCA_SIZE:%.*]], +// LLVM: [[TMP_ALLOCA_SIZE:%.*]] = load i64, ptr [[LOCAL_VAR_ALLOCA_SIZE]], +// LLVM: [[ALLOCA_RES:%.*]] = alloca i8, i64 [[TMP_ALLOCA_SIZE]], align 16 +// LLVM: } + +void my___builtin_alloca(size_t n) +{ + int *c1 = (int *)__builtin_alloca(n); +} + +// CIR: cir.func @my___builtin_alloca([[ALLOCA_SIZE:%.*]]: !u64i +// CIR: cir.store [[ALLOCA_SIZE]], [[LOCAL_VAR_ALLOCA_SIZE:%.*]] : !u64i, cir.ptr +// CIR: [[TMP_ALLOCA_SIZE:%.*]] = cir.load [[LOCAL_VAR_ALLOCA_SIZE]] : cir.ptr , !u64i +// CIR: [[ALLOCA_RES:%.*]] = cir.alloca !u8i, cir.ptr , [[TMP_ALLOCA_SIZE]] : !u64i, ["bi_alloca"] {alignment = 16 : i64} +// CIR-NEXT: cir.cast(bitcast, [[ALLOCA_RES]] : !cir.ptr), !cir.ptr +// CIR: } + + +// LLVM: define void @my___builtin_alloca(i64 [[ALLOCA_SIZE:%.*]]) +// LLVM: store i64 [[ALLOCA_SIZE]], ptr [[LOCAL_VAR_ALLOCA_SIZE:%.*]], +// LLVM: [[TMP_ALLOCA_SIZE:%.*]] = load i64, ptr [[LOCAL_VAR_ALLOCA_SIZE]], +// LLVM: [[ALLOCA_RES:%.*]] = alloca i8, i64 [[TMP_ALLOCA_SIZE]], align 16 +// LLVM: } + +void my__builtin_alloca_uninitialized(size_t n) +{ + int *c1 = (int *)__builtin_alloca_uninitialized(n); +} + +// CIR: cir.func @my__builtin_alloca_uninitialized([[ALLOCA_SIZE:%.*]]: !u64i +// CIR: cir.store [[ALLOCA_SIZE]], [[LOCAL_VAR_ALLOCA_SIZE:%.*]] : !u64i, cir.ptr +// CIR: [[TMP_ALLOCA_SIZE:%.*]] = cir.load [[LOCAL_VAR_ALLOCA_SIZE]] : cir.ptr , !u64i +// CIR: [[ALLOCA_RES:%.*]] = cir.alloca !u8i, cir.ptr , [[TMP_ALLOCA_SIZE]] : !u64i, ["bi_alloca"] {alignment = 16 : i64} +// CIR-NEXT: cir.cast(bitcast, [[ALLOCA_RES]] : !cir.ptr), !cir.ptr +// CIR: } + + +// LLVM: define void @my__builtin_alloca_uninitialized(i64 [[ALLOCA_SIZE:%.*]]) +// LLVM: store i64 [[ALLOCA_SIZE]], ptr [[LOCAL_VAR_ALLOCA_SIZE:%.*]], +// LLVM: [[TMP_ALLOCA_SIZE:%.*]] = load i64, ptr [[LOCAL_VAR_ALLOCA_SIZE]], +// LLVM: [[ALLOCA_RES:%.*]] = alloca i8, i64 [[TMP_ALLOCA_SIZE]], align 16 +// LLVM: } diff --git a/clang/test/CIR/CodeGen/builtin-ms-alloca.c b/clang/test/CIR/CodeGen/builtin-ms-alloca.c new file mode 100644 index 000000000000..d500304d7f6d --- /dev/null +++ b/clang/test/CIR/CodeGen/builtin-ms-alloca.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fms-extensions -emit-cir %s -o - | FileCheck %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fms-extensions -fclangir -emit-llvm %s -o - | FileCheck %s -check-prefix=LLVM + +typedef __SIZE_TYPE__ size_t; + +void my_win_alloca(size_t n) +{ + int *c1 = (int *)_alloca(n); +} + +// CIR: cir.func @my_win_alloca([[ALLOCA_SIZE:%.*]]: !u64i +// CIR: cir.store [[ALLOCA_SIZE]], [[LOCAL_VAR_ALLOCA_SIZE:%.*]] : !u64i, cir.ptr +// CIR: [[TMP_ALLOCA_SIZE:%.*]] = cir.load [[LOCAL_VAR_ALLOCA_SIZE]] : cir.ptr , !u64i +// CIR: [[ALLOCA_RES:%.*]] = cir.alloca !u8i, cir.ptr , [[TMP_ALLOCA_SIZE]] : !u64i, ["bi_alloca"] {alignment = 16 : i64} +// CIR-NEXT: cir.cast(bitcast, [[ALLOCA_RES]] : !cir.ptr), !cir.ptr +// CIR: } + + +// LLVM: define void @my_win_alloca(i64 [[ALLOCA_SIZE:%.*]]) +// LLVM: store i64 [[ALLOCA_SIZE]], ptr [[LOCAL_VAR_ALLOCA_SIZE:%.*]], +// LLVM: [[TMP_ALLOCA_SIZE:%.*]] = load i64, ptr [[LOCAL_VAR_ALLOCA_SIZE]], +// LLVM: [[ALLOCA_RES:%.*]] = alloca i8, i64 [[TMP_ALLOCA_SIZE]], align 16 +// LLVM: }