Skip to content

Commit 504714a

Browse files
authored
[CIR] Implement function alias lowering (#1739)
This updates the lowering of CIR function aliases in such a way that they now actually become aliases in the final LLVM IR.
1 parent b0901d3 commit 504714a

File tree

3 files changed

+88
-11
lines changed

3 files changed

+88
-11
lines changed

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,13 @@ mlir::Value CirAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
701701
sourceType = converter->convertType(fun.getFunctionType());
702702
symName = fun.getSymName();
703703
sourceAddrSpace = 0;
704+
} else if (auto alias = dyn_cast<mlir::LLVM::AliasOp>(sourceSymbol)) {
705+
// FIXME: It seems unusual that we need to handle an LLVM dialect op here,
706+
// but that's the way it happens. Should we be doing something somewhere
707+
// else to lower the GlobalViewAttr sooner?
708+
sourceType = alias.getType();
709+
symName = alias.getSymName();
710+
sourceAddrSpace = 0;
704711
} else {
705712
llvm_unreachable("Unexpected GlobalOp type");
706713
}
@@ -1434,13 +1441,46 @@ rewriteToCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
14341441
willReturn);
14351442

14361443
mlir::LLVM::LLVMFunctionType llvmFnTy;
1444+
1445+
// Temporary to handle the case where we need to prepend an operand if the
1446+
// callee is an alias.
1447+
SmallVector<mlir::Value> adjustedCallOperands;
1448+
14371449
if (calleeAttr) { // direct call
1438-
auto fn =
1439-
mlir::SymbolTable::lookupNearestSymbolFrom<mlir::FunctionOpInterface>(
1440-
op, calleeAttr);
1441-
assert(fn && "Did not find function for call");
1442-
llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
1443-
converter->convertType(fn.getFunctionType()));
1450+
mlir::Operation *callee =
1451+
mlir::SymbolTable::lookupNearestSymbolFrom(op, calleeAttr);
1452+
if (auto fn = dyn_cast<mlir::FunctionOpInterface>(callee)) {
1453+
llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
1454+
converter->convertType(fn.getFunctionType()));
1455+
} else if (auto alias = cast<mlir::LLVM::AliasOp>(callee)) {
1456+
// If the callee wasan alias. In that case,
1457+
// we need to prepend the address of the alias to the operands. The
1458+
// way aliases work in the LLVM dialect is a little counter-intuitive.
1459+
// The AliasOp itself is a pseudo-function that returns the address of
1460+
// the global value being aliased, but when we generate the call we
1461+
// need to insert an operation that gets the address of the AliasOp.
1462+
// This all gets sorted out when the LLVM dialect is lowered to LLVM IR.
1463+
auto symAttr = cast<mlir::FlatSymbolRefAttr>(calleeAttr);
1464+
auto addrOfAlias =
1465+
rewriter
1466+
.create<mlir::LLVM::AddressOfOp>(
1467+
op->getLoc(),
1468+
mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
1469+
symAttr)
1470+
.getResult();
1471+
adjustedCallOperands.push_back(addrOfAlias);
1472+
1473+
// Now add the regular operands and assign this to the range value.
1474+
llvm::append_range(adjustedCallOperands, callOperands);
1475+
callOperands = adjustedCallOperands;
1476+
1477+
// Clear the callee attribute because we're calling an alias.
1478+
calleeAttr = {};
1479+
llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(alias.getType());
1480+
} else {
1481+
// Was this an ifunc?
1482+
return op->emitError("Unexpected callee type!");
1483+
}
14441484
} else { // indirect call
14451485
assert(op->getOperands().size() &&
14461486
"operands list must no be empty for the indirect call");
@@ -1449,6 +1489,7 @@ rewriteToCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
14491489
auto ptyp = dyn_cast<cir::PointerType>(typ);
14501490
auto ftyp = dyn_cast<cir::FuncType>(ptyp.getPointee());
14511491
assert(ftyp && "expected a pointer to a function as the first operand");
1492+
llvm::append_range(adjustedCallOperands, callOperands);
14521493
llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(converter->convertType(ftyp));
14531494
}
14541495

@@ -2187,10 +2228,34 @@ void CIRToLLVMFuncOpLowering::lowerFuncOpenCLKernelMetadata(
21872228
newExtraAttrs.getDictionary(getContext())));
21882229
}
21892230

