Skip to content

Commit

Permalink
[CIR][Interface] introduce CIRGlobalValueInterface for GlobalOp and F…
Browse files Browse the repository at this point in the history
…uncOp (llvm#641)

CIRGlobalValueInterface inherits from mlir::Symbol as it should, and
GlobalOp and FuncOp now has interface mlir::Symbol through
CIRGlobalValueInterface

and this PR basically make function isDeclarationForLinker into the
CIRGlobalValueInterface interface. We also change some call sites of
isDeclaration to use CIRGlobalValueInterface when its appropriate.
  • Loading branch information
ghehg authored and lanza committed Oct 12, 2024
1 parent 01dd7d8 commit 92085eb
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 43 deletions.
23 changes: 8 additions & 15 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1872,7 +1872,10 @@ def TLSModel : I32EnumAttr<
let cppNamespace = "::mlir::cir";
}

def GlobalOp : CIR_Op<"global", [Symbol, DeclareOpInterfaceMethods<RegionBranchOpInterface>, NoRegionArguments]> {
def GlobalOp : CIR_Op<"global",
[DeclareOpInterfaceMethods<RegionBranchOpInterface>,
DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
NoRegionArguments]> {
let summary = "Declares or defines a global variable";
let description = [{
The `cir.global` operation declares or defines a named global variable.
Expand Down Expand Up @@ -1933,13 +1936,6 @@ def GlobalOp : CIR_Op<"global", [Symbol, DeclareOpInterfaceMethods<RegionBranchO
bool hasAvailableExternallyLinkage() {
return mlir::cir::isAvailableExternallyLinkage(getLinkage());
}
bool isDeclarationForLinker() {
if (hasAvailableExternallyLinkage())
return true;

return isDeclaration();
}

/// Whether the definition of this global may be replaced at link time.
bool isWeakForLinker() { return cir::isWeakForLinker(getLinkage()); }
}];
Expand Down Expand Up @@ -2585,7 +2581,8 @@ def BaseClassAddrOp : CIR_Op<"base_class_addr"> {

def FuncOp : CIR_Op<"func", [
AutomaticAllocationScope, CallableOpInterface, FunctionOpInterface,
IsolatedFromAbove, Symbol
DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
IsolatedFromAbove
]> {
let summary = "Declare or define a function";
let description = [{
Expand Down Expand Up @@ -2727,12 +2724,8 @@ def FuncOp : CIR_Op<"func", [

bool isDeclaration();

// FIXME: should be shared with GlobalOp extra declaration.
bool isDeclarationForLinker() {
if (mlir::cir::isAvailableExternallyLinkage(getLinkage()))
return true;

return isDeclaration();
bool hasAvailableExternallyLinkage() {
return mlir::cir::isAvailableExternallyLinkage(getLinkage());
}
}];

Expand Down
35 changes: 30 additions & 5 deletions clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,49 @@
#define MLIR_CIR_OP_INTERFACES

include "mlir/IR/OpBase.td"
include "mlir/IR/SymbolInterfaces.td"
include "mlir/Interfaces/CallInterfaces.td"

let cppNamespace = "::mlir::cir" in {
// The CIRCallOpInterface must be used instead of CallOpInterface when looking
// at arguments and other bits of CallOp. This creates a level of abstraction
// that's useful for handling indirect calls and other details.
def CIRCallOpInterface : OpInterface<"CIRCallOpInterface", [CallOpInterface]> {
def CIRCallOpInterface
: OpInterface<"CIRCallOpInterface", [CallOpInterface]> {
let methods = [
InterfaceMethod<"", "mlir::Operation::operand_iterator",
"arg_operand_begin", (ins)>,
InterfaceMethod<"", "mlir::Operation::operand_iterator",
"arg_operand_end", (ins)>,
InterfaceMethod<
"Return the operand at index 'i', accounts for indirect call or "
"exception info", "mlir::Value", "getArgOperand", (ins "unsigned":$i)>,
"Return the operand at index 'i', accounts for indirect call or "
"exception info",
"mlir::Value", "getArgOperand",
(ins "unsigned"
: $i)>,
InterfaceMethod<
"Return the number of operands, accounts for indirect call or "
"exception info", "unsigned", "getNumArgOperands", (ins)>,
"Return the number of operands, accounts for indirect call or "
"exception info",
"unsigned", "getNumArgOperands", (ins)>,
];
}

def CIRGlobalValueInterface
: OpInterface<"CIRGlobalValueInterface", [Symbol]> {

let methods = [
InterfaceMethod<"",
"bool", "hasAvailableExternallyLinkage", (ins), [{}],
/*defaultImplementation=*/[{ return false; }]
>,
InterfaceMethod<"",
"bool", "isDeclarationForLinker", (ins), [{}],
/*defaultImplementation=*/[{
if ($_op.hasAvailableExternallyLinkage())
return true;
return $_op.isDeclaration();
}]
>,
];
}

Expand Down
11 changes: 6 additions & 5 deletions clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,19 @@ bool CIRGenModule::tryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
// Check if we have it already.
StringRef MangledName = getMangledName(AliasDecl);
auto Entry = getGlobalValue(MangledName);
auto fnOp = dyn_cast_or_null<mlir::cir::FuncOp>(Entry);
if (Entry && fnOp && !fnOp.isDeclaration())
auto globalValue = dyn_cast<mlir::cir::CIRGlobalValueInterface>(Entry);
if (Entry && globalValue && !globalValue.isDeclaration())
return false;
if (Replacements.count(MangledName))
return false;

assert(fnOp && "only knows how to handle FuncOp");
assert(globalValue && "only knows how to handle GlobalValue");
[[maybe_unused]] auto AliasValueType = getTypes().GetFunctionType(AliasDecl);

// Find the referent.
auto Aliasee = cast<mlir::cir::FuncOp>(GetAddrOfGlobal(TargetDecl));

auto AliaseeGV = dyn_cast_or_null<mlir::cir::CIRGlobalValueInterface>(
GetAddrOfGlobal(TargetDecl));
// Instead of creating as alias to a linkonce_odr, replace all of the uses
// of the aliasee.
if (mlir::cir::isDiscardableIfUnused(Linkage) &&
Expand Down Expand Up @@ -161,7 +162,7 @@ bool CIRGenModule::tryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
// is
// avaialable_externally, don't emit an alias. We can't emit aliases to
// declarations; that's just not how aliases work.
if (Aliasee.isDeclarationForLinker())
if (AliaseeGV && AliaseeGV.isDeclarationForLinker())
return true;

// Don't create an alias to a linker weak symbol. This avoids producing
Expand Down
17 changes: 12 additions & 5 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,10 +444,14 @@ static void emitConstructorDestructorAlias(CIRGenModule &CGM,

// Does this function alias already exists?
StringRef MangledName = CGM.getMangledName(AliasDecl);
auto globalValue = dyn_cast_or_null<mlir::cir::CIRGlobalValueInterface>(
CGM.getGlobalValue(MangledName));
if (globalValue && !globalValue.isDeclaration()) {
return;
}

auto Entry =
dyn_cast_or_null<mlir::cir::FuncOp>(CGM.getGlobalValue(MangledName));
if (Entry && !Entry.isDeclaration())
return;

// Retrieve aliasee info.
auto Aliasee =
Expand Down Expand Up @@ -2047,19 +2051,22 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &CGVT,
// EmitFundamentalRTTIDescriptors(RD);
}

auto VTableAsGlobalValue =
dyn_cast<mlir::cir::CIRGlobalValueInterface>(*VTable);
assert(VTableAsGlobalValue && "VTable must support CIRGlobalValueInterface");
bool isDeclarationForLinker = VTableAsGlobalValue.isDeclarationForLinker();
// Always emit type metadata on non-available_externally definitions, and on
// available_externally definitions if we are performing whole program
// devirtualization. For WPD we need the type metadata on all vtable
// definitions to ensure we associate derived classes with base classes
// defined in headers but with a strong definition only in a shared
// library.
if (!VTable.isDeclarationForLinker() ||
CGM.getCodeGenOpts().WholeProgramVTables) {
if (!isDeclarationForLinker || CGM.getCodeGenOpts().WholeProgramVTables) {
CGM.buildVTableTypeMetadata(RD, VTable, VTLayout);
// For available_externally definitions, add the vtable to
// @llvm.compiler.used so that it isn't deleted before whole program
// analysis.
if (VTable.isDeclarationForLinker()) {
if (isDeclarationForLinker) {
llvm_unreachable("NYI");
assert(CGM.getCodeGenOpts().WholeProgramVTables);
assert(!UnimplementedFeature::addCompilerUsedGlobal());
Expand Down
26 changes: 13 additions & 13 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,11 +470,12 @@ void CIRGenModule::buildGlobalFunctionDefinition(GlobalDecl GD,
Op = GetAddrOfFunction(GD, Ty, /*ForVTable=*/false, /*DontDefer=*/true,
ForDefinition);

auto Fn = cast<mlir::cir::FuncOp>(Op);
// Already emitted.
if (!Fn.isDeclaration())
auto globalVal = dyn_cast_or_null<mlir::cir::CIRGlobalValueInterface>(Op);
if (globalVal && !globalVal.isDeclaration()) {
// Already emitted.
return;

}
auto Fn = cast<mlir::cir::FuncOp>(Op);
setFunctionLinkage(GD, Fn);
setGVProperties(Op, D);
// TODO(cir): MaubeHandleStaticInExternC
Expand Down Expand Up @@ -2435,18 +2436,17 @@ void CIRGenModule::buildGlobalDecl(clang::GlobalDecl &D) {
// ways (e.g. by an extern inline function acquiring a strong function
// redefinition). Just ignore those cases.
// TODO: Not sure what to map this to for MLIR
if (auto Fn = dyn_cast<mlir::cir::FuncOp>(Op))
if (!Fn.isDeclaration())
return;

// TODO(cir): create a global value trait that allow us to uniformly handle
// global variables and functions.
auto globalValueOp = Op;
if (auto Gv = dyn_cast<mlir::cir::GetGlobalOp>(Op)) {
auto *result =
mlir::SymbolTable::lookupSymbolIn(getModule(), Gv.getNameAttr());
if (auto globalOp = dyn_cast<mlir::cir::GlobalOp>(result))
if (!globalOp.isDeclaration())
return;
globalValueOp = result;
}

if (auto cirGlobalValue =
dyn_cast<mlir::cir::CIRGlobalValueInterface>(globalValueOp)) {
if (!cirGlobalValue.isDeclaration())
return;
}

// If this is OpenMP, check if it is legal to emit this global normally.
Expand Down

0 comments on commit 92085eb

Please sign in to comment.