Skip to content

[CIR] Upstream initial function call support #134673

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

Merged
merged 1 commit into from
Apr 10, 2025
Merged
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: 13 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,19 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return create<cir::PtrStrideOp>(loc, base.getType(), base, stride);
}

//===--------------------------------------------------------------------===//
// Call operators
//===--------------------------------------------------------------------===//

cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee) {
auto op = create<cir::CallOp>(loc, callee);
return op;
}

cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee) {
return createCallOp(loc, mlir::SymbolRefAttr::get(callee));
}

//===--------------------------------------------------------------------===//
// Cast/Conversion Operators
//===--------------------------------------------------------------------===//
Expand Down
42 changes: 42 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,48 @@ def FuncOp : CIR_Op<"func", [
let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
// CallOp
//===----------------------------------------------------------------------===//

class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
: Op<CIR_Dialect, mnemonic,
!listconcat(extra_traits,
[DeclareOpInterfaceMethods<CIRCallOpInterface>,
DeclareOpInterfaceMethods<SymbolUserOpInterface>])> {
let hasCustomAssemblyFormat = 1;
let skipDefaultBuilders = 1;
let hasVerifier = 0;

// TODO(cir): for now cir.call is just a tiny shell of what it will become.
// More attributes, arguments, and properties will be added in the future as
// the upstreaming process moves on. The verifiers is also missing for now,
// will add in the future.

dag commonArgs = (ins FlatSymbolRefAttr:$callee);
}

def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
let summary = "call a function";
let description = [{
The `cir.call` operation represents a direct call to a function that is
within the same symbol scope as the call. The callee is encoded as a symbol
reference attribute named `callee`.

Example:

```mlir
%0 = cir.call @foo()
```
}];

let arguments = commonArgs;

let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee), [{
$_state.addAttribute("callee", callee);
}]>];
}

//===----------------------------------------------------------------------===//
// UnreachableOp
//===----------------------------------------------------------------------===//
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@

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

let cppNamespace = "::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", []> {
// Currently we don't have any methods defined in CIRCallOpInterface. We'll
// add more methods as the upstreaming proceeds.
}

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

