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
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,14 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return createGlobal(module, loc, uniqueName, type, isConstant, linkage);
}

cir::StackSaveOp createStackSave(mlir::Location loc, mlir::Type ty) {
return cir::StackSaveOp::create(*this, loc, ty);
}

cir::StackRestoreOp createStackRestore(mlir::Location loc, mlir::Value v) {
return cir::StackRestoreOp::create(*this, loc, v);
}

mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType,
Address dstAddr, mlir::Type storageType,
mlir::Value src, const CIRGenBitFieldInfo &info,
Expand Down
98 changes: 72 additions & 26 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,38 +44,70 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d,

// If the type is variably-modified, emit all the VLA sizes for it.
if (ty->isVariablyModifiedType())
cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: variably modified type");
emitVariablyModifiedType(ty);

assert(!cir::MissingFeatures::openMP());

Address address = Address::invalid();
if (!ty->isConstantSizeType())
cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: non-constant size type");

// A normal fixed sized variable becomes an alloca in the entry block,
// unless:
// - it's an NRVO variable.
// - we are compiling OpenMP and it's an OpenMP local variable.
if (nrvo) {
// The named return value optimization: allocate this variable in the
// return slot, so that we can elide the copy when returning this
// variable (C++0x [class.copy]p34).
address = returnValue;

if (const RecordDecl *rd = ty->getAsRecordDecl()) {
if (const auto *cxxrd = dyn_cast<CXXRecordDecl>(rd);
(cxxrd && !cxxrd->hasTrivialDestructor()) ||
rd->isNonTrivialToPrimitiveDestroy())
cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: set NRVO flag");
if (ty->isConstantSizeType()) {
// A normal fixed sized variable becomes an alloca in the entry block,
// unless:
// - it's an NRVO variable.
// - we are compiling OpenMP and it's an OpenMP local variable.
if (nrvo) {
// The named return value optimization: allocate this variable in the
// return slot, so that we can elide the copy when returning this
// variable (C++0x [class.copy]p34).
address = returnValue;

if (const RecordDecl *rd = ty->getAsRecordDecl()) {
if (const auto *cxxrd = dyn_cast<CXXRecordDecl>(rd);
(cxxrd && !cxxrd->hasTrivialDestructor()) ||
rd->isNonTrivialToPrimitiveDestroy())
cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: set NRVO flag");
}
} else {
// A normal fixed sized variable becomes an alloca in the entry block,
mlir::Type allocaTy = convertTypeForMem(ty);
// Create the temp alloca and declare variable using it.
address = createTempAlloca(allocaTy, alignment, loc, d.getName(),
/*arraySize=*/nullptr, /*alloca=*/nullptr, ip);
declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()),
alignment);
}
} else {
// A normal fixed sized variable becomes an alloca in the entry block,
mlir::Type allocaTy = convertTypeForMem(ty);
// Create the temp alloca and declare variable using it.
address = createTempAlloca(allocaTy, alignment, loc, d.getName(),
/*arraySize=*/nullptr, /*alloca=*/nullptr, ip);
declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()),
alignment);
// Non-constant size type
assert(!cir::MissingFeatures::openMP());
if (!didCallStackSave) {
// Save the stack.
cir::PointerType 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);
}

VlaSizePair vlaSize = getVLASize(ty);
mlir::Type memTy = convertTypeForMem(vlaSize.type);

// Allocate memory for the array.
address =
createTempAlloca(memTy, alignment, loc, d.getName(), vlaSize.numElts,
/*alloca=*/nullptr, 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(!cir::MissingFeatures::generateDebugInfo());
}

emission.addr = address;
Expand Down Expand Up @@ -696,6 +728,16 @@ struct DestroyObject final : EHScopeStack::Cleanup {
cgf.emitDestroy(addr, type, destroyer);
}
};

struct CallStackRestore final : EHScopeStack::Cleanup {
Address stack;
CallStackRestore(Address stack) : stack(stack) {}
void emit(CIRGenFunction &cgf) override {
mlir::Location loc = stack.getPointer().getLoc();
mlir::Value v = cgf.getBuilder().createLoad(loc, stack);
cgf.getBuilder().createStackRestore(loc, v);
}
};
} // namespace

void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
Expand Down Expand Up @@ -805,6 +847,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::emitAutoVarTypeCleanup(
const CIRGenFunction::AutoVarEmission &emission,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2068,7 +2068,7 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
mlir::OpBuilder::InsertionGuard guard(builder);
builder.restoreInsertionPoint(ip);
addr = builder.createAlloca(loc, /*addr type*/ localVarPtrTy,
/*var type*/ ty, name, alignIntAttr);
/*var type*/ ty, name, alignIntAttr, arraySize);
assert(!cir::MissingFeatures::astVarDeclInterface());
}
return addr;
Expand Down
58 changes: 57 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
curFn = fn;

const Decl *d = gd.getDecl();

