Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
61 changes: 61 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -3762,6 +3762,67 @@ def CIR_OptionalPriorityAttr : OptionalAttr<
>
>;

def CIR_AliasOp : CIR_Op<"alias", [AutomaticAllocationScope, CallableOpInterface,
FunctionOpInterface, IsolatedFromAbove]> {
Copy link
Member

Choose a reason for hiding this comment

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

You need to add a description, examples, etc, see others for inspiration

let arguments = (ins
SymbolNameAttr:$sym_name,
TypeAttrOf<CIR_FuncType>:$function_type, FlatSymbolRefAttr:$aliasee,
CIR_VisibilityAttr:$global_visibility,
DefaultValuedAttr<CIR_GlobalLinkageKind,
"GlobalLinkageKind::ExternalLinkage">:$linkage,
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs,
DefaultValuedAttr<CIR_CallingConv, "CallingConv::C">:$calling_conv
);

let regions = (region AnyRegion:$body);
let extraClassDeclaration = [{
/// Returns the region on the current operation that is callable. This may
/// return null in the case of an external callable object, e.g. an external
/// function.
::mlir::Region *getCallableRegion() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will this operation also be used for global variable aliases?

Copy link
Contributor Author

@badumbatish badumbatish Oct 1, 2025

Choose a reason for hiding this comment

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

hi Andy, I want to have a simple lowering to LLVM_AliasOp but not sure how messy if the same op has two different functionalities. I want to try using 1 op first. I can change the comments

return nullptr;
}

/// Returns the results types that the callable region produces when
/// executed.
llvm::ArrayRef<mlir::Type> getCallableResults() {
return getFunctionType().getReturnTypes();
}

/// Returns the argument attributes for all callable region arguments or
/// null if there are none.
::mlir::ArrayAttr getCallableArgAttrs() {
return getArgAttrs().value_or(nullptr);
}

/// Returns the result attributes for all callable region results or null if
/// there are none.
::mlir::ArrayAttr getCallableResAttrs() {
return getResAttrs().value_or(nullptr);
}

/// Returns the argument types of this function.
llvm::ArrayRef<mlir::Type> getArgumentTypes() {
return getFunctionType().getInputs();
}

/// Returns 0 or 1 result type of this function (0 in the case of a function
/// returing void)
llvm::ArrayRef<mlir::Type> getResultTypes() {
return getFunctionType().getReturnTypes();
}

//===------------------------------------------------------------------===//
// SymbolOpInterface Methods
//===------------------------------------------------------------------===//

bool isDeclaration() {
return true;
}
}];
}

