Skip to content
Open
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
13 changes: 12 additions & 1 deletion clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,18 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return createCompare(loc, cir::CmpOpKind::ne, operand, operand);
}

/// Return a boolean value testing if \p arg == 0.
mlir::Value createIsNull(mlir::Location loc, mlir::Value arg) {
return createCompare(loc, cir::CmpOpKind::eq, arg,
getNullValue(arg.getType(), loc));
}

/// Return a boolean value testing if \p arg != 0.
mlir::Value createIsNotNull(mlir::Location loc, mlir::Value arg) {
return createCompare(loc, cir::CmpOpKind::ne, arg,
getNullValue(arg.getType(), loc));
}

mlir::Value createUnaryOp(mlir::Location loc, cir::UnaryOpKind kind,
mlir::Value operand) {
return cir::UnaryOp::create(*this, loc, kind, operand);
Expand Down Expand Up @@ -399,7 +411,6 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
/*tbaa=*/cir::TBAAAttr{});
}


mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
mlir::Type type, llvm::StringRef name,
mlir::IntegerAttr alignment,
Expand Down
48 changes: 26 additions & 22 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2600,6 +2600,10 @@ def CIR_GlobalOp : CIR_Op<"global", [

`visibility_attr` is defined in terms of CIR's visibility.

The `static_local` attribute indicates that this global represents a
function-local static variable that requires guarded initialization
(e.g., C++ static local variables with non-constant initializers).

Example:

```mlir
Expand All @@ -2611,27 +2615,18 @@ def CIR_GlobalOp : CIR_Op<"global", [
// Note that both sym_name and sym_visibility are tied to Symbol trait.
// TODO: sym_visibility can possibly be represented by implementing the
// necessary Symbol's interface in terms of linkage instead.
let arguments = (ins
SymbolNameAttr:$sym_name,
DefaultValuedAttr<
CIR_VisibilityAttr,
"VisibilityKind::Default"
>:$global_visibility,
OptionalAttr<StrAttr>:$sym_visibility,
TypeAttr:$sym_type,
CIR_GlobalLinkageKind:$linkage,
OptionalAttr<MemorySpaceAttrInterface>:$addr_space,
OptionalAttr<CIR_TLSModel>:$tls_model,
// Note this can also be a FlatSymbolRefAttr
OptionalAttr<AnyAttr>:$initial_value,
UnitAttr:$comdat,
UnitAttr:$constant,
UnitAttr:$dso_local,
OptionalAttr<I64Attr>:$alignment,
OptionalAttr<ASTVarDeclInterface>:$ast,
OptionalAttr<StrAttr>:$section,
OptionalAttr<ArrayAttr>:$annotations
);
let arguments = (ins SymbolNameAttr:$sym_name,
DefaultValuedAttr<CIR_VisibilityAttr,
"VisibilityKind::Default">:$global_visibility,
OptionalAttr<StrAttr>:$sym_visibility, TypeAttr:$sym_type,
CIR_GlobalLinkageKind:$linkage,
OptionalAttr<MemorySpaceAttrInterface>:$addr_space,
OptionalAttr<CIR_TLSModel>:$tls_model,
// Note this can also be a FlatSymbolRefAttr
OptionalAttr<AnyAttr>:$initial_value, UnitAttr:$comdat,
UnitAttr:$constant, UnitAttr:$dso_local, UnitAttr:$static_local,
OptionalAttr<I64Attr>:$alignment, OptionalAttr<ASTVarDeclInterface>:$ast,
OptionalAttr<StrAttr>:$section, OptionalAttr<ArrayAttr>:$annotations);

let regions = (region AnyRegion:$ctorRegion, AnyRegion:$dtorRegion);

Expand All @@ -2643,6 +2638,7 @@ def CIR_GlobalOp : CIR_Op<"global", [
(`comdat` $comdat^)?
($tls_model^)?
(`dso_local` $dso_local^)?
(`static_local` $static_local^)?
(` ` custom<GlobalAddressSpaceValue>($addr_space)^ )?
$sym_name
custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value, $ctorRegion, $dtorRegion)
Expand Down Expand Up @@ -2696,6 +2692,10 @@ def CIR_GetGlobalOp : CIR_Op<"get_global", [
Addresses of thread local globals can only be retrieved if this operation
is marked `thread_local`, which indicates the address isn't constant.

The `static_local` attribute indicates that this global is a function-local
static variable that requires guarded initialization (e.g., C++ static
local variables with non-constant initializers).

Example:
```mlir
%x = cir.get_global @foo : !cir.ptr<i32>
Expand All @@ -2704,14 +2704,18 @@ def CIR_GetGlobalOp : CIR_Op<"get_global", [
...
cir.global external addrspace(offload_global) @gv = #cir.int<0> : !s32i
%z = cir.get_global @gv : !cir.ptr<!s32i, addrspace(offload_global)>
...
%w = cir.get_global static_local @func_static : !cir.ptr<i32>
```
}];

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 = [{
(`thread_local` $tls^)?
(`static_local` $static_local^)?
$name `:` qualified(type($addr)) attr-dict
}];
}
Expand Down
48 changes: 38 additions & 10 deletions clang/include/clang/CIR/Interfaces/ASTAttrInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,48 @@ let cppNamespace = "::cir" in {

def ASTVarDeclInterface : AttrInterface<"ASTVarDeclInterface",
[ASTDeclaratorDeclInterface]> {
let methods = [
InterfaceMethod<"", "void", "mangleDynamicInitializer", (ins "llvm::raw_ostream&":$Out), [{}],
/*defaultImplementation=*/ [{
let methods = [InterfaceMethod<"", "void", "mangleDynamicInitializer",
(ins "llvm::raw_ostream&":$Out), [{}],
/*defaultImplementation=*/[{
std::unique_ptr<clang::MangleContext> MangleCtx(
$_attr.getAst()->getASTContext().createMangleContext());
MangleCtx->mangleDynamicInitializer($_attr.getAst(), Out);
}]
>,
InterfaceMethod<"", "clang::VarDecl::TLSKind", "getTLSKind", (ins), [{}],
/*defaultImplementation=*/ [{
}]>,
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();
}]>];
}

def ASTFunctionDeclInterface : AttrInterface<"ASTFunctionDeclInterface",
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,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 @@ -160,6 +162,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 @@ -484,6 +487,13 @@ struct MissingFeatures {
static bool dataLayoutPtrHandlingBasedOnLangAS() { return false; }

static bool tailCall() { return false; }

static bool addressSpaceInGlobalVar() { return false; }

static bool useARMGuardVarABI() { return false; }

// Static local variable guard
static bool guardAbortOnException() { return false; }
};

} // namespace cir
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 @@ -174,6 +174,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
24 changes: 23 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,8 +636,9 @@ CIRGenFunction::addInitializerToStaticVarDecl(const VarDecl &varDecl,
else {
// Since we have a static initializer, this global variable can't
// be constant.
llvm_unreachable("C++ guarded init it NYI");
globalOp.setConstant(false);
emitCXXGuardedInit(varDecl, globalOp, /*performInit*/ true);
getGlobalOp.setStaticLocal(true);
}
return globalOp;
}
Expand All @@ -660,13 +661,34 @@ CIRGenFunction::addInitializerToStaticVarDecl(const VarDecl &varDecl,
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?
Copy link
Member

Choose a reason for hiding this comment

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

LLVM translation usually put them out in the proper order, because of the way MLIR handles this I don't think it should be a problem for us.

globalOp.setVisibility(oldGlobalOp.getVisibility());
globalOp.setGlobalVisibilityAttr(oldGlobalOp.getGlobalVisibilityAttr());
globalOp.setInitialValueAttr(typedInit);
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.
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 =
Expand Down
21 changes: 21 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,23 @@ 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.
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 @@ -438,7 +438,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
13 changes: 13 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2243,6 +2243,19 @@ class CIRGenFunction : public CIRGenTypeCache {

void emitInvariantStart(CharUnits Size);

/// Emit code in this function to perform a guarded variable
/// initialization. Guarded initializations are used when it's not
/// possible to prove that an initialization will be done exactly
/// once, e.g. with a static local variable or a static data member
/// of a class template.
void emitCXXGuardedInit(const VarDecl &varDecl, cir::GlobalOp globalOp,
bool performInit);

/// EmitCXXGlobalVarDeclInit - Create the initializer for a C++
Copy link
Member

Choose a reason for hiding this comment

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

Remove the EmitCXXGlobalVarDeclInit - in the comment

/// variable with global storage.
void emitCXXGlobalVarDeclInit(const VarDecl &varDecl, cir::GlobalOp globalOp,
bool performInit);

mlir::LogicalResult emitLabel(const clang::LabelDecl *D);
mlir::LogicalResult emitLabelStmt(const clang::LabelStmt &S);

Expand Down
30 changes: 30 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,27 @@
//
//===----------------------------------------------------------------------===//

#include "CIRGenBuilder.h"
#include "CIRGenCXXABI.h"
#include "CIRGenCleanup.h"
#include "CIRGenFunction.h"
#include "CIRGenFunctionInfo.h"
#include "CIRGenModule.h"
#include "ConstantInitBuilder.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/BuiltinAttributes.h"

#include "clang/AST/GlobalDecl.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/Linkage.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/Support/ErrorHandling.h"

using namespace clang;
Expand Down Expand Up @@ -192,6 +202,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
CXXDtorType Type, bool ForVirtualBase,
bool Delegating, Address This,
QualType ThisTy) override;
void emitGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl,
cir::GlobalOp globalOp, bool performInit) override;
void registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D,
cir::FuncOp dtor, mlir::Value Addr) override;
void emitVirtualObjectDelete(CIRGenFunction &CGF, const CXXDeleteExpr *DE,
Expand Down Expand Up @@ -3019,3 +3031,21 @@ LValue CIRGenItaniumCXXABI::emitThreadLocalVarDeclLValue(CIRGenFunction &cgf,
lv = cgf.makeAddrLValue(addr, lvalType, AlignmentSource::Decl);
return lv;
}

/// The ARM code here follows the Itanium code closely enough that we just
/// special-case it at particular places.
void CIRGenItaniumCXXABI::emitGuardedInit(CIRGenFunction &cgf,
const VarDecl &varDecl,
cir::GlobalOp globalOp,
bool performInit) {
// NOTE(CIR): this diverges from CodeGen. We handle all these considerations
// in LoweringPrepare::handleStaticLocal

// Emit the initializer and add a global destructor if appropriate.
cgf.CGM.emitCXXGlobalVarDeclInit(&varDecl, globalOp, performInit);

// CIR diverges from IRGen here by emitting the init into the ctor region and
// marking the global as static local. The emission of the guard/acquire walk
// is done during LoweringPrepare.
globalOp.setStaticLocal(true);
}
Loading