2231+
mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias(
2232+
cir::FuncOp op, mlir::FlatSymbolRefAttr aliasee, mlir::Type ty,
2233+
OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const {
2234+
SmallVector<mlir::NamedAttribute, 4> attributes;
2235+
lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
2236+
2237+
auto loc = op.getLoc();
2238+
auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>(
2239+
op, ty, convertLinkage(op.getLinkage()), op.getName(), op.getDsoLocal(),
2240+
/*threadLocal=*/false, attributes);
2241+
2242+
// Create the alias body
2243+
mlir::OpBuilder builder(op.getContext());
2244+
mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion());
2245+
builder.setInsertionPointToStart(block);
2246+
// The type of AddressOfOp is always a pointer.
2247+
assert(!cir::MissingFeatures::addressSpace());
2248+
mlir::Type ptrTy = mlir::LLVM::LLVMPointerType::get(ty.getContext());
2249+
auto addrOp =
2250+
builder.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, aliasee.getValue());
2251+
builder.create<mlir::LLVM::ReturnOp>(loc, addrOp);
2252+
2253+
return mlir::success();
2254+
}
2255+
21902256
mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
21912257
cir::FuncOp op, OpAdaptor adaptor,
21922258
mlir::ConversionPatternRewriter &rewriter) const {
2193-
21942259
auto fnType = op.getFunctionType();
21952260
auto isDsoLocal = op.getDsoLocal();
21962261
mlir::TypeConverter::SignatureConversion signatureConversion(
@@ -2211,6 +2276,12 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
22112276
resultType ? resultType : mlir::LLVM::LLVMVoidType::get(getContext()),
22122277
signatureConversion.getConvertedTypes(),
22132278
/*isVarArg=*/fnType.isVarArg());
2279+
2280+
// If this is an alias, it needs to be lowered to llvm::AliasOp.
2281+
std::optional<mlir::FlatSymbolRefAttr> aliasee = op.getAliaseeAttr();
2282+
if (aliasee && *aliasee)
2283+
return matchAndRewriteAlias(op, *aliasee, llvmFnTy, adaptor, rewriter);
2284+
22142285
// LLVMFuncOp expects a single FileLine Location instead of a fused
22152286
// location.
22162287
auto Loc = op.getLoc();

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@ class CIRToLLVMFuncOpLowering : public mlir::OpConversionPattern<cir::FuncOp> {
517517
void
518518
lowerFuncOpenCLKernelMetadata(mlir::NamedAttribute &extraAttrsEntry) const;
519519

520+
mlir::LogicalResult
521+
matchAndRewriteAlias(cir::FuncOp op, mlir::FlatSymbolRefAttr aliasee,
522+
mlir::Type ty, OpAdaptor adaptor,
523+
mlir::ConversionPatternRewriter &rewriter) const;
524+
520525
public:
521526
using mlir::OpConversionPattern<cir::FuncOp>::OpConversionPattern;
522527

clang/test/CIR/CodeGen/virtual-destructor-calls.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ struct B : A {
2323
virtual ~B();
2424
};
2525

26+
// Aliases are inserted before the function definitions in LLVM IR
27+
// FIXME: These should have unnamed_addr set.
28+
// LLVM: @_ZN1BD1Ev = alias void (ptr), ptr @_ZN1BD2Ev
29+
// LLVM: @_ZN1CD1Ev = alias void (ptr), ptr @_ZN1CD2Ev
30+
2631
// Base dtor: actually calls A's base dtor.
2732
// CIR: cir.func dso_local @_ZN1BD2Ev
2833
// CIR: cir.call @_ZN6MemberD1Ev
@@ -33,8 +38,6 @@ struct B : A {
3338

3439
// Complete dtor: just an alias because there are no virtual bases.
3540
// CIR: cir.func private dso_local @_ZN1BD1Ev(!cir.ptr<!rec_B>) alias(@_ZN1BD2Ev)
36-
// FIXME: LLVM output should be: @_ZN1BD1Ev ={{.*}} unnamed_addr alias {{.*}} @_ZN1BD2Ev
37-
// LLVM: declare dso_local void @_ZN1BD1Ev(ptr)
3841

3942
// Deleting dtor: defers to the complete dtor.
4043
// LLVM: define{{.*}} void @_ZN1BD0Ev(ptr
@@ -51,8 +54,6 @@ struct B : A {
5154

5255
// FIXME: LLVM output should be: @_ZN1CD2Ev ={{.*}} unnamed_addr alias {{.*}} @_ZN1BD2Ev
5356
// LLVM: define dso_local void @_ZN1CD2Ev(ptr
54-
// FIXME: LLVM output should be: @_ZN1CD1Ev ={{.*}} unnamed_addr alias {{.*}} @_ZN1CD2Ev
55-
// LLVM: declare dso_local void @_ZN1CD1Ev(ptr)
5657
// FIXME: note that LLVM_O1 cannot be tested because the canocalizers running
5758
// on top of LLVM IR dialect delete _ZN1CD2Ev in its current form (a function
5859
// declaration) since its not used in the TU.

0 commit comments

Comments
 (0)