Skip to content

Commit

Permalink
[CIR][CodeGen] Static local variables
Browse files Browse the repository at this point in the history
Pull Request: #1224
  • Loading branch information
lanza committed Dec 12, 2024
1 parent 92ae283 commit 35024d3
Show file tree
Hide file tree
Showing 17 changed files with 899 additions and 47 deletions.
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), [{}],
/*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
12 changes: 12 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 Expand Up @@ -938,6 +942,14 @@ mlir::Value CIRGenFunction::emitRuntimeCall(mlir::Location loc,
return call->getResult(0);
}

mlir::Value
CIRGenFunction::emitNounwindRuntimeCall(mlir::Location loc, cir::FuncOp callee,
ArrayRef<mlir::Value> args) {
mlir::Value call = emitRuntimeCall(loc, callee, args);
assert(!cir::MissingFeatures::noUnwindAttribute());
return call;
}

void CIRGenFunction::emitCallArg(CallArgList &args, const Expr *E,
QualType type) {
// TODO: Add the DisableDebugLocationUpdates helper
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");
}
Loading

0 comments on commit 35024d3

Please sign in to comment.