Skip to content
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
16 changes: 16 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return cir::ConstPtrAttr::get(type, getI64IntegerAttr(value));
}

mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
mlir::Type type, llvm::StringRef name,
mlir::IntegerAttr alignment,
mlir::Value dynAllocSize) {
return cir::AllocaOp::create(*this, loc, addrType, type, name, alignment,
dynAllocSize);
}

mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
mlir::Type type, llvm::StringRef name,
clang::CharUnits alignment,
mlir::Value dynAllocSize) {
mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment);
return createAlloca(loc, addrType, type, name, alignmentAttr, dynAllocSize);
}

mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
mlir::Type type, llvm::StringRef name,
mlir::IntegerAttr alignment) {
Expand Down
21 changes: 20 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,11 @@ def CIR_AllocaOp : CIR_Op<"alloca", [
The presence of the `const` attribute indicates that the local variable is
declared with C/C++ `const` keyword.

The `dynAllocSize` specifies the size to dynamically allocate on the stack
and ignores the allocation size based on the original type. This is useful
when handling VLAs or the `alloca` builtin and is omitted when declaring
regular local variables.

The result type is a pointer to the input's type.

Example:
Expand All @@ -356,6 +361,7 @@ def CIR_AllocaOp : CIR_Op<"alloca", [
}];

let arguments = (ins
Optional<CIR_AnyFundamentalIntType>:$dynAllocSize,
TypeAttr:$allocaType,
StrAttr:$name,
UnitAttr:$init,
Expand All @@ -372,16 +378,29 @@ def CIR_AllocaOp : CIR_Op<"alloca", [
OpBuilder<(ins "mlir::Type":$addr,
"mlir::Type":$allocaType,
"llvm::StringRef":$name,
"mlir::IntegerAttr":$alignment)>
"mlir::IntegerAttr":$alignment)>,

OpBuilder<(ins "mlir::Type":$addr,
"mlir::Type":$allocaType,
"llvm::StringRef":$name,
"mlir::IntegerAttr":$alignment,
"mlir::Value":$dynAllocSize),
[{
if (dynAllocSize)
$_state.addOperands(dynAllocSize);
build($_builder, $_state, addr, allocaType, name, alignment);
}]>
];

let extraClassDeclaration = [{
// Whether the alloca input type is a pointer.
bool isPointerType() { return ::mlir::isa<::cir::PointerType>(getAllocaType()); }
bool isDynamic() { return (bool)getDynAllocSize(); }
}];

let assemblyFormat = [{
$allocaType `,` qualified(type($addr)) `,`
($dynAllocSize^ `:` type($dynAllocSize) `,`)?
`[` $name
(`,` `init` $init^)?
(`,` `const` $constant^)?
Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ struct MissingFeatures {
static bool opAllocaEscapeByReference() { return false; }
static bool opAllocaReference() { return false; }
static bool opAllocaAnnotations() { return false; }
static bool opAllocaDynAllocSize() { return false; }
static bool opAllocaCaptureByInit() { return false; }

// FuncOp handling
Expand Down
51 changes: 51 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,57 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
emitVAEnd(emitVAListRef(e->getArg(0)).getPointer());
return {};

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 = emitScalarExpr(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.
mlir::Value 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) {
// Initialize the alloca with the given size and alignment according to
// the lang opts. Only the trivial non-initialization is supported for
// now.

switch (getLangOpts().getTrivialAutoVarInit()) {
case LangOptions::TrivialAutoVarInitKind::Uninitialized:
// Nothing to initialize.
break;
case LangOptions::TrivialAutoVarInitKind::Zero:
case LangOptions::TrivialAutoVarInitKind::Pattern:
cgm.errorNYI("trivial auto var init");
break;
}
}

// 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(!cir::MissingFeatures::addressSpace());

// Bitcast the alloca to the expected type.
return RValue::get(
builder.createBitcast(allocaAddr, builder.getVoidPtrTy()));
}

case Builtin::BIfabs:
case Builtin::BIfabsf:
case Builtin::BIfabsl:
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CIR/Dialect/Transforms/HoistAllocas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ static void process(mlir::ModuleOp mod, cir::FuncOp func) {
if (alloca->getBlock() == &entryBlock)
return;
// Don't hoist allocas with dynamic alloca size.
assert(!cir::MissingFeatures::opAllocaDynAllocSize());
if (alloca.getDynAllocSize())
return;

// Hoist allocas into the entry block.

Expand Down
9 changes: 6 additions & 3 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1053,9 +1053,12 @@ mlir::LogicalResult CIRToLLVMBaseClassAddrOpLowering::matchAndRewrite(
mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite(
cir::AllocaOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
assert(!cir::MissingFeatures::opAllocaDynAllocSize());
mlir::Value size = rewriter.create<mlir::LLVM::ConstantOp>(
op.getLoc(), typeConverter->convertType(rewriter.getIndexType()), 1);
mlir::Value size =
op.isDynamic()
? adaptor.getDynAllocSize()
: rewriter.create<mlir::LLVM::ConstantOp>(
op.getLoc(),
typeConverter->convertType(rewriter.getIndexType()), 1);
mlir::Type elementTy =
convertTypeForMemory(*getTypeConverter(), dataLayout, op.getAllocaType());
mlir::Type resultTy =
Expand Down
48 changes: 48 additions & 0 deletions clang/test/CIR/CodeGen/builtin_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ void unreachable() {
// LLVM: unreachable
// LLVM: }

// OGCG-LABEL: @_Z11unreachablev
// OGCG: unreachable
// OGCG: }

void f1();
void unreachable2() {
__builtin_unreachable();
Expand All @@ -229,6 +233,9 @@ void unreachable2() {
// LLVM-NEXT: call void @_Z2f1v()
// LLVM: }

// OGCG-LABEL: @_Z12unreachable2v
// OGCG: unreachable

void trap() {
__builtin_trap();
}
Expand All @@ -241,6 +248,10 @@ void trap() {
// LLVM: call void @llvm.trap()
// LLVM: }

// OGCG-LABEL: @_Z4trapv
// OGCG: call void @llvm.trap()
// OGCG: }

void trap2() {
__builtin_trap();
f1();
Expand All @@ -258,3 +269,40 @@ void trap2() {
// LLVM: {{.+}}:
// LLVM-NEXT: call void @_Z2f1v()
// LLVM: }

// OGCG-LABEL: define{{.*}} void @_Z5trap2v
// OGCG: call void @llvm.trap()
// OGCG-NEXT: call void @_Z2f1v()
// OGCG: ret void
// OGCG: }

void *test_alloca(unsigned long n) {
return __builtin_alloca(n);
}

// CIR-LABEL: @_Z11test_allocam(
// CIR: %{{.+}} = cir.alloca !u8i, !cir.ptr<!u8i>, %{{.+}} : !u64i, ["bi_alloca"]

// LLVM-LABEL: @_Z11test_allocam(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the OGCG checks? This test seems to have started drifting away from doing that for some reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a some missing OGCG checks with the exception of __builtin_expect and __builtin_expect_with_probability because OGCG doesn't lower to the LLVM intrinsic.

// LLVM: alloca i8, i64 %{{.+}}

// OGCG-LABEL: @_Z11test_allocam(
// OGCG: alloca i8, i64 %{{.+}}

bool test_multiple_allocas(unsigned long n) {
void *a = __builtin_alloca(n);
void *b = __builtin_alloca(n);
return a != b;
}

// CIR-LABEL: @_Z21test_multiple_allocasm(
// CIR: %{{.+}} = cir.alloca !u8i, !cir.ptr<!u8i>, %{{.+}} : !u64i, ["bi_alloca"]
// CIR: %{{.+}} = cir.alloca !u8i, !cir.ptr<!u8i>, %{{.+}} : !u64i, ["bi_alloca"]

// LLVM-LABEL: @_Z21test_multiple_allocasm(
// LLVM: alloca i8, i64 %{{.+}}
// LLVM: alloca i8, i64 %{{.+}}

// OGCG-LABEL: @_Z21test_multiple_allocasm(
// OGCG: alloca i8, i64 %{{.+}}
// OGCG: alloca i8, i64 %{{.+}}
32 changes: 32 additions & 0 deletions clang/test/CIR/IR/alloca.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

// RUN: cir-opt %s | FileCheck %s

!u64i = !cir.int<u, 64>
!u8i = !cir.int<u, 8>
!void = !cir.void
module {
cir.func dso_local @_Z11test_allocam(%arg0: !u64i) -> !cir.ptr<!void> {
%0 = cir.alloca !u64i, !cir.ptr<!u64i>, ["n", init] {alignment = 8 : i64}
%1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] {alignment = 8 : i64}
cir.store %arg0, %0 : !u64i, !cir.ptr<!u64i>
%2 = cir.load align(8) %0 : !cir.ptr<!u64i>, !u64i
// Dynamically sized alloca
%3 = cir.alloca !u8i, !cir.ptr<!u8i>, %2 : !u64i, ["bi_alloca"] {alignment = 16 : i64}
%4 = cir.cast(bitcast, %3 : !cir.ptr<!u8i>), !cir.ptr<!void>
cir.store %4, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
%5 = cir.load %1 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
cir.return %5 : !cir.ptr<!void>
}

// CHECK: cir.func dso_local @_Z11test_allocam(%arg0: !u64i) -> !cir.ptr<!void> {
// CHECK: %0 = cir.alloca !u64i, !cir.ptr<!u64i>, ["n", init] {alignment = 8 : i64}
// CHECK: %1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__retval"] {alignment = 8 : i64}
// CHECK: cir.store %arg0, %0 : !u64i, !cir.ptr<!u64i>
// CHECK: %2 = cir.load align(8) %0 : !cir.ptr<!u64i>, !u64i
// CHECK: %3 = cir.alloca !u8i, !cir.ptr<!u8i>, %2 : !u64i, ["bi_alloca"] {alignment = 16 : i64}
// CHECK: %4 = cir.cast(bitcast, %3 : !cir.ptr<!u8i>), !cir.ptr<!void>
// CHECK: cir.store %4, %1 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CHECK: %5 = cir.load %1 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
// CHECK: cir.return %5 : !cir.ptr<!void>
// CHECK: }
}