def FuncOp : CIR_Op<"func", [
AutomaticAllocationScope, CallableOpInterface, FunctionOpInterface,
DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
Expand Down
47 changes: 41 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2460,11 +2460,9 @@ void CIRGenModule::emitAliasForGlobal(StringRef mangledName,
// point.
auto &fnInfo = getTypes().arrangeCXXStructorDeclaration(aliasGD);
auto fnType = getTypes().GetFunctionType(fnInfo);

auto alias = createCIRFunction(getLoc(aliasGD.getDecl()->getSourceRange()),
mangledName, fnType, aliasFD);
alias.setAliasee(aliasee.getName());
alias.setLinkage(linkage);
auto alias = createCIRAliasFunction(
getLoc(aliasGD.getDecl()->getSourceRange()), mangledName, fnType,
aliasee.getName(), linkage, aliasFD);
// Declarations cannot have public MLIR visibility, just mark them private
// but this really should have no meaning since CIR should not be using
// this information to derive linkage information.
Expand All @@ -2476,7 +2474,8 @@ void CIRGenModule::emitAliasForGlobal(StringRef mangledName,

// Switch any previous uses to the alias.
if (op) {
llvm_unreachable("NYI");
op->replaceAllUsesWith(alias);
op->erase();
} else {
// Name already set by createCIRFunction
}
Expand Down Expand Up @@ -2707,7 +2706,43 @@ bool CIRGenModule::lookupRepresentativeDecl(StringRef mangledName,
result = res->getValue();
return true;
}
cir::AliasOp
CIRGenModule::createCIRAliasFunction(mlir::Location loc, llvm::StringRef name,
cir::FuncType Ty, StringRef aliasee,
cir::GlobalLinkageKind linkage,
const clang::FunctionDecl *FD) {
// not sure whats going on here but will repeat format of func op
AliasOp Alias;
{
mlir::OpBuilder::InsertionGuard guard(builder);

// Some global emissions are triggered while emitting a function, e.g.
// void s() { x.method() }
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't seem to apply here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i think i need some help and explanation on this comment in the OG createCIRFunction to understand more why it's not applicable.

//
// Be sure to insert a new function before a current one.
auto *curCGF = getCurrCIRGenFun();
if (curCGF)
builder.setInsertionPoint(curCGF->CurFn);
Copy link
Member

Choose a reason for hiding this comment

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

is this ever the case? I can't see how one might want to insert an alias op outside the module level scope

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i'm not quite sure what you mean here? don't we want to reset the insertion point if we're inside a function?


Alias = cir::AliasOp::create(builder, loc, name, Ty, aliasee,
cir::VisibilityKind::Default, linkage, nullptr,
nullptr);

assert(Alias.isDeclaration() && "expected empty body");

// A declaration gets private visibility by default, but external linkage
// as the default linkage.
Alias.setLinkageAttr(cir::GlobalLinkageKindAttr::get(
&getMLIRContext(), cir::GlobalLinkageKind::ExternalLinkage));
mlir::SymbolTable::setSymbolVisibility(
Alias, mlir::SymbolTable::Visibility::Private);

if (!curCGF)
theModule.push_back(Alias);
}

return Alias;
}
cir::FuncOp CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
cir::FuncType ty,
const clang::FunctionDecl *fd) {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,10 @@ class CIRGenModule : public CIRGenTypeCache {

void ReplaceUsesOfNonProtoTypeWithRealFunction(mlir::Operation *Old,
cir::FuncOp NewFn);
cir::AliasOp createCIRAliasFunction(mlir::Location loc, llvm::StringRef name,
cir::FuncType Ty, StringRef aliasee,
cir::GlobalLinkageKind linkage,
const clang::FunctionDecl *FD);

// TODO: CodeGen also passes an AttributeList here. We'll have to match that
// in CIR
Expand Down
90 changes: 51 additions & 39 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2952,55 +2952,67 @@ verifyCallCommInSymbolUses(Operation *op, SymbolTableCollection &symbolTable) {
return success();

cir::FuncOp fn = symbolTable.lookupNearestSymbolFrom<cir::FuncOp>(op, fnAttr);
if (!fn)
return op->emitOpError() << "'" << fnAttr.getValue()
<< "' does not reference a valid function";
cir::AliasOp alias =
symbolTable.lookupNearestSymbolFrom<cir::AliasOp>(op, fnAttr);
if (!fn && !alias)
return op->emitOpError()
<< "'" << fnAttr.getValue()
<< "' does not reference a valid function or alias";
auto callIf = dyn_cast<cir::CIRCallOpInterface>(op);
assert(callIf && "expected CIR call interface to be always available");

// Verify that the operand and result types match the callee. Note that
// argument-checking is disabled for functions without a prototype.
auto fnType = fn.getFunctionType();
if (!fn.getNoProto()) {
unsigned numCallOperands = callIf.getNumArgOperands();
unsigned numFnOpOperands = fnType.getNumInputs();

if (!fnType.isVarArg() && numCallOperands != numFnOpOperands)
return op->emitOpError("incorrect number of operands for callee");

if (fnType.isVarArg() && numCallOperands < numFnOpOperands)
return op->emitOpError("too few operands for callee");
auto verify = [&op, &callIf](bool getNoProto, cir::FuncType fnType,
cir::CallingConv cc) -> LogicalResult {
if (!getNoProto) {
unsigned numCallOperands = callIf.getNumArgOperands();
unsigned numFnOpOperands = fnType.getNumInputs();

for (unsigned i = 0, e = numFnOpOperands; i != e; ++i)
if (callIf.getArgOperand(i).getType() != fnType.getInput(i))
return op->emitOpError("operand type mismatch: expected operand type ")
<< fnType.getInput(i) << ", but provided "
<< op->getOperand(i).getType() << " for operand number " << i;
}

// Calling convention must match.
if (callIf.getCallingConv() != fn.getCallingConv())
return op->emitOpError("calling convention mismatch: expected ")
<< stringifyCallingConv(fn.getCallingConv()) << ", but provided "
<< stringifyCallingConv(callIf.getCallingConv());
if (!fnType.isVarArg() && numCallOperands != numFnOpOperands)
return op->emitOpError("incorrect number of operands for callee");

// Void function must not return any results.
if (fnType.hasVoidReturn() && op->getNumResults() != 0)
return op->emitOpError("callee returns void but call has results");
if (fnType.isVarArg() && numCallOperands < numFnOpOperands)
return op->emitOpError("too few operands for callee");

// Non-void function calls must return exactly one result.
if (!fnType.hasVoidReturn() && op->getNumResults() != 1)
return op->emitOpError("incorrect number of results for callee");
for (unsigned i = 0, e = numFnOpOperands; i != e; ++i)
if (callIf.getArgOperand(i).getType() != fnType.getInput(i))
return op->emitOpError(
"operand type mismatch: expected operand type ")
<< fnType.getInput(i) << ", but provided "
<< op->getOperand(i).getType() << " for operand number " << i;
}
// Calling convention must match.
if (callIf.getCallingConv() != cc)
return op->emitOpError("calling convention mismatch: expected ")
<< stringifyCallingConv(cc) << ", but provided "
<< stringifyCallingConv(callIf.getCallingConv());

// Void function must not return any results.
if (fnType.hasVoidReturn() && op->getNumResults() != 0)
return op->emitOpError("callee returns void but call has results");

// Non-void function calls must return exactly one result.
if (!fnType.hasVoidReturn() && op->getNumResults() != 1)
return op->emitOpError("incorrect number of results for callee");

// Parent function and return value types must match.
if (!fnType.hasVoidReturn() &&
op->getResultTypes().front() != fnType.getReturnType()) {
return op->emitOpError("result type mismatch: expected ")
<< fnType.getReturnType() << ", but provided "
<< op->getResult(0).getType();
}

// Parent function and return value types must match.
if (!fnType.hasVoidReturn() &&
op->getResultTypes().front() != fnType.getReturnType()) {
return op->emitOpError("result type mismatch: expected ")
<< fnType.getReturnType() << ", but provided "
<< op->getResult(0).getType();
}
return success();
};

return success();
if (fn)
return verify(fn.getNoProto(), fn.getFunctionType(), fn.getCallingConv());
if (alias)
return verify(/*getNoProto=*/true, alias.getFunctionType(),
alias.getCallingConv());
llvm_unreachable("unreachable");
}

static mlir::ParseResult
Expand Down
Loading