Skip to content

Commit

Permalink
[CIR][CIRGen] Add dynamic builtin alloca intrinsics support (#547)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
orbiri authored and lanza committed Apr 29, 2024
1 parent 612a1c7 commit 6355c90
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 12 deletions.
30 changes: 30 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,36 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return create<mlir::cir::StoreOp>(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<mlir::cir::AllocaOp>(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<mlir::cir::AllocaOp>(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<mlir::cir::BinOp>(lhs.getLoc(), lhs.getType(),
Expand Down
61 changes: 61 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2479,9 +2479,8 @@ mlir::Value CIRGenFunction::buildAlloca(StringRef name, mlir::Type ty,
{
mlir::OpBuilder::InsertionGuard guard(builder);
builder.restoreInsertionPoint(ip);
addr = builder.create<mlir::cir::AllocaOp>(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<mlir::cir::AllocaOp>(addr.getDefiningOp());
alloca.setAstAttr(ASTVarDeclAttr::get(builder.getContext(), currVarDecl));
Expand Down
13 changes: 4 additions & 9 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,29 +497,24 @@ static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder,
mlir::Value end = builder.create<mlir::cir::PtrStrideOp>(
loc, eltTy, begin, numArrayElementsConst);

auto tmpAddr = builder.create<mlir::cir::AllocaOp>(
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<mlir::cir::LoadOp>(loc, eltTy, tmpAddr.getResult());
auto currentElement = b.create<mlir::cir::LoadOp>(loc, eltTy, tmpAddr);
mlir::Type boolTy = mlir::cir::BoolType::get(b.getContext());
auto cmp = builder.create<mlir::cir::CmpOp>(
loc, boolTy, mlir::cir::CmpOpKind::eq, currentElement, end);
builder.createCondition(cmp);
},
/*bodyBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
auto currentElement =
b.create<mlir::cir::LoadOp>(loc, eltTy, tmpAddr.getResult());
auto currentElement = b.create<mlir::cir::LoadOp>(loc, eltTy, tmpAddr);

CallOp ctorCall;
op->walk([&](CallOp c) { ctorCall = c; });
Expand Down
62 changes: 62 additions & 0 deletions clang/test/CIR/CodeGen/builtin-alloca.c
Original file line number Diff line number Diff line change
@@ -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 <!u64i>
// CIR: [[TMP_ALLOCA_SIZE:%.*]] = cir.load [[LOCAL_VAR_ALLOCA_SIZE]] : cir.ptr <!u64i>, !u64i
// CIR: [[ALLOCA_RES:%.*]] = cir.alloca !u8i, cir.ptr <!u8i>, [[TMP_ALLOCA_SIZE]] : !u64i, ["bi_alloca"] {alignment = 16 : i64}
// CIR-NEXT: cir.cast(bitcast, [[ALLOCA_RES]] : !cir.ptr<!u8i>), !cir.ptr<!void>
// 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 <!u64i>
// CIR: [[TMP_ALLOCA_SIZE:%.*]] = cir.load [[LOCAL_VAR_ALLOCA_SIZE]] : cir.ptr <!u64i>, !u64i
// CIR: [[ALLOCA_RES:%.*]] = cir.alloca !u8i, cir.ptr <!u8i>, [[TMP_ALLOCA_SIZE]] : !u64i, ["bi_alloca"] {alignment = 16 : i64}
// CIR-NEXT: cir.cast(bitcast, [[ALLOCA_RES]] : !cir.ptr<!u8i>), !cir.ptr<!void>
// 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 <!u64i>
// CIR: [[TMP_ALLOCA_SIZE:%.*]] = cir.load [[LOCAL_VAR_ALLOCA_SIZE]] : cir.ptr <!u64i>, !u64i
// CIR: [[ALLOCA_RES:%.*]] = cir.alloca !u8i, cir.ptr <!u8i>, [[TMP_ALLOCA_SIZE]] : !u64i, ["bi_alloca"] {alignment = 16 : i64}
// CIR-NEXT: cir.cast(bitcast, [[ALLOCA_RES]] : !cir.ptr<!u8i>), !cir.ptr<!void>
// 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: }
23 changes: 23 additions & 0 deletions clang/test/CIR/CodeGen/builtin-ms-alloca.c
Original file line number Diff line number Diff line change
@@ -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 <!u64i>
// CIR: [[TMP_ALLOCA_SIZE:%.*]] = cir.load [[LOCAL_VAR_ALLOCA_SIZE]] : cir.ptr <!u64i>, !u64i
// CIR: [[ALLOCA_RES:%.*]] = cir.alloca !u8i, cir.ptr <!u8i>, [[TMP_ALLOCA_SIZE]] : !u64i, ["bi_alloca"] {alignment = 16 : i64}
// CIR-NEXT: cir.cast(bitcast, [[ALLOCA_RES]] : !cir.ptr<!u8i>), !cir.ptr<!void>
// 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: }

0 comments on commit 6355c90

Please sign in to comment.