Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIR][CIRGen] Add dynamic builtin alloca intrinsics support #547

Merged
Merged
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
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 @@ -152,6 +152,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.
orbiri marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -2475,9 +2475,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 @@ -425,29 +425,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-enable -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-enable -emit-llvm %s -o - | FileCheck %s -check-prefix=LLVM
orbiri marked this conversation as resolved.
Show resolved Hide resolved

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: }
Loading