Skip to content

Commit

Permalink
[CIR][CodeGen] Initial variable length array support (#398)
Browse files Browse the repository at this point in the history
This is a first PR for variable length array support. There are one (or
more :) ) ahead.

Basically, we already did lot's of preliminary job in order to land VLA
in CIR in #367 #346 #340. So now we add initial VLA support itself.

Most of the changes are taken from the original codegen, so there is
nothing to be scary of)

I added just one test, and basically that's all we can test right now.
Later, I will add more, from the original codegen tests.
  • Loading branch information
gitoleg authored and lanza committed Mar 23, 2024
1 parent f78515d commit e4fee87
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 8 deletions.
9 changes: 9 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return createBinop(lhs, mlir::cir::BinOpKind::Or, rhs);
}

mlir::Value createMul(mlir::Value lhs, mlir::Value rhs) {
return createBinop(lhs, mlir::cir::BinOpKind::Mul, rhs);
}

mlir::Value createMul(mlir::Value lhs, llvm::APInt rhs) {
auto val = getConstAPInt(lhs.getLoc(), lhs.getType(), rhs);
return createBinop(lhs, mlir::cir::BinOpKind::Mul, val);
}

//===--------------------------------------------------------------------===//
// Cast/Conversion Operators
//===--------------------------------------------------------------------===//
Expand Down
50 changes: 47 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ CIRGenFunction::buildAutoVarAlloca(const VarDecl &D,
// getLangOpts().OpenCL))
assert(!UnimplementedFeature::openCL());
assert(Ty.getAddressSpace() == LangAS::Default);
assert(!Ty->isVariablyModifiedType() && "not implemented");
assert(!D.hasAttr<AnnotateAttr>() && "not implemented");

auto loc = getLoc(D.getSourceRange());
Expand All @@ -51,6 +50,11 @@ CIRGenFunction::buildAutoVarAlloca(const VarDecl &D,
emission.IsEscapingByRef = isEscapingByRef;

CharUnits alignment = getContext().getDeclAlign(&D);

// If the type is variably-modified, emit all the VLA sizes for it.
if (Ty->isVariablyModifiedType())
buildVariablyModifiedType(Ty);

assert(!UnimplementedFeature::generateDebugInfo());
assert(!UnimplementedFeature::cxxABI());

Expand Down Expand Up @@ -146,7 +150,41 @@ CIRGenFunction::buildAutoVarAlloca(const VarDecl &D,
assert(!UnimplementedFeature::shouldEmitLifetimeMarkers());
}
} else { // not openmp nor constant sized type
llvm_unreachable("NYI");
bool VarAllocated = false;
if (getLangOpts().OpenMPIsTargetDevice)
llvm_unreachable("NYI");

if (!VarAllocated) {
if (!DidCallStackSave) {
// Save the stack.
auto defaultTy = AllocaInt8PtrTy;
CharUnits Align = CharUnits::fromQuantity(
CGM.getDataLayout().getAlignment(defaultTy, false));
Address Stack = CreateTempAlloca(defaultTy, Align, loc, "saved_stack");

mlir::Value V = builder.createStackSave(loc, defaultTy);
assert(V.getType() == AllocaInt8PtrTy);
builder.createStore(loc, V, Stack);

DidCallStackSave = true;

// Push a cleanup block and restore the stack there.
// FIXME: in general circumstances, this should be an EH cleanup.
pushStackRestore(NormalCleanup, Stack);
}

auto VlaSize = getVLASize(Ty);
mlir::Type mTy = convertTypeForMem(VlaSize.Type);

// Allocate memory for the array.
address = CreateTempAlloca(mTy, alignment, loc, "vla", VlaSize.NumElts,
&allocaAddr, builder.saveInsertionPoint());
}

// If we have debug info enabled, properly describe the VLA dimensions for
// this type by registering the vla size expression for each of the
// dimensions.
assert(!UnimplementedFeature::generateDebugInfo());
}

emission.Addr = address;
Expand Down Expand Up @@ -858,7 +896,9 @@ struct CallStackRestore final : EHScopeStack::Cleanup {
CallStackRestore(Address Stack) : Stack(Stack) {}
bool isRedundantBeforeReturn() override { return true; }
void Emit(CIRGenFunction &CGF, Flags flags) override {
llvm_unreachable("NYI");
auto loc = Stack.getPointer().getLoc();
mlir::Value V = CGF.getBuilder().createLoad(loc, Stack);
CGF.getBuilder().createStackRestore(loc, V);
}
};

Expand Down Expand Up @@ -941,6 +981,10 @@ CIRGenFunction::getDestroyer(QualType::DestructionKind kind) {
llvm_unreachable("Unknown DestructionKind");
}

void CIRGenFunction::pushStackRestore(CleanupKind Kind, Address SPMem) {
EHStack.pushCleanup<CallStackRestore>(Kind, SPMem);
}

/// Enter a destroy cleanup for the given local variable.
void CIRGenFunction::buildAutoVarTypeCleanup(
const CIRGenFunction::AutoVarEmission &emission,
Expand Down
174 changes: 174 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "clang/AST/ASTLambda.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticCategories.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/FPEnv.h"
Expand Down Expand Up @@ -1394,3 +1395,176 @@ void CIRGenFunction::checkTargetFeatures(SourceLocation Loc,
}
}
}