Expand Down
24 changes: 24 additions & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ struct MissingFeatures {
static bool opFuncLinkage() { return false; }
static bool opFuncVisibility() { return false; }

// CallOp handling
static bool opCallBuiltinFunc() { return false; }
static bool opCallPseudoDtor() { return false; }
static bool opCallArgs() { return false; }
static bool opCallReturn() { return false; }
static bool opCallArgEvaluationOrder() { return false; }
static bool opCallCallConv() { return false; }
static bool opCallSideEffect() { return false; }
static bool opCallChainCall() { return false; }
static bool opCallNoPrototypeFunc() { return false; }
static bool opCallMustTail() { return false; }
static bool opCallIndirect() { return false; }
static bool opCallVirtual() { return false; }
static bool opCallInAlloca() { return false; }
static bool opCallAttrs() { return false; }
static bool opCallSurroundingTry() { return false; }
static bool opCallASTAttr() { return false; }

// ScopeOp handling
static bool opScopeCleanupRegion() { return false; }

Expand All @@ -90,7 +108,10 @@ struct MissingFeatures {
static bool lowerModeOptLevel() { return false; }
static bool opTBAA() { return false; }
static bool objCLifetime() { return false; }
static bool objCBlocks() { return false; }
static bool emitNullabilityCheck() { return false; }
static bool emitLValueAlignmentAssumption() { return false; }
static bool emitLifetimeMarkers() { return false; }
static bool astVarDeclInterface() { return false; }
static bool stackSaveOp() { return false; }
static bool aggValueSlot() { return false; }
Expand All @@ -113,6 +134,8 @@ struct MissingFeatures {
static bool incrementProfileCounter() { return false; }
static bool insertBuiltinUnpredictable() { return false; }
static bool objCGC() { return false; }
static bool weakRefReference() { return false; }
static bool hip() { return false; }

// Missing types
static bool dataMemberType() { return false; }
Expand All @@ -132,6 +155,7 @@ struct MissingFeatures {
static bool complexImagOp() { return false; }
static bool complexRealOp() { return false; }
static bool ifOp() { return false; }
static bool invokeOp() { return false; }
static bool labelOp() { return false; }
static bool ptrDiffOp() { return false; }
static bool ptrStrideOp() { return false; }
Expand Down
95 changes: 95 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCall.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//===--- CIRGenCall.cpp - Encapsulate calling convention details ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// These classes wrap the information about a call or function definition used
// to handle ABI compliancy.
//
//===----------------------------------------------------------------------===//

#include "CIRGenCall.h"
#include "CIRGenFunction.h"
#include "clang/CIR/MissingFeatures.h"

using namespace clang;
using namespace clang::CIRGen;

CIRGenFunctionInfo *CIRGenFunctionInfo::create() {
// For now we just create an empty CIRGenFunctionInfo.
CIRGenFunctionInfo *fi = new CIRGenFunctionInfo();
return fi;
}

CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
assert(!cir::MissingFeatures::opCallVirtual());
return *this;
}

static const CIRGenFunctionInfo &arrangeFreeFunctionLikeCall(CIRGenTypes &cgt) {
assert(!cir::MissingFeatures::opCallArgs());
return cgt.arrangeCIRFunctionInfo();
}

const CIRGenFunctionInfo &CIRGenTypes::arrangeFreeFunctionCall() {
return arrangeFreeFunctionLikeCall(*this);
}

static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf,
mlir::Location callLoc,
cir::FuncOp directFuncOp) {
CIRGenBuilderTy &builder = cgf.getBuilder();

assert(!cir::MissingFeatures::opCallSurroundingTry());
assert(!cir::MissingFeatures::invokeOp());

assert(builder.getInsertionBlock() && "expected valid basic block");
assert(!cir::MissingFeatures::opCallIndirect());

return builder.createCallOp(callLoc, directFuncOp);
}

RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
const CIRGenCallee &callee,
cir::CIRCallOpInterface *callOp,
mlir::Location loc) {
assert(!cir::MissingFeatures::opCallArgs());
assert(!cir::MissingFeatures::emitLifetimeMarkers());

const CIRGenCallee &concreteCallee = callee.prepareConcreteCallee(*this);
mlir::Operation *calleePtr = concreteCallee.getFunctionPointer();

assert(!cir::MissingFeatures::opCallInAlloca());

mlir::NamedAttrList attrs;
StringRef funcName;
if (auto calleeFuncOp = dyn_cast<cir::FuncOp>(calleePtr))
funcName = calleeFuncOp.getName();

assert(!cir::MissingFeatures::opCallCallConv());
assert(!cir::MissingFeatures::opCallSideEffect());
assert(!cir::MissingFeatures::opCallAttrs());

assert(!cir::MissingFeatures::invokeOp());

auto directFuncOp = dyn_cast<cir::FuncOp>(calleePtr);
assert(!cir::MissingFeatures::opCallIndirect());
assert(!cir::MissingFeatures::opCallAttrs());

cir::CIRCallOpInterface theCall = emitCallLikeOp(*this, loc, directFuncOp);

if (callOp)
*callOp = theCall;

assert(!cir::MissingFeatures::opCallMustTail());
assert(!cir::MissingFeatures::opCallReturn());

// For now we just return nothing because we don't have support for return
// values yet.
RValue ret = RValue::get(nullptr);

return ret;
}
58 changes: 58 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,73 @@
#ifndef CLANG_LIB_CODEGEN_CIRGENCALL_H
#define CLANG_LIB_CODEGEN_CIRGENCALL_H

#include "mlir/IR/Operation.h"
#include "clang/AST/GlobalDecl.h"
#include "llvm/ADT/SmallVector.h"

namespace clang::CIRGen {

class CIRGenFunction;

/// Abstract information about a function or function prototype.
class CIRGenCalleeInfo {
clang::GlobalDecl calleeDecl;

public:
explicit CIRGenCalleeInfo() : calleeDecl() {}
CIRGenCalleeInfo(clang::GlobalDecl calleeDecl) : calleeDecl(calleeDecl) {}
};

class CIRGenCallee {
enum class SpecialKind : uintptr_t {
Invalid,

Last = Invalid,
};

SpecialKind kindOrFunctionPtr;

union {
CIRGenCalleeInfo abstractInfo;
};

public:
CIRGenCallee() : kindOrFunctionPtr(SpecialKind::Invalid) {}

CIRGenCallee(const CIRGenCalleeInfo &abstractInfo, mlir::Operation *funcPtr)
: kindOrFunctionPtr(SpecialKind(reinterpret_cast<uintptr_t>(funcPtr))),
abstractInfo(abstractInfo) {
assert(funcPtr && "configuring callee without function pointer");
}

static CIRGenCallee
forDirect(mlir::Operation *funcPtr,
const CIRGenCalleeInfo &abstractInfo = CIRGenCalleeInfo()) {
return CIRGenCallee(abstractInfo, funcPtr);
}

bool isOrdinary() const {
return uintptr_t(kindOrFunctionPtr) > uintptr_t(SpecialKind::Last);
}

/// If this is a delayed callee computation of some sort, prepare a concrete
/// callee
CIRGenCallee prepareConcreteCallee(CIRGenFunction &cgf) const;

mlir::Operation *getFunctionPointer() const {
assert(isOrdinary());
return reinterpret_cast<mlir::Operation *>(kindOrFunctionPtr);
}
};

/// Type for representing both the decl and type of parameters to a function.
/// The decl must be either a ParmVarDecl or ImplicitParamDecl.
class FunctionArgList : public llvm::SmallVector<const clang::VarDecl *, 16> {};

struct CallArg {};

class CallArgList : public llvm::SmallVector<CallArg, 8> {};

} // namespace clang::CIRGen

#endif // CLANG_LIB_CODEGEN_CIRGENCALL_H
Loading