didCallStackSave = false;
curCodeDecl = d;
const auto *fd = dyn_cast_or_null<FunctionDecl>(d);
curFuncDecl = d->getNonClosureContext();
Expand Down Expand Up @@ -1006,6 +1008,41 @@ mlir::Value CIRGenFunction::emitAlignmentAssumption(
offsetValue);
}

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.getLoc(), numElements, vlaSize,
cir::OverflowBehavior::NoUnsignedWrap);
}
} while ((type = getContext().getAsVariableArrayType(elementType)));

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

// TODO(cir): Most of this function can be shared between CIRGen
// and traditional LLVM codegen
void CIRGenFunction::emitVariablyModifiedType(QualType type) {
Expand Down Expand Up @@ -1086,7 +1123,26 @@ void CIRGenFunction::emitVariablyModifiedType(QualType type) {
break;

case Type::VariableArray: {
cgm.errorNYI("CIRGenFunction::emitVariablyModifiedType VLA");
// 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 = emitScalarExpr(sizeExpr);
assert(!cir::MissingFeatures::sanitizers());

// 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;
}

Expand Down
32 changes: 32 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ class CIRGenFunction : public CIRGenTypeCache {
using SymTableTy = llvm::ScopedHashTable<const clang::Decl *, mlir::Value>;
SymTableTy symbolTable;

/// Whether a cir.stacksave operation has been added. Used to avoid
/// inserting cir.stacksave for multiple VLAs in the same scope.
bool didCallStackSave = false;

/// Whether or not a Microsoft-style asm block has been processed within
/// this fuction. These can potentially set the return value.
bool sawAsmBlock = false;
Expand Down Expand Up @@ -188,6 +192,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 @@ -436,6 +448,20 @@ class CIRGenFunction : public CIRGenTypeCache {
}
};

struct VlaSizePair {
mlir::Value numElts;
QualType type;

VlaSizePair(mlir::Value num, QualType ty) : numElts(num), type(ty) {}
};

/// Returns an MLIR::Value+QualType pair 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 emitVariablyModifiedType.
VlaSizePair getVLASize(const VariableArrayType *type);
VlaSizePair getVLASize(QualType type);

void finishFunction(SourceLocation endLoc);

/// Determine whether the given initializer is trivial in the sense
Expand Down Expand Up @@ -583,6 +609,8 @@ class CIRGenFunction : public CIRGenTypeCache {
return needsEHCleanup(kind) ? NormalAndEHCleanup : NormalCleanup;
}

void pushStackRestore(CleanupKind kind, Address spMem);

/// Set the address of a local variable.
void setAddrOfLocalVar(const clang::VarDecl *vd, Address addr) {
assert(!localDeclMap.count(vd) && "Decl already exists in LocalDeclMap!");
Expand Down Expand Up @@ -854,6 +882,7 @@ class CIRGenFunction : public CIRGenTypeCache {

protected:
bool performCleanup;
bool oldDidCallStackSave;

private:
RunCleanupsScope(const RunCleanupsScope &) = delete;
Expand All @@ -867,6 +896,8 @@ class CIRGenFunction : public CIRGenTypeCache {
explicit RunCleanupsScope(CIRGenFunction &cgf)
: performCleanup(true), cgf(cgf) {
cleanupStackDepth = cgf.ehStack.stable_begin();
oldDidCallStackSave = cgf.didCallStackSave;
cgf.didCallStackSave = false;
oldCleanupStackDepth = cgf.currentCleanupStackDepth;
cgf.currentCleanupStackDepth = cleanupStackDepth;
}
Expand All @@ -883,6 +914,7 @@ class CIRGenFunction : public CIRGenTypeCache {
assert(performCleanup && "Already forced cleanup");
{
mlir::OpBuilder::InsertionGuard guard(cgf.getBuilder());
cgf.didCallStackSave = oldDidCallStackSave;
cgf.popCleanupBlocks(cleanupStackDepth);
performCleanup = false;
cgf.currentCleanupStackDepth = oldCleanupStackDepth;
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
FP80Ty = cir::FP80Type::get(&getMLIRContext());
FP128Ty = cir::FP128Type::get(&getMLIRContext());

AllocaInt8PtrTy = cir::PointerType::get(UInt8Ty, cirAllocaAddressSpace);

PointerAlignInBytes =
astContext
.toCharUnitsFromBits(
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenTypeCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ struct CIRGenTypeCache {
cir::PointerType VoidPtrTy;
cir::PointerType UInt8PtrTy;

/// void* in alloca address space
cir::PointerType AllocaInt8PtrTy;

/// The size and alignment of a pointer into the generic address space.
union {
unsigned char PointerAlignInBytes;
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,16 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
break;
}

case Type::VariableArray: {
const VariableArrayType *a = cast<VariableArrayType>(ty);
if (a->getIndexTypeCVRQualifiers() != 0)
cgm.errorNYI(SourceLocation(), "non trivial array types", type);
// VLAs resolve to the innermost element type; this matches
// the return of alloca, and there isn't any obviously better choice.
resultType = convertTypeForMem(a->getElementType());
break;
}

case Type::IncompleteArray: {
const IncompleteArrayType *arrTy = cast<IncompleteArrayType>(ty);
if (arrTy->getIndexTypeCVRQualifiers() != 0)
Expand Down
Loading