CIRGenFunction::VlaSizePair CIRGenFunction::getVLASize(QualType type) {
const VariableArrayType *vla =
CGM.getASTContext().getAsVariableArrayType(type);
assert(vla && "type was not a variable array type!");
return getVLASize(vla);
}

CIRGenFunction::VlaSizePair
CIRGenFunction::getVLASize(const VariableArrayType *type) {
// The number of elements so far; always size_t.
mlir::Value numElements;

QualType elementType;
do {
elementType = type->getElementType();
mlir::Value vlaSize = VLASizeMap[type->getSizeExpr()];
assert(vlaSize && "no size for VLA!");
assert(vlaSize.getType() == SizeTy);

if (!numElements) {
numElements = vlaSize;
} else {
// It's undefined behavior if this wraps around, so mark it that way.
// FIXME: Teach -fsanitize=undefined to trap this.

numElements = builder.createMul(numElements, vlaSize);
}
} while ((type = getContext().getAsVariableArrayType(elementType)));

assert(numElements && "Undefined elements number");
return {numElements, elementType};
}

// TODO(cir): most part of this function can be shared between CIRGen
// and traditional LLVM codegen
void CIRGenFunction::buildVariablyModifiedType(QualType type) {
assert(type->isVariablyModifiedType() &&
"Must pass variably modified type to EmitVLASizes!");

// We're going to walk down into the type and look for VLA
// expressions.
do {
assert(type->isVariablyModifiedType());

const Type *ty = type.getTypePtr();
switch (ty->getTypeClass()) {
case clang::Type::CountAttributed:
case clang::Type::PackIndexing:
llvm_unreachable("NYI");

#define TYPE(Class, Base)
#define ABSTRACT_TYPE(Class, Base)
#define NON_CANONICAL_TYPE(Class, Base)
#define DEPENDENT_TYPE(Class, Base) case Type::Class:
#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base)
#include "clang/AST/TypeNodes.inc"
llvm_unreachable("unexpected dependent type!");

// These types are never variably-modified.
case Type::Builtin:
case Type::Complex:
case Type::Vector:
case Type::ExtVector:
case Type::ConstantMatrix:
case Type::Record:
case Type::Enum:
case Type::Using:
case Type::TemplateSpecialization:
case Type::ObjCTypeParam:
case Type::ObjCObject:
case Type::ObjCInterface:
case Type::ObjCObjectPointer:
case Type::BitInt:
llvm_unreachable("type class is never variably-modified!");

case Type::Elaborated:
type = cast<clang::ElaboratedType>(ty)->getNamedType();
break;

case Type::Adjusted:
type = cast<clang::AdjustedType>(ty)->getAdjustedType();
break;

case Type::Decayed:
type = cast<clang::DecayedType>(ty)->getPointeeType();
break;

case Type::Pointer:
type = cast<clang::PointerType>(ty)->getPointeeType();
break;

case Type::BlockPointer:
type = cast<clang::BlockPointerType>(ty)->getPointeeType();
break;

case Type::LValueReference:
case Type::RValueReference:
type = cast<clang::ReferenceType>(ty)->getPointeeType();
break;

case Type::MemberPointer:
type = cast<clang::MemberPointerType>(ty)->getPointeeType();
break;

case Type::ConstantArray:
case Type::IncompleteArray:
// Losing element qualification here is fine.
type = cast<clang::ArrayType>(ty)->getElementType();
break;

case Type::VariableArray: {
// Losing element qualification here is fine.
const VariableArrayType *vat = cast<clang::VariableArrayType>(ty);

// Unknown size indication requires no size computation.
// Otherwise, evaluate and record it.
if (const Expr *sizeExpr = vat->getSizeExpr()) {
// It's possible that we might have emitted this already,
// e.g. with a typedef and a pointer to it.
mlir::Value &entry = VLASizeMap[sizeExpr];
if (!entry) {
mlir::Value size = buildScalarExpr(sizeExpr);
assert(!UnimplementedFeature::sanitizeVLABound());

// Always zexting here would be wrong if it weren't
// undefined behavior to have a negative bound.
// FIXME: What about when size's type is larger than size_t?
entry = builder.createIntCast(size, SizeTy);
}
}
type = vat->getElementType();
break;
}

case Type::FunctionProto:
case Type::FunctionNoProto:
type = cast<clang::FunctionType>(ty)->getReturnType();
break;

case Type::Paren:
case Type::TypeOf:
case Type::UnaryTransform:
case Type::Attributed:
case Type::BTFTagAttributed:
case Type::SubstTemplateTypeParm:
case Type::MacroQualified:
// Keep walking after single level desugaring.
type = type.getSingleStepDesugaredType(getContext());
break;

case Type::Typedef:
case Type::Decltype:
case Type::Auto:
case Type::DeducedTemplateSpecialization:
// Stop walking: nothing to do.
return;

case Type::TypeOfExpr:
// Stop walking: emit typeof expression.
buildIgnoredExpr(cast<clang::TypeOfExprType>(ty)->getUnderlyingExpr());
return;

case Type::Atomic:
type = cast<clang::AtomicType>(ty)->getValueType();
break;

case Type::Pipe:
type = cast<clang::PipeType>(ty)->getElementType();
break;
}
} while (type->isVariablyModifiedType());
}
26 changes: 26 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ class CIRGenFunction : public CIRGenTypeCache {
llvm::DenseMap<const OpaqueValueExpr *, LValue> OpaqueLValues;
llvm::DenseMap<const OpaqueValueExpr *, RValue> OpaqueRValues;

// This keeps track of the associated size for each VLA type.
// We track this by the size expression rather than the type itself because
// in certain situations, like a const qualifier applied to an VLA typedef,
// multiple VLA types can share the same size expression.
// FIXME: Maybe this could be a stack of maps that is pushed/popped as we
// enter/leave scopes.
llvm::DenseMap<const Expr *, mlir::Value> VLASizeMap;

public:
/// A non-RAII class containing all the information about a bound
/// opaque value. OpaqueValueMapping, below, is a RAII wrapper for
Expand Down Expand Up @@ -707,6 +715,22 @@ class CIRGenFunction : public CIRGenTypeCache {
/// \returns SSA value with the argument.
mlir::Value buildVAArg(VAArgExpr *VE, Address &VAListAddr);

void buildVariablyModifiedType(QualType Ty);

struct VlaSizePair {
mlir::Value NumElts;
QualType Type;

VlaSizePair(mlir::Value NE, QualType T) : NumElts(NE), Type(T) {}
};

/// Returns an MLIR value that corresponds to the size,
/// in non-variably-sized elements, of a variable length array type,
/// plus that largest non-variably-sized element type. Assumes that
/// the type has already been emitted with buildVariablyModifiedType.
VlaSizePair getVLASize(const VariableArrayType *vla);
VlaSizePair getVLASize(QualType vla);

mlir::Value emitBuiltinObjectSize(const Expr *E, unsigned Type,
mlir::cir::IntType ResType,
mlir::Value EmittedE, bool IsDynamic);
Expand Down Expand Up @@ -1242,6 +1266,8 @@ class CIRGenFunction : public CIRGenTypeCache {
void pushEHDestroy(QualType::DestructionKind dtorKind, Address addr,
QualType type);

void pushStackRestore(CleanupKind kind, Address SPMem);

static bool
IsConstructorDelegationValid(const clang::CXXConstructorDecl *Ctor);

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &context,
/*isSigned=*/false);
UInt8PtrTy = builder.getPointerTo(UInt8Ty);
UInt8PtrPtrTy = builder.getPointerTo(UInt8PtrTy);
// TODO: AllocaInt8PtrTy
AllocaInt8PtrTy = UInt8PtrTy;
// TODO: GlobalsInt8PtrTy
// TODO: ConstGlobalsPtrTy
// TODO: ASTAllocaAddressSpace
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenTypeCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ struct CIRGenTypeCache {
};

/// void* in alloca address space
// union {
// mlir::cir::PointerType AllocaVoidPtrTy;
// mlir::cir::PointerType AllocaInt8PtrTy;
// };
union {
mlir::cir::PointerType AllocaVoidPtrTy;
mlir::cir::PointerType AllocaInt8PtrTy;
};

/// void* in default globals address space
// union {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct UnimplementedFeature {
static bool emitCheckedInBoundsGEP() { return false; }
static bool pointerOverflowSanitizer() { return false; }
static bool sanitizeDtor() { return false; }
static bool sanitizeVLABound() { return false; }

// ObjC
static bool setObjCGCLValueClass() { return false; }
Expand Down

0 comments on commit e4fee87

Please sign in to comment.