Skip to content
Merged
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
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 @@ -157,6 +157,19 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return create<cir::ComplexImagOp>(loc, operandTy.getElementType(), operand);
}

cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr,
uint64_t alignment = 0) {
mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment);
assert(!cir::MissingFeatures::opLoadStoreVolatile());
assert(!cir::MissingFeatures::opLoadStoreMemOrder());
return create<cir::LoadOp>(loc, ptr, /*isDeref=*/false, alignmentAttr);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since you bugged me about it before, should this be cir::LoadOp::Create?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, probably so. I overlooked it since it's in the builder class itself, but it should probably still follow the new idiom.

}

mlir::Value createAlignedLoad(mlir::Location loc, mlir::Value ptr,
uint64_t alignment) {
return createLoad(loc, ptr, alignment);
}

mlir::Value createNot(mlir::Value value) {
return create<cir::UnaryOp>(value.getLoc(), value.getType(),
cir::UnaryOpKind::Not, value);
Expand Down
48 changes: 48 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,54 @@ def CIR_VTableGetVPtrOp : CIR_Op<"vtable.get_vptr", [Pure]> {
}];
}

//===----------------------------------------------------------------------===//
// VTableGetVirtualFnAddrOp
//===----------------------------------------------------------------------===//

def CIR_VTableGetVirtualFnAddrOp : CIR_Op<"vtable.get_virtual_fn_addr", [
Pure
]> {
let summary = "Get a the address of a virtual function pointer";
let description = [{
The `vtable.get_virtual_fn_addr` operation retrieves the address of a
virtual function pointer from an object's vtable (__vptr).
This is an abstraction to perform the basic pointer arithmetic to get
the address of the virtual function pointer, which can then be loaded and
called.

The `vptr` operand must be a `!cir.ptr<!cir.vptr>` value, which would
have been returned by a previous call to `cir.vatble.get_vptr`. The
`index` operand is an index of the virtual function in the vtable.

The return type is a pointer-to-pointer to the function type.

Example:
```mlir
%2 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C>
%3 = cir.vtable.get_vptr %2 : !cir.ptr<!rec_C> -> !cir.ptr<!cir.vptr>
%4 = cir.load %3 : !cir.ptr<!cir.vptr>, !cir.vptr
%5 = cir.vtable.get_virtual_fn_addr %4[2] : !cir.vptr
-> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>>
%6 = cir.load align(8) %5 : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>)
-> !s32i>>>,
!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>
%7 = cir.call %6(%2) : (!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>,
!cir.ptr<!rec_C>) -> !s32i
```
}];

let arguments = (ins
Arg<CIR_VPtrType, "vptr", [MemRead]>:$vptr,
I64Attr:$index);

let results = (outs CIR_PointerType:$result);

let assemblyFormat = [{
$vptr `[` $index `]` attr-dict
`:` qualified(type($vptr)) `->` qualified(type($result))
}];
}

//===----------------------------------------------------------------------===//
// SetBitfieldOp
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ struct MissingFeatures {
static bool opCallArgEvaluationOrder() { return false; }
static bool opCallCallConv() { return false; }
static bool opCallMustTail() { return false; }
static bool opCallVirtual() { return false; }
static bool opCallInAlloca() { return false; }
static bool opCallAttrs() { return false; }
static bool opCallSurroundingTry() { return false; }
Expand Down Expand Up @@ -204,6 +203,7 @@ struct MissingFeatures {
static bool dataLayoutTypeAllocSize() { return false; }
static bool dataLayoutTypeStoreSize() { return false; }
static bool deferredCXXGlobalInit() { return false; }
static bool devirtualizeMemberFunction() { return false; }
static bool ehCleanupFlags() { return false; }
static bool ehCleanupScope() { return false; }
static bool ehCleanupScopeRequiresEHCleanup() { return false; }
Expand All @@ -215,6 +215,7 @@ struct MissingFeatures {
static bool emitLValueAlignmentAssumption() { return false; }
static bool emitNullabilityCheck() { return false; }
static bool emitTypeCheck() { return false; }
static bool emitTypeMetadataCodeForVCall() { return false; }
static bool fastMathFlags() { return false; }
static bool fpConstraints() { return false; }
static bool generateDebugInfo() { return false; }
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ class CIRGenCXXABI {
/// parameter.
virtual bool needsVTTParameter(clang::GlobalDecl gd) { return false; }

/// Perform ABI-specific "this" argument adjustment required prior to
/// a call of a virtual function.
/// The "VirtualCall" argument is true iff the call itself is virtual.
virtual Address adjustThisArgumentForVirtualFunctionCall(CIRGenFunction &cgf,
clang::GlobalDecl gd,
Address thisPtr,
bool virtualCall) {
return thisPtr;
}

/// Build a parameter variable suitable for 'this'.
void buildThisParam(CIRGenFunction &cgf, FunctionArgList &params);

Expand Down Expand Up @@ -100,6 +110,13 @@ class CIRGenCXXABI {
virtual cir::GlobalOp getAddrOfVTable(const CXXRecordDecl *rd,
CharUnits vptrOffset) = 0;

/// Build a virtual function pointer in the ABI-specific way.
virtual CIRGenCallee getVirtualFunctionPointer(CIRGenFunction &cgf,
clang::GlobalDecl gd,
Address thisAddr,
mlir::Type ty,
SourceLocation loc) = 0;

/// Get the address point of the vtable for the given base subobject.
virtual mlir::Value
getVTableAddressPoint(BaseSubobject base,
Expand Down
49 changes: 35 additions & 14 deletions clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,10 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
const Expr *base) {
assert(isa<CXXMemberCallExpr>(ce) || isa<CXXOperatorCallExpr>(ce));

if (md->isVirtual()) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: virtual call");
return RValue::get(nullptr);
}
// Compute the object pointer.
bool canUseVirtualCall = md->isVirtual() && !hasQualifier;
const CXXMethodDecl *devirtualizedMethod = nullptr;
assert(!cir::MissingFeatures::devirtualizeMemberFunction());

// Note on trivial assignment
// --------------------------
Expand Down Expand Up @@ -127,7 +126,8 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
return RValue::get(nullptr);

// Compute the function type we're calling
const CXXMethodDecl *calleeDecl = md;
const CXXMethodDecl *calleeDecl =
devirtualizedMethod ? devirtualizedMethod : md;
const CIRGenFunctionInfo *fInfo = nullptr;
if (isa<CXXDestructorDecl>(calleeDecl)) {
cgm.errorNYI(ce->getSourceRange(),
Expand All @@ -137,25 +137,46 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(

fInfo = &cgm.getTypes().arrangeCXXMethodDeclaration(calleeDecl);

mlir::Type ty = cgm.getTypes().getFunctionType(*fInfo);
cir::FuncType ty = cgm.getTypes().getFunctionType(*fInfo);

assert(!cir::MissingFeatures::sanitizers());
assert(!cir::MissingFeatures::emitTypeCheck());

// C++ [class.virtual]p12:
// Explicit qualification with the scope operator (5.1) suppresses the
// virtual call mechanism.
//
// We also don't emit a virtual call if the base expression has a record type
// because then we know what the type is.
bool useVirtualCall = canUseVirtualCall && !devirtualizedMethod;

if (isa<CXXDestructorDecl>(calleeDecl)) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: destructor call");
return RValue::get(nullptr);
}

assert(!cir::MissingFeatures::sanitizers());
if (getLangOpts().AppleKext) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: AppleKext");
return RValue::get(nullptr);
CIRGenCallee callee;
if (useVirtualCall) {
callee = CIRGenCallee::forVirtual(ce, md, thisPtr.getAddress(), ty);
} else {
assert(!cir::MissingFeatures::sanitizers());
if (getLangOpts().AppleKext) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: AppleKext");
return RValue::get(nullptr);
}

callee = CIRGenCallee::forDirect(cgm.getAddrOfFunction(calleeDecl, ty),
GlobalDecl(calleeDecl));
}

if (md->isVirtual()) {
Address newThisAddr =
cgm.getCXXABI().adjustThisArgumentForVirtualFunctionCall(
*this, calleeDecl, thisPtr.getAddress(), useVirtualCall);
thisPtr.setAddress(newThisAddr);
}
CIRGenCallee callee =
CIRGenCallee::forDirect(cgm.getAddrOfFunction(md, ty), GlobalDecl(md));

return emitCXXMemberOrOperatorCall(
calleeDecl, callee, returnValue, thisPtr.getPointer(),
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ cir::FuncType CIRGenTypes::getFunctionType(const CIRGenFunctionInfo &info) {
}

CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
assert(!cir::MissingFeatures::opCallVirtual());
if (isVirtual()) {
const CallExpr *ce = getVirtualCallExpr();
return cgf.cgm.getCXXABI().getVirtualFunctionPointer(
cgf, getVirtualMethodDecl(), getThisAddress(), getVirtualFunctionType(),
ce ? ce->getBeginLoc() : SourceLocation());
}
return *this;
}

Expand Down
46 changes: 44 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ class CIRGenCallee {
Invalid,
Builtin,
PseudoDestructor,
Virtual,

Last = Builtin,
Last = Virtual
};

struct BuiltinInfoStorage {
Expand All @@ -58,13 +59,20 @@ class CIRGenCallee {
struct PseudoDestructorInfoStorage {
const clang::CXXPseudoDestructorExpr *expr;
};
struct VirtualInfoStorage {
const clang::CallExpr *ce;
clang::GlobalDecl md;
Address addr;
cir::FuncType fTy;
};

SpecialKind kindOrFunctionPtr;

union {
CIRGenCalleeInfo abstractInfo;
BuiltinInfoStorage builtinInfo;
PseudoDestructorInfoStorage pseudoDestructorInfo;
VirtualInfoStorage virtualInfo;
};

explicit CIRGenCallee(SpecialKind kind) : kindOrFunctionPtr(kind) {}
Expand Down Expand Up @@ -128,7 +136,8 @@ class CIRGenCallee {
CIRGenCallee prepareConcreteCallee(CIRGenFunction &cgf) const;

CIRGenCalleeInfo getAbstractInfo() const {
assert(!cir::MissingFeatures::opCallVirtual());
if (isVirtual())
return virtualInfo.md;
assert(isOrdinary());
return abstractInfo;
}
Expand All @@ -138,6 +147,39 @@ class CIRGenCallee {
return reinterpret_cast<mlir::Operation *>(kindOrFunctionPtr);
}

bool isVirtual() const { return kindOrFunctionPtr == SpecialKind::Virtual; }

static CIRGenCallee forVirtual(const clang::CallExpr *ce,
clang::GlobalDecl md, Address addr,
cir::FuncType fTy) {
CIRGenCallee result(SpecialKind::Virtual);
result.virtualInfo.ce = ce;
result.virtualInfo.md = md;
result.virtualInfo.addr = addr;
result.virtualInfo.fTy = fTy;
return result;
}

const clang::CallExpr *getVirtualCallExpr() const {
assert(isVirtual());
return virtualInfo.ce;
}

clang::GlobalDecl getVirtualMethodDecl() const {
assert(isVirtual());
return virtualInfo.md;
}

Address getThisAddress() const {
assert(isVirtual());
return virtualInfo.addr;
}

cir::FuncType getVirtualFunctionType() const {
assert(isVirtual());
return virtualInfo.fTy;
}

void setFunctionPointer(mlir::Operation *functionPtr) {
assert(isOrdinary());
kindOrFunctionPtr = SpecialKind(reinterpret_cast<uintptr_t>(functionPtr));
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,20 @@ Address CIRGenFunction::getAddressOfBaseClass(
return value;
}

// TODO(cir): this can be shared with LLVM codegen.
bool CIRGenFunction::shouldEmitVTableTypeCheckedLoad(const CXXRecordDecl *rd) {
assert(!cir::MissingFeatures::hiddenVisibility());
if (!cgm.getCodeGenOpts().WholeProgramVTables)
return false;

if (cgm.getCodeGenOpts().VirtualFunctionElimination)
return true;

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

return false;
}

mlir::Value CIRGenFunction::getVTablePtr(mlir::Location loc, Address thisAddr,
const CXXRecordDecl *rd) {
auto vtablePtr = cir::VTableGetVPtrOp::create(
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,11 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::Value getVTablePtr(mlir::Location loc, Address thisAddr,
const clang::CXXRecordDecl *vtableClass);

/// Returns whether we should perform a type checked load when loading a
/// virtual function for virtual calls to members of RD. This is generally
/// true when both vcall CFI and whole-program-vtables are enabled.
bool shouldEmitVTableTypeCheckedLoad(const CXXRecordDecl *rd);

/// A scope within which we are constructing the fields of an object which
/// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use if
/// we need to evaluate the CXXDefaultInitExpr within the evaluation.
Expand Down
Loading
Loading