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][CodeGen] Support static local variables #1224

Open
wants to merge 14 commits into
base: spr/lanza/main.circodegen-static-local-variables
Choose a base branch
from
6 changes: 6 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class StructLayout;
class CIRDataLayout {
bool bigEndian = false;

unsigned defaultGlobalsAddrSpace = 0;

/// Primitive type alignment data. This is sorted by type and bit
/// width during construction.
llvm::DataLayout::PrimitiveSpec StructAlignment;
Expand Down Expand Up @@ -106,6 +108,10 @@ class CIRDataLayout {
cir::IntType::get(Ty.getContext(), getPointerTypeSizeInBits(Ty), false);
return IntTy;
}

unsigned getDefaultGlobalsAddressSpace() const {
return defaultGlobalsAddrSpace;
}
};

/// Used to lazily calculate structure layout information for a target machine,
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2418,6 +2418,7 @@ def GlobalOp : CIR_Op<"global",
UnitAttr:$comdat,
UnitAttr:$constant,
UnitAttr:$dsolocal,
UnitAttr:$static_local,
OptionalAttr<I64Attr>:$alignment,
OptionalAttr<ASTVarDeclInterface>:$ast,
OptionalAttr<StrAttr>:$section,
Expand Down Expand Up @@ -2503,7 +2504,9 @@ def GetGlobalOp : CIR_Op<"get_global",
```
}];

let arguments = (ins FlatSymbolRefAttr:$name, UnitAttr:$tls);
let arguments = (ins FlatSymbolRefAttr:$name,
UnitAttr:$tls,
UnitAttr:$static_local);
let results = (outs Res<CIR_PointerType, "", []>:$addr);

let assemblyFormat = [{
Expand Down
32 changes: 32 additions & 0 deletions clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,42 @@ let cppNamespace = "::cir" in {
MangleCtx->mangleDynamicInitializer($_attr.getAst(), Out);
}]
>,
InterfaceMethod<"", "void", "mangleStaticGuardVariable", (ins "llvm::raw_ostream&":$Out), [{}],
/*defaultImplementation=*/ [{
std::unique_ptr<clang::MangleContext> mangleCtx(
$_attr.getAst()->getASTContext().createMangleContext());
mangleCtx->mangleStaticGuardVariable($_attr.getAst(), Out);
}]
>,
InterfaceMethod<"", "clang::VarDecl::TLSKind", "getTLSKind", (ins), [{}],
/*defaultImplementation=*/ [{
return $_attr.getAst()->getTLSKind();
}]
>,
InterfaceMethod<"", "bool", "isInline", (ins), [{}],
/*defaultImplementation=*/ [{
return $_attr.getAst()->isInline();
}]
>,
InterfaceMethod<"", "clang::TemplateSpecializationKind", "getTemplateSpecializationKind", (ins), [{}],
/*defaultImplementation=*/ [{
return $_attr.getAst()->getTemplateSpecializationKind();
}]
>,
InterfaceMethod<"", "bool", "isLocalVarDecl", (ins), [{}],
/*defaultImplementation=*/ [{
return $_attr.getAst()->isLocalVarDecl();
}]
>,
InterfaceMethod<"", "clang::SourceLocation", "getLocation", (ins), [{}],
/*defaultImplementation=*/ [{
return $_attr.getAst()->getLocation();
}]
>,
InterfaceMethod<"", "const clang::VarDecl *", "getRawDecl", (ins), [{}],
Copy link
Member Author

Choose a reason for hiding this comment

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

@bcardosolopes Thoughts on this? This wasn't scaling to the real world. e.g. functions that take a VarDecl as an argument.

Copy link
Member

Choose a reason for hiding this comment

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

After you latest update you can probably ditch this altogether

/*defaultImplementation=*/ [{
return $_attr.getAst();
}]
>
];
}
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ struct MissingFeatures {
static bool setFunctionAttributes() { return false; }
static bool attributeBuiltin() { return false; }
static bool attributeNoBuiltin() { return false; }
static bool functionIndexAttribute() { return false; }
static bool noUnwindAttribute() { return false; }
static bool parameterAttributes() { return false; }
static bool minLegalVectorWidthAttr() { return false; }
static bool vscaleRangeAttr() { return false; }
Expand Down Expand Up @@ -151,6 +153,7 @@ struct MissingFeatures {

// Folding methods.
static bool foldBinOpFMF() { return false; }
static bool folder() { return false; }

// Fast math.
static bool fastMathGuard() { return false; }
Expand Down Expand Up @@ -454,6 +457,10 @@ struct MissingFeatures {
static bool mustProgress() { return false; }

static bool skipTempCopy() { return false; }

static bool addressSpaceInGlobalVar() { return false; }

static bool useARMGuardVarABI() { return false; }
};

} // namespace cir
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class Address {
mlir::Type ElementType;
clang::CharUnits Alignment;

/// Offset from the base pointer. This is non-null only when the base pointer
/// is signed.
mlir::Attribute offset = nullptr;

protected:
Address(std::nullptr_t) : ElementType(nullptr) {}

Expand Down Expand Up @@ -134,6 +138,8 @@ class Address {
return *this;
}

bool hasOffset() const { return bool(offset); }

/// Get the operation which defines this address.
mlir::Operation *getDefiningOp() const {
if (!isValid())
Expand Down
30 changes: 30 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "clang/CIR/Dialect/IR/FPEnv.h"

#include "mlir/IR/Attributes.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/BuiltinOps.h"
Expand Down Expand Up @@ -817,6 +818,35 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return Address{createImagPtr(loc, addr.getPointer()), addr.getAlignment()};
}

/// Return a boolean value testing if \p arg == 0.
mlir::Value createIsNull(mlir::Location loc, mlir::Value arg,
const llvm::Twine &name = "") {
return createICmpEQ(loc, arg, getNullValue(arg.getType(), loc), name);
}

/// Return a boolean value testing if \p arg != 0.
mlir::Value createIsNotNull(mlir::Location loc, mlir::Value arg,
const llvm::Twine &name = "") {
return createICmpNE(loc, arg, getNullValue(arg.getType(), loc), name);
}

mlir::Value createICmpEQ(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
const llvm::Twine &name = "") {
return createICmp(loc, cir::CmpOpKind::eq, lhs, rhs, name);
}
mlir::Value createICmpNE(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
const llvm::Twine &name = "") {
return createICmp(loc, cir::CmpOpKind::ne, lhs, rhs, name);
}

mlir::Value createICmp(mlir::Location loc, cir::CmpOpKind kind,
mlir::Value lhs, mlir::Value rhs,
const llvm::Twine &name = "") {
if (cir::MissingFeatures::folder())
llvm_unreachable("NYI");
return createCompare(loc, kind, lhs, rhs);
}

/// Cast the element type of the given address to a different type,
/// preserving information like the alignment.
Address createElementBitCast(mlir::Location loc, Address addr,
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,19 @@ class CIRGenCXXABI {
bool ForVirtualBase, bool Delegating,
Address This, QualType ThisTy) = 0;

/*************************** Static local guards ****************************/

/// Emits the guarded initializer and destructor setup for the given
/// variable, given that it couldn't be emitted as a constant.
/// If \p PerformInit is false, the initialization has been folded to a
/// constant and should not be performed.
///
/// The variable may be:
/// - a static local variable
/// - a static data member of a class template instantiation
virtual void emitGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl,
cir::GlobalOp globalOp, bool performInit) = 0;

/// Emit code to force the execution of a destructor during global
/// teardown. The default implementation of this uses atexit.
///
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//

#include "CIRGenCall.h"
#include "CIRGenBuilder.h"
#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
Expand All @@ -19,8 +20,10 @@
#include "TargetInfo.h"

#include "clang/AST/Attr.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/CIR/ABIArgInfo.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/FnInfoOpts.h"
Expand All @@ -31,6 +34,7 @@
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/SymbolTable.h"
#include "mlir/IR/Types.h"
#include "clang/CIR/MissingFeatures.h"
Expand Down
86 changes: 57 additions & 29 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,70 +539,98 @@ CIRGenModule::getOrCreateStaticVarDecl(const VarDecl &D,
/// Add the initializer for 'D' to the global variable that has already been
/// created for it. If the initializer has a different type than GV does, this
/// may free GV and return a different one. Otherwise it just returns GV.
cir::GlobalOp CIRGenFunction::addInitializerToStaticVarDecl(
const VarDecl &D, cir::GlobalOp GV, cir::GetGlobalOp GVAddr) {
cir::GlobalOp
CIRGenFunction::addInitializerToStaticVarDecl(const VarDecl &varDecl,
cir::GlobalOp globalOp,
cir::GetGlobalOp getGlobalOp) {
ConstantEmitter emitter(*this);
mlir::TypedAttr Init =
mlir::dyn_cast<mlir::TypedAttr>(emitter.tryEmitForInitializer(D));
assert(Init && "Expected typed attribute");
mlir::Attribute init = emitter.tryEmitForInitializer(varDecl);

// If constant emission failed, then this should be a C++ static
// initializer.
if (!Init) {
if (!init) {
if (!getLangOpts().CPlusPlus)
CGM.ErrorUnsupported(D.getInit(), "constant l-value expression");
else if (D.hasFlexibleArrayInit(getContext()))
CGM.ErrorUnsupported(D.getInit(), "flexible array initializer");
CGM.ErrorUnsupported(varDecl.getInit(), "constant l-value expression");
else if (varDecl.hasFlexibleArrayInit(getContext()))
CGM.ErrorUnsupported(varDecl.getInit(), "flexible array initializer");
else {
// Since we have a static initializer, this global variable can't
// be constant.
GV.setConstant(false);
llvm_unreachable("C++ guarded init it NYI");
globalOp.setConstant(false);

emitCXXGuardedInit(varDecl, globalOp, /*performInit*/ true);
getGlobalOp.setStaticLocal(true);
}
return GV;
return globalOp;
}

auto typedInit = mlir::cast<mlir::TypedAttr>(init);

#ifndef NDEBUG
CharUnits VarSize = CGM.getASTContext().getTypeSizeInChars(D.getType()) +
D.getFlexibleArrayInitChars(getContext());
CharUnits CstSize = CharUnits::fromQuantity(
CGM.getDataLayout().getTypeAllocSize(Init.getType()));
assert(VarSize == CstSize && "Emitted constant has unexpected size");
CharUnits varSize =
CGM.getASTContext().getTypeSizeInChars(varDecl.getType()) +
varDecl.getFlexibleArrayInitChars(getContext());
CharUnits cstSize = CharUnits::fromQuantity(
CGM.getDataLayout().getTypeAllocSize(typedInit.getType()));
assert(varSize == cstSize && "Emitted constant has unexpected size");
#endif

// The initializer may differ in type from the global. Rewrite
// the global to match the initializer. (We have to do this
// because some types, like unions, can't be completely represented
// in the LLVM type system.)
if (GV.getSymType() != Init.getType()) {
GV.setSymType(Init.getType());
// NOTE(CIR): This was removed in OG since opaque pointers made it trivial. We
// need it since we still have typed pointers.
if (globalOp.getSymType() != typedInit.getType()) {
globalOp.setSymType(typedInit.getType());

cir::GlobalOp oldGlobalOp = globalOp;
globalOp =
builder.createGlobal(CGM.getModule(), getLoc(varDecl.getSourceRange()),
oldGlobalOp.getName(), typedInit.getType(),
oldGlobalOp.getConstant(), globalOp.getLinkage());
// FIXME(cir): OG codegen inserts new GV before old one, we probably don't
// need that?
globalOp.setVisibility(oldGlobalOp.getVisibility());
globalOp.setGlobalVisibilityAttr(oldGlobalOp.getGlobalVisibilityAttr());
globalOp.setInitialValueAttr(init);
globalOp.setTlsModelAttr(oldGlobalOp.getTlsModelAttr());
globalOp.setDSOLocal(oldGlobalOp.getDsolocal());
assert(!cir::MissingFeatures::setComdat());
assert(!cir::MissingFeatures::addressSpaceInGlobalVar());

// Normally this should be done with a call to CGM.replaceGlobal(OldGV, GV),
// but since at this point the current block hasn't been really attached,
// there's no visibility into the GetGlobalOp corresponding to this Global.
// Given those constraints, thread in the GetGlobalOp and update it
// directly.
GVAddr.getAddr().setType(
getBuilder().getPointerTo(Init.getType(), GV.getAddrSpaceAttr()));
getGlobalOp.getAddr().setType(getBuilder().getPointerTo(
typedInit.getType(), globalOp.getAddrSpaceAttr()));

// Replace all uses of the old global with the new global
oldGlobalOp->replaceAllUsesWith(globalOp);

// Erase the old global, since it is no longer used.
oldGlobalOp->erase();
}

bool NeedsDtor =
D.needsDestruction(getContext()) == QualType::DK_cxx_destructor;
bool needsDtor =
varDecl.needsDestruction(getContext()) == QualType::DK_cxx_destructor;

GV.setConstant(
CGM.isTypeConstant(D.getType(), /*ExcludeCtor=*/true, !NeedsDtor));
GV.setInitialValueAttr(Init);
globalOp.setConstant(
CGM.isTypeConstant(varDecl.getType(), /*ExcludeCtor=*/true, !needsDtor));
globalOp.setInitialValueAttr(init);

emitter.finalize(GV);
emitter.finalize(globalOp);

if (NeedsDtor) {
if (needsDtor) {
// We have a constant initializer, but a nontrivial destructor. We still
// need to perform a guarded "initialization" in order to register the
// destructor.
llvm_unreachable("C++ guarded init is NYI");
}

return GV;
return globalOp;
}

void CIRGenFunction::emitStaticVarDecl(const VarDecl &D,
Expand Down
23 changes: 23 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
#include "CIRGenModule.h"
#include "TargetInfo.h"
Expand Down Expand Up @@ -51,3 +52,25 @@ void CIRGenModule::emitCXXGlobalVarDeclInitFunc(const VarDecl *D,

emitCXXGlobalVarDeclInit(D, Addr, PerformInit);
}

void CIRGenFunction::emitCXXGuardedInit(const VarDecl &varDecl,
cir::GlobalOp globalOp,
bool performInit) {
// If we've been asked to forbid guard variables, emit an error now. This
// diagnostic is hard-coded for Darwin's use case; we can find better phrasing
// if someone else needs it.
if (CGM.getCodeGenOpts().ForbidGuardVariables)
llvm_unreachable("NYI");

CGM.getCXXABI().emitGuardedInit(*this, varDecl, globalOp, performInit);
}

void CIRGenFunction::emitCXXGlobalVarDeclInit(const VarDecl &varDecl,
cir::GlobalOp globalOp,
bool performInit) {
// TODO(CIR): We diverge from CodeGen here via having this in CIRGenModule
// instead. This is necessary due to the way we are constructing global inits
// at the moment. With LoweringPrepare being moved to CIRGen we should
// refactor this to live here.
llvm_unreachable("NYI");
}
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ void CIRGenFunction::LexicalScope::cleanup() {
// An empty non-entry block has nothing to offer, and since this is
// synthetic, losing information does not affect anything.
bool entryBlock = builder.getInsertionBlock()->isEntryBlock();
if (!entryBlock && currBlock->empty()) {
if (!entryBlock && currBlock->empty() && currBlock->hasNoPredecessors()) {
currBlock->erase();
// Remove unused cleanup blocks.
if (cleanupBlock && cleanupBlock->hasNoPredecessors())
Expand Down
Loading
Loading