| //===--- LowerFunction.cpp - Lower CIR Function Code ----------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file partially mimics clang/lib/CodeGen/CodeGenFunction.cpp. The queries |
| // are adapted to operate on the CIR dialect, however. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "LowerFunction.h" |
| #include "CIRToCIRArgMapping.h" |
| #include "LowerCall.h" |
| #include "LowerFunctionInfo.h" |
| #include "LowerModule.h" |
| #include "mlir/IR/MLIRContext.h" |
| #include "mlir/IR/PatternMatch.h" |
| #include "mlir/Support/LogicalResult.h" |
| #include "clang/CIR/ABIArgInfo.h" |
| #include "clang/CIR/Dialect/IR/CIRAttrs.h" |
| #include "clang/CIR/Dialect/IR/CIRDialect.h" |
| #include "clang/CIR/Dialect/IR/CIRTypes.h" |
| #include "clang/CIR/MissingFeatures.h" |
| #include "clang/CIR/TypeEvaluationKind.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| using ABIArgInfo = ::cir::ABIArgInfo; |
| |
| namespace mlir { |
| namespace cir { |
| |
| namespace { |
| |
| Value buildAddressAtOffset(LowerFunction &LF, Value addr, |
| const ABIArgInfo &info) { |
| if (unsigned offset = info.getDirectOffset()) { |
| llvm_unreachable("NYI"); |
| } |
| return addr; |
| } |
| |
| /// Given a struct pointer that we are accessing some number of bytes out of it, |
| /// try to gep into the struct to get at its inner goodness. Dive as deep as |
| /// possible without entering an element with an in-memory size smaller than |
| /// DstSize. |
| Value enterStructPointerForCoercedAccess(Value SrcPtr, StructType SrcSTy, |
| uint64_t DstSize, LowerFunction &CGF) { |
| // We can't dive into a zero-element struct. |
| if (SrcSTy.getNumElements() == 0) |
| llvm_unreachable("NYI"); |
| |
| Type FirstElt = SrcSTy.getMembers()[0]; |
| |
| // If the first elt is at least as large as what we're looking for, or if the |
| // first element is the same size as the whole struct, we can enter it. The |
| // comparison must be made on the store size and not the alloca size. Using |
| // the alloca size may overstate the size of the load. |
| uint64_t FirstEltSize = CGF.LM.getDataLayout().getTypeStoreSize(FirstElt); |
| if (FirstEltSize < DstSize && |
| FirstEltSize < CGF.LM.getDataLayout().getTypeStoreSize(SrcSTy)) |
| return SrcPtr; |
| |
| llvm_unreachable("NYI"); |
| } |
| |
| /// Create a store to \param Dst from \param Src where the source and |
| /// destination may have different types. |
| /// |
| /// This safely handles the case when the src type is larger than the |
| /// destination type; the upper bits of the src will be lost. |
| void createCoercedStore(Value Src, Value Dst, bool DstIsVolatile, |
| LowerFunction &CGF) { |
| Type SrcTy = Src.getType(); |
| Type DstTy = Dst.getType(); |
| if (SrcTy == DstTy) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // FIXME(cir): We need a better way to handle datalayout queries. |
| assert(isa<IntType>(SrcTy)); |
| llvm::TypeSize SrcSize = CGF.LM.getDataLayout().getTypeAllocSize(SrcTy); |
| |
| if (StructType DstSTy = dyn_cast<StructType>(DstTy)) { |
| Dst = enterStructPointerForCoercedAccess(Dst, DstSTy, |
| SrcSize.getFixedValue(), CGF); |
| assert(isa<PointerType>(Dst.getType())); |
| DstTy = cast<PointerType>(Dst.getType()).getPointee(); |
| } |
| |
| PointerType SrcPtrTy = dyn_cast<PointerType>(SrcTy); |
| PointerType DstPtrTy = dyn_cast<PointerType>(DstTy); |
| // TODO(cir): Implement address space. |
| if (SrcPtrTy && DstPtrTy && !::cir::MissingFeatures::addressSpace()) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // If the source and destination are integer or pointer types, just do an |
| // extension or truncation to the desired type. |
| if ((isa<IntegerType>(SrcTy) || isa<PointerType>(SrcTy)) && |
| (isa<IntegerType>(DstTy) || isa<PointerType>(DstTy))) { |
| llvm_unreachable("NYI"); |
| } |
| |
| llvm::TypeSize DstSize = CGF.LM.getDataLayout().getTypeAllocSize(DstTy); |
| |
| // If store is legal, just bitcast the src pointer. |
| assert(!::cir::MissingFeatures::vectorType()); |
| if (SrcSize.getFixedValue() <= DstSize.getFixedValue()) { |
| // Dst = Dst.withElementType(SrcTy); |
| CGF.buildAggregateStore(Src, Dst, DstIsVolatile); |
| } else { |
| llvm_unreachable("NYI"); |
| } |
| } |
| |
| // FIXME(cir): Create a custom rewriter class to abstract this away. |
| Value createBitcast(Value Src, Type Ty, LowerFunction &LF) { |
| return LF.getRewriter().create<CastOp>(Src.getLoc(), Ty, CastKind::bitcast, |
| Src); |
| } |
| |
| /// Coerces a \param Src value to a value of type \param Ty. |
| /// |
| /// This safely handles the case when the src type is smaller than the |
| /// destination type; in this situation the values of bits which not present in |
| /// the src are undefined. |
| /// |
| /// NOTE(cir): This method has partial parity with CGCall's CreateCoercedLoad. |
| /// Unlike the original codegen, this function does not emit a coerced load |
| /// since CIR's type checker wouldn't allow it. Instead, it casts the existing |
| /// ABI-agnostic value to it's ABI-aware counterpart. Nevertheless, we should |
| /// try to follow the same logic as the original codegen for correctness. |
| Value createCoercedValue(Value Src, Type Ty, LowerFunction &CGF) { |
| Type SrcTy = Src.getType(); |
| |
| // If SrcTy and Ty are the same, just reuse the exising load. |
| if (SrcTy == Ty) |
| return Src; |
| |
| // If it is the special boolean case, simply bitcast it. |
| if ((isa<BoolType>(SrcTy) && isa<IntType>(Ty)) || |
| (isa<IntType>(SrcTy) && isa<BoolType>(Ty))) |
| return createBitcast(Src, Ty, CGF); |
| |
| llvm::TypeSize DstSize = CGF.LM.getDataLayout().getTypeAllocSize(Ty); |
| |
| if (auto SrcSTy = dyn_cast<StructType>(SrcTy)) { |
| Src = enterStructPointerForCoercedAccess(Src, SrcSTy, |
| DstSize.getFixedValue(), CGF); |
| SrcTy = Src.getType(); |
| } |
| |
| llvm::TypeSize SrcSize = CGF.LM.getDataLayout().getTypeAllocSize(SrcTy); |
| |
| // If the source and destination are integer or pointer types, just do an |
| // extension or truncation to the desired type. |
| if ((isa<IntType>(Ty) || isa<PointerType>(Ty)) && |
| (isa<IntType>(SrcTy) || isa<PointerType>(SrcTy))) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // If load is legal, just bitcast the src pointer. |
| if (!SrcSize.isScalable() && !DstSize.isScalable() && |
| SrcSize.getFixedValue() >= DstSize.getFixedValue()) { |
| // Generally SrcSize is never greater than DstSize, since this means we are |
| // losing bits. However, this can happen in cases where the structure has |
| // additional padding, for example due to a user specified alignment. |
| // |
| // FIXME: Assert that we aren't truncating non-padding bits when have access |
| // to that information. |
| // Src = Src.withElementType(); |
| return CGF.buildAggregateBitcast(Src, Ty); |
| } |
| |
| llvm_unreachable("NYI"); |
| } |
| |
| Value emitAddressAtOffset(LowerFunction &LF, Value addr, |
| const ABIArgInfo &info) { |
| if (unsigned offset = info.getDirectOffset()) { |
| llvm_unreachable("NYI"); |
| } |
| return addr; |
| } |
| |
| /// After the calling convention is lowered, an ABI-agnostic type might have to |
| /// be loaded back to its ABI-aware couterpart so it may be returned. If they |
| /// differ, we have to do a coerced load. A coerced load, which means to load a |
| /// type to another despite that they represent the same value. The simplest |
| /// cases can be solved with a mere bitcast. |
| /// |
| /// This partially replaces CreateCoercedLoad from the original codegen. |
| /// However, instead of emitting the load, it emits a cast. |
| /// |
| /// FIXME(cir): Improve parity with the original codegen. |
| Value castReturnValue(Value Src, Type Ty, LowerFunction &LF) { |
| Type SrcTy = Src.getType(); |
| |
| // If SrcTy and Ty are the same, nothing to do. |
| if (SrcTy == Ty) |
| return Src; |
| |
| // If is the special boolean case, simply bitcast it. |
| if (isa<BoolType>(SrcTy) && isa<IntType>(Ty)) |
| return createBitcast(Src, Ty, LF); |
| |
| llvm::TypeSize DstSize = LF.LM.getDataLayout().getTypeAllocSize(Ty); |
| |
| // FIXME(cir): Do we need the EnterStructPointerForCoercedAccess routine here? |
| |
| llvm::TypeSize SrcSize = LF.LM.getDataLayout().getTypeAllocSize(SrcTy); |
| |
| if ((isa<IntType>(Ty) || isa<PointerType>(Ty)) && |
| (isa<IntType>(SrcTy) || isa<PointerType>(SrcTy))) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // If load is legal, just bitcast the src pointer. |
| if (!SrcSize.isScalable() && !DstSize.isScalable() && |
| SrcSize.getFixedValue() >= DstSize.getFixedValue()) { |
| // Generally SrcSize is never greater than DstSize, since this means we are |
| // losing bits. However, this can happen in cases where the structure has |
| // additional padding, for example due to a user specified alignment. |
| // |
| // FIXME: Assert that we aren't truncating non-padding bits when have access |
| // to that information. |
| return LF.getRewriter().create<CastOp>(Src.getLoc(), Ty, CastKind::bitcast, |
| Src); |
| } |
| |
| llvm_unreachable("NYI"); |
| } |
| |
| } // namespace |
| |
| // FIXME(cir): Pass SrcFn and NewFn around instead of having then as attributes. |
| LowerFunction::LowerFunction(LowerModule &LM, PatternRewriter &rewriter, |
| FuncOp srcFn, FuncOp newFn) |
| : Target(LM.getTarget()), rewriter(rewriter), SrcFn(srcFn), NewFn(newFn), |
| LM(LM) {} |
| |
| LowerFunction::LowerFunction(LowerModule &LM, PatternRewriter &rewriter, |
| FuncOp srcFn, CallOp callOp) |
| : Target(LM.getTarget()), rewriter(rewriter), SrcFn(srcFn), callOp(callOp), |
| LM(LM) {} |
| |
| /// This method has partial parity with CodeGenFunction::EmitFunctionProlog from |
| /// the original codegen. However, it focuses on the ABI-specific details. On |
| /// top of that, it is also responsible for rewriting the original function. |
| LogicalResult |
| LowerFunction::buildFunctionProlog(const LowerFunctionInfo &FI, FuncOp Fn, |
| MutableArrayRef<BlockArgument> Args) { |
| // NOTE(cir): Skipping naked and implicit-return-zero functions here. These |
| // are dealt with in CIRGen. |
| |
| CIRToCIRArgMapping IRFunctionArgs(LM.getContext(), FI); |
| assert(Fn.getNumArguments() == IRFunctionArgs.totalIRArgs()); |
| |
| // If we're using inalloca, all the memory arguments are GEPs off of the last |
| // parameter, which is a pointer to the complete memory area. |
| assert(!::cir::MissingFeatures::inallocaArgs()); |
| |
| // Name the struct return parameter. |
| assert(!::cir::MissingFeatures::sretArgs()); |
| |
| // Track if we received the parameter as a pointer (indirect, byval, or |
| // inalloca). If already have a pointer, EmitParmDecl doesn't need to copy it |
| // into a local alloca for us. |
| SmallVector<Value, 8> ArgVals; |
| ArgVals.reserve(Args.size()); |
| |
| // Create a pointer value for every parameter declaration. This usually |
| // entails copying one or more LLVM IR arguments into an alloca. Don't push |
| // any cleanups or do anything that might unwind. We do that separately, so |
| // we can push the cleanups in the correct order for the ABI. |
| assert(FI.arg_size() == Args.size()); |
| unsigned ArgNo = 0; |
| LowerFunctionInfo::const_arg_iterator info_it = FI.arg_begin(); |
| for (MutableArrayRef<BlockArgument>::const_iterator i = Args.begin(), |
| e = Args.end(); |
| i != e; ++i, ++info_it, ++ArgNo) { |
| const Value Arg = *i; |
| const ABIArgInfo &ArgI = info_it->info; |
| |
| bool isPromoted = ::cir::MissingFeatures::varDeclIsKNRPromoted(); |
| // We are converting from ABIArgInfo type to VarDecl type directly, unless |
| // the parameter is promoted. In this case we convert to |
| // CGFunctionInfo::ArgInfo type with subsequent argument demotion. |
| Type Ty = {}; |
| if (isPromoted) |
| llvm_unreachable("NYI"); |
| else |
| Ty = Arg.getType(); |
| assert(!::cir::MissingFeatures::evaluationKind()); |
| |
| unsigned FirstIRArg, NumIRArgs; |
| std::tie(FirstIRArg, NumIRArgs) = IRFunctionArgs.getIRArgs(ArgNo); |
| |
| switch (ArgI.getKind()) { |
| case ABIArgInfo::Extend: |
| case ABIArgInfo::Direct: { |
| auto AI = Fn.getArgument(FirstIRArg); |
| Type LTy = Arg.getType(); |
| |
| // Prepare parameter attributes. So far, only attributes for pointer |
| // parameters are prepared. See |
| // http://llvm.org/docs/LangRef.html#paramattrs. |
| if (ArgI.getDirectOffset() == 0 && isa<PointerType>(LTy) && |
| isa<PointerType>(ArgI.getCoerceToType())) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // Prepare the argument value. If we have the trivial case, handle it |
| // with no muss and fuss. |
| if (!isa<StructType>(ArgI.getCoerceToType()) && |
| ArgI.getCoerceToType() == Ty && ArgI.getDirectOffset() == 0) { |
| assert(NumIRArgs == 1); |
| |
| // LLVM expects swifterror parameters to be used in very restricted |
| // ways. Copy the value into a less-restricted temporary. |
| Value V = AI; |
| if (::cir::MissingFeatures::extParamInfo()) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // Ensure the argument is the correct type. |
| if (V.getType() != ArgI.getCoerceToType()) |
| llvm_unreachable("NYI"); |
| |
| if (isPromoted) |
| llvm_unreachable("NYI"); |
| |
| ArgVals.push_back(V); |
| |
| // NOTE(cir): Here we have a trivial case, which means we can just |
| // replace all uses of the original argument with the new one. |
| Value oldArg = SrcFn.getArgument(ArgNo); |
| Value newArg = Fn.getArgument(FirstIRArg); |
| rewriter.replaceAllUsesWith(oldArg, newArg); |
| |
| break; |
| } |
| |
| assert(!::cir::MissingFeatures::vectorType()); |
| |
| // Allocate original argument to be "uncoerced". |
| // FIXME(cir): We should have a alloca op builder that does not required |
| // the pointer type to be explicitly passed. |
| // FIXME(cir): Get the original name of the argument, as well as the |
| // proper alignment for the given type being allocated. |
| auto Alloca = rewriter.create<AllocaOp>( |
| Fn.getLoc(), rewriter.getType<PointerType>(Ty), Ty, |
| /*name=*/StringRef(""), |
| /*alignment=*/rewriter.getI64IntegerAttr(4)); |
| |
| Value Ptr = buildAddressAtOffset(*this, Alloca.getResult(), ArgI); |
| |
| // Fast-isel and the optimizer generally like scalar values better than |
| // FCAs, so we flatten them if this is safe to do for this argument. |
| StructType STy = dyn_cast<StructType>(ArgI.getCoerceToType()); |
| if (ArgI.isDirect() && ArgI.getCanBeFlattened() && STy && |
| STy.getNumElements() > 1) { |
| llvm_unreachable("NYI"); |
| } else { |
| // Simple case, just do a coerced store of the argument into the alloca. |
| assert(NumIRArgs == 1); |
| Value AI = Fn.getArgument(FirstIRArg); |
| // TODO(cir): Set argument name in the new function. |
| createCoercedStore(AI, Ptr, /*DstIsVolatile=*/false, *this); |
| } |
| |
| // Match to what EmitParamDecl is expecting for this type. |
| if (::cir::MissingFeatures::evaluationKind()) { |
| llvm_unreachable("NYI"); |
| } else { |
| // FIXME(cir): Should we have an ParamValue abstraction like in the |
| // original codegen? |
| ArgVals.push_back(Alloca); |
| } |
| |
| // NOTE(cir): Once we have uncoerced the argument, we should be able to |
| // RAUW the original argument alloca with the new one. This assumes that |
| // the argument is used only to be stored in a alloca. |
| Value arg = SrcFn.getArgument(ArgNo); |
| assert(arg.hasOneUse()); |
| for (auto *firstStore : arg.getUsers()) { |
| assert(isa<StoreOp>(firstStore)); |
| auto argAlloca = cast<StoreOp>(firstStore).getAddr(); |
| rewriter.replaceAllUsesWith(argAlloca, Alloca); |
| rewriter.eraseOp(firstStore); |
| rewriter.eraseOp(argAlloca.getDefiningOp()); |
| } |
| |
| break; |
| } |
| default: |
| llvm_unreachable("Unhandled ABIArgInfo::Kind"); |
| } |
| } |
| |
| if (getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) { |
| llvm_unreachable("NYI"); |
| } else { |
| // FIXME(cir): In the original codegen, EmitParamDecl is called here. It |
| // is likely that said function considers ABI details during emission, so |
| // we migth have to add a counter part here. Currently, it is not needed. |
| } |
| |
| return success(); |
| } |
| |
| LogicalResult LowerFunction::buildFunctionEpilog(const LowerFunctionInfo &FI) { |
| // NOTE(cir): no-return, naked, and no result functions should be handled in |
| // CIRGen. |
| |
| Value RV = {}; |
| Type RetTy = FI.getReturnType(); |
| const ABIArgInfo &RetAI = FI.getReturnInfo(); |
| |
| switch (RetAI.getKind()) { |
| |
| case ABIArgInfo::Ignore: |
| break; |
| |
| case ABIArgInfo::Extend: |
| case ABIArgInfo::Direct: |
| // FIXME(cir): Should we call ConvertType(RetTy) here? |
| if (RetAI.getCoerceToType() == RetTy && RetAI.getDirectOffset() == 0) { |
| // The internal return value temp always will have pointer-to-return-type |
| // type, just do a load. |
| |
| // If there is a dominating store to ReturnValue, we can elide |
| // the load, zap the store, and usually zap the alloca. |
| // NOTE(cir): This seems like a premature optimization case. Skipping it. |
| if (::cir::MissingFeatures::returnValueDominatingStoreOptmiization()) { |
| llvm_unreachable("NYI"); |
| } |
| // Otherwise, we have to do a simple load. |
| else { |
| // NOTE(cir): Nothing to do here. The codegen already emitted this load |
| // for us and there is no casting necessary to conform to the ABI. The |
| // zero-extension is enforced by the return value's attribute. Just |
| // early exit. |
| return success(); |
| } |
| } else { |
| // NOTE(cir): Unlike the original codegen, CIR may have multiple return |
| // statements in the function body. We have to handle this here. |
| mlir::PatternRewriter::InsertionGuard guard(rewriter); |
| NewFn->walk([&](ReturnOp returnOp) { |
| rewriter.setInsertionPoint(returnOp); |
| RV = castReturnValue(returnOp->getOperand(0), RetAI.getCoerceToType(), |
| *this); |
| rewriter.replaceOpWithNewOp<ReturnOp>(returnOp, RV); |
| }); |
| } |
| |
| // TODO(cir): Should AutoreleaseResult be handled here? |
| break; |
| |
| default: |
| llvm_unreachable("Unhandled ABIArgInfo::Kind"); |
| } |
| |
| return success(); |
| } |
| |
| /// Generate code for a function based on the ABI-specific information. |
| /// |
| /// This method has partial parity with CodeGenFunction::GenerateCode, but it |
| /// focuses on the ABI-specific details. So a lot of codegen stuff is removed. |
| LogicalResult LowerFunction::generateCode(FuncOp oldFn, FuncOp newFn, |
| const LowerFunctionInfo &FnInfo) { |
| assert(newFn && "generating code for null Function"); |
| auto Args = oldFn.getArguments(); |
| |
| // Emit the ABI-specific function prologue. |
| assert(newFn.empty() && "Function already has a body"); |
| rewriter.setInsertionPointToEnd(newFn.addEntryBlock()); |
| if (buildFunctionProlog(FnInfo, newFn, oldFn.getArguments()).failed()) |
| return failure(); |
| |
| // Ensure that old ABI-agnostic arguments uses were replaced. |
| const auto hasNoUses = [](Value val) { return val.getUses().empty(); }; |
| assert(std::all_of(Args.begin(), Args.end(), hasNoUses) && "Missing RAUW?"); |
| |
| // Migrate function body to new ABI-aware function. |
| assert(oldFn.getBody().hasOneBlock() && |
| "Multiple blocks in original function not supported"); |
| |
| // Move old function body to new function. |
| // FIXME(cir): The merge below is not very good: will not work if SrcFn has |
| // multiple blocks and it mixes the new and old prologues. |
| rewriter.mergeBlocks(&oldFn.getBody().front(), &newFn.getBody().front(), |
| newFn.getArguments()); |
| |
| // FIXME(cir): What about saving parameters for corotines? Should we do |
| // something about it in this pass? If the change with the calling |
| // convention, we might have to handle this here. |
| |
| // Emit the standard function epilogue. |
| if (buildFunctionEpilog(FnInfo).failed()) |
| return failure(); |
| |
| return success(); |
| } |
| |
| void LowerFunction::buildAggregateStore(Value Val, Value Dest, |
| bool DestIsVolatile) { |
| // In LLVM codegen: |
| // Function to store a first-class aggregate into memory. We prefer to |
| // store the elements rather than the aggregate to be more friendly to |
| // fast-isel. |
| assert(mlir::isa<PointerType>(Dest.getType()) && "Storing in a non-pointer!"); |
| (void)DestIsVolatile; |
| |
| // Circumvent CIR's type checking. |
| Type pointeeTy = mlir::cast<PointerType>(Dest.getType()).getPointee(); |
| if (Val.getType() != pointeeTy) { |
| // NOTE(cir): We only bitcast and store if the types have the same size. |
| assert((LM.getDataLayout().getTypeSizeInBits(Val.getType()) == |
| LM.getDataLayout().getTypeSizeInBits(pointeeTy)) && |
| "Incompatible types"); |
| auto loc = Val.getLoc(); |
| Val = rewriter.create<CastOp>(loc, pointeeTy, CastKind::bitcast, Val); |
| } |
| |
| rewriter.create<StoreOp>(Val.getLoc(), Val, Dest); |
| } |
| |
| Value LowerFunction::buildAggregateBitcast(Value Val, Type DestTy) { |
| return rewriter.create<CastOp>(Val.getLoc(), DestTy, CastKind::bitcast, Val); |
| } |
| |
| /// Rewrite a call operation to abide to the ABI calling convention. |
| /// |
| /// FIXME(cir): This method has partial parity to CodeGenFunction's |
| /// EmitCallEpxr method defined in CGExpr.cpp. This could likely be |
| /// removed in favor of a more direct approach. |
| LogicalResult LowerFunction::rewriteCallOp(CallOp op, |
| ReturnValueSlot retValSlot) { |
| |
| // TODO(cir): Check if BlockCall, CXXMemberCall, CUDAKernelCall, or |
| // CXXOperatorMember require special handling here. These should be handled |
| // in CIRGen, unless there is call conv or ABI-specific stuff to be handled, |
| // them we should do it here. |
| |
| // TODO(cir): Also check if Builtin and CXXPeseudoDtor need special handling |
| // here. These should be handled in CIRGen, unless there is call conv or |
| // ABI-specific stuff to be handled, them we should do it here. |
| |
| // NOTE(cir): There is no direct way to fetch the function type from the |
| // CallOp, so we fetch it from the source function. This assumes the |
| // function definition has not yet been lowered. |
| assert(SrcFn && "No source function"); |
| auto fnType = SrcFn.getFunctionType(); |
| |
| // Rewrite the call operation to abide to the ABI calling convention. |
| auto Ret = rewriteCallOp(fnType, SrcFn, op, retValSlot); |
| |
| // Replace the original call result with the new one. |
| if (Ret) |
| rewriter.replaceAllUsesWith(op.getResult(), Ret); |
| |
| // Erase original ABI-agnostic call. |
| rewriter.eraseOp(op); |
| return success(); |
| } |
| |
| /// Rewrite a call operation to abide to the ABI calling convention. |
| /// |
| /// FIXME(cir): This method has partial parity to CodeGenFunction's EmitCall |
| /// method defined in CGExpr.cpp. This could likely be removed in favor of a |
| /// more direct approach since most of the code here is exclusively CodeGen. |
| Value LowerFunction::rewriteCallOp(FuncType calleeTy, FuncOp origCallee, |
| CallOp callOp, ReturnValueSlot retValSlot, |
| Value Chain) { |
| // NOTE(cir): Skip a bunch of function pointer stuff and AST declaration |
| // asserts. Also skip sanitizers, as these should likely be handled at |
| // CIRGen. |
| CallArgList Args; |
| if (Chain) |
| llvm_unreachable("NYI"); |
| |
| // NOTE(cir): Call args were already emitted in CIRGen. Skip the evaluation |
| // order done in CIRGen and just fetch the exiting arguments here. |
| Args = callOp.getArgOperands(); |
| |
| const LowerFunctionInfo &FnInfo = LM.getTypes().arrangeFreeFunctionCall( |
| callOp.getArgOperands(), calleeTy, /*chainCall=*/false); |
| |
| // C99 6.5.2.2p6: |
| // If the expression that denotes the called function has a type |
| // that does not include a prototype, [the default argument |
| // promotions are performed]. If the number of arguments does not |
| // equal the number of parameters, the behavior is undefined. If |
| // the function is defined with a type that includes a prototype, |
| // and either the prototype ends with an ellipsis (, ...) or the |
| // types of the arguments after promotion are not compatible with |
| // the types of the parameters, the behavior is undefined. If the |
| // function is defined with a type that does not include a |
| // prototype, and the types of the arguments after promotion are |
| // not compatible with those of the parameters after promotion, |
| // the behavior is undefined [except in some trivial cases]. |
| // That is, in the general case, we should assume that a call |
| // through an unprototyped function type works like a *non-variadic* |
| // call. The way we make this work is to cast to the exact type |
| // of the promoted arguments. |
| // |
| // Chain calls use this same code path to add the invisible chain parameter |
| // to the function type. |
| if (origCallee.getNoProto() || Chain) { |
| llvm_unreachable("NYI"); |
| } |
| |
| assert(!::cir::MissingFeatures::CUDA()); |
| |
| // TODO(cir): LLVM IR has the concept of "CallBase", which is a base class |
| // for all types of calls. Perhaps we should have a CIR interface to mimic |
| // this class. |
| CallOp CallOrInvoke = {}; |
| Value CallResult = |
| rewriteCallOp(FnInfo, origCallee, callOp, retValSlot, Args, CallOrInvoke, |
| /*isMustTail=*/false, callOp.getLoc()); |
| |
| // NOTE(cir): Skipping debug stuff here. |
| |
| return CallResult; |
| } |
| |
| // NOTE(cir): This method has partial parity to CodeGenFunction's EmitCall |
| // method in CGCall.cpp. When incrementing it, use the original codegen as a |
| // reference: add ABI-specific stuff and skip codegen stuff. |
| Value LowerFunction::rewriteCallOp(const LowerFunctionInfo &CallInfo, |
| FuncOp Callee, CallOp Caller, |
| ReturnValueSlot ReturnValue, |
| CallArgList &CallArgs, CallOp CallOrInvoke, |
| bool isMustTail, Location loc) { |
| // FIXME: We no longer need the types from CallArgs; lift up and simplify. |
| |
| // Handle struct-return functions by passing a pointer to the |
| // location that we would like to return into. |
| Type RetTy = CallInfo.getReturnType(); // ABI-agnostic type. |
| const ::cir::ABIArgInfo &RetAI = CallInfo.getReturnInfo(); |
| |
| FuncType IRFuncTy = LM.getTypes().getFunctionType(CallInfo); |
| |
| // NOTE(cir): Some target/ABI related checks happen here. They are skipped |
| // under the assumption that they are handled in CIRGen. |
| |
| // 1. Set up the arguments. |
| |
| // If we're using inalloca, insert the allocation after the stack save. |
| // FIXME: Do this earlier rather than hacking it in here! |
| if (StructType ArgStruct = CallInfo.getArgStruct()) { |
| llvm_unreachable("NYI"); |
| } |
| |
| CIRToCIRArgMapping IRFunctionArgs(LM.getContext(), CallInfo); |
| SmallVector<Value, 16> IRCallArgs(IRFunctionArgs.totalIRArgs()); |
| |
| // If the call returns a temporary with struct return, create a temporary |
| // alloca to hold the result, unless one is given to us. |
| if (RetAI.isIndirect() || RetAI.isCoerceAndExpand() || RetAI.isInAlloca()) { |
| llvm_unreachable("NYI"); |
| } |
| |
| assert(!::cir::MissingFeatures::swift()); |
| |
| // NOTE(cir): Skipping lifetime markers here. |
| |
| // Translate all of the arguments as necessary to match the IR lowering. |
| assert(CallInfo.arg_size() == CallArgs.size() && |
| "Mismatch between function signature & arguments."); |
| unsigned ArgNo = 0; |
| LowerFunctionInfo::const_arg_iterator info_it = CallInfo.arg_begin(); |
| for (auto I = CallArgs.begin(), E = CallArgs.end(); I != E; |
| ++I, ++info_it, ++ArgNo) { |
| const ABIArgInfo &ArgInfo = info_it->info; |
| |
| if (IRFunctionArgs.hasPaddingArg(ArgNo)) |
| llvm_unreachable("NYI"); |
| |
| unsigned FirstIRArg, NumIRArgs; |
| std::tie(FirstIRArg, NumIRArgs) = IRFunctionArgs.getIRArgs(ArgNo); |
| |
| switch (ArgInfo.getKind()) { |
| case ABIArgInfo::Extend: |
| case ABIArgInfo::Direct: { |
| |
| if (isa<BoolType>(info_it->type)) { |
| IRCallArgs[FirstIRArg] = *I; |
| break; |
| } |
| |
| if (!isa<StructType>(ArgInfo.getCoerceToType()) && |
| ArgInfo.getCoerceToType() == info_it->type && |
| ArgInfo.getDirectOffset() == 0) { |
| assert(NumIRArgs == 1); |
| Value V; |
| if (!isa<StructType>(I->getType())) { |
| V = *I; |
| } else { |
| llvm_unreachable("NYI"); |
| } |
| |
| if (::cir::MissingFeatures::extParamInfo()) { |
| llvm_unreachable("NYI"); |
| } |
| |
| if (ArgInfo.getCoerceToType() != V.getType() && |
| isa<IntType>(V.getType())) |
| llvm_unreachable("NYI"); |
| |
| if (FirstIRArg < IRFuncTy.getNumInputs() && |
| V.getType() != IRFuncTy.getInput(FirstIRArg)) |
| llvm_unreachable("NYI"); |
| |
| if (::cir::MissingFeatures::undef()) |
| llvm_unreachable("NYI"); |
| IRCallArgs[FirstIRArg] = V; |
| break; |
| } |
| |
| // FIXME: Avoid the conversion through memory if possible. |
| Value Src = {}; |
| if (!isa<StructType>(I->getType())) { |
| llvm_unreachable("NYI"); |
| } else { |
| // NOTE(cir): L/RValue stuff are left for CIRGen to handle. |
| Src = *I; |
| } |
| |
| // If the value is offst in memory, apply the offset now. |
| // FIXME(cir): Is this offset already handled in CIRGen? |
| Src = emitAddressAtOffset(*this, Src, ArgInfo); |
| |
| // Fast-isel and the optimizer generally like scalar values better than |
| // FCAs, so we flatten them if this is safe to do for this argument. |
| StructType STy = dyn_cast<StructType>(ArgInfo.getCoerceToType()); |
| if (STy && ArgInfo.isDirect() && ArgInfo.getCanBeFlattened()) { |
| llvm_unreachable("NYI"); |
| } else { |
| // In the simple case, just pass the coerced loaded value. |
| assert(NumIRArgs == 1); |
| Value Load = createCoercedValue(Src, ArgInfo.getCoerceToType(), *this); |
| |
| // FIXME(cir): We should probably handle CMSE non-secure calls here |
| assert(!::cir::MissingFeatures::cmseNonSecureCallAttr()); |
| |
| // since they are a ARM-specific feature. |
| if (::cir::MissingFeatures::undef()) |
| llvm_unreachable("NYI"); |
| IRCallArgs[FirstIRArg] = Load; |
| } |
| |
| break; |
| } |
| default: |
| llvm::outs() << "Missing ABIArgInfo::Kind: " << ArgInfo.getKind() << "\n"; |
| llvm_unreachable("NYI"); |
| } |
| } |
| |
| // 2. Prepare the function pointer. |
| // NOTE(cir): This is not needed for CIR. |
| |
| // 3. Perform the actual call. |
| |
| // NOTE(cir): CIRGen handle when to "deactive" cleanups. We also skip some |
| // debugging stuff here. |
| |
| // Update the largest vector width if any arguments have vector types. |
| assert(!::cir::MissingFeatures::vectorType()); |
| |
| // Compute the calling convention and attributes. |
| |
| // FIXME(cir): Skipping call attributes for now. Not sure if we have to do |
| // this at all since we already do it for the function definition. |
| |
| // FIXME(cir): Implement the required procedures for strictfp function and |
| // fast-math. |
| |
| // FIXME(cir): Add missing call-site attributes here if they are |
| // ABI/target-specific, otherwise, do it in CIRGen. |
| |
| // NOTE(cir): Deciding whether to use Call or Invoke is done in CIRGen. |
| |
| // Rewrite the actual call operation. |
| // TODO(cir): Handle other types of CIR calls (e.g. cir.try_call). |
| // NOTE(cir): We don't know if the callee was already lowered, so we only |
| // fetch the name from the callee, while the return type is fetch from the |
| // lowering types manager. |
| CallOp newCallOp = rewriter.create<CallOp>( |
| loc, Caller.getCalleeAttr(), IRFuncTy.getReturnType(), IRCallArgs); |
| auto extraAttrs = |
| rewriter.getAttr<ExtraFuncAttributesAttr>(rewriter.getDictionaryAttr({})); |
| newCallOp->setAttr("extra_attrs", extraAttrs); |
| |
| assert(!::cir::MissingFeatures::vectorType()); |
| |
| // NOTE(cir): Skipping some ObjC, tail-call, debug, and attribute stuff |
| // here. |
| |
| // 4. Finish the call. |
| |
| // NOTE(cir): Skipping no-return, isMustTail, swift error handling, and |
| // writebacks here. These should be handled in CIRGen, I think. |
| |
| // Convert return value from ABI-agnostic to ABI-aware. |
| Value Ret = [&] { |
| // NOTE(cir): CIRGen already handled the emission of the return value. We |
| // need only to handle the ABI-specific to ABI-agnostic cast here. |
| switch (RetAI.getKind()) { |
| |
| case ::cir::ABIArgInfo::Ignore: |
| // If we are ignoring an argument that had a result, make sure to |
| // construct the appropriate return value for our caller. |
| return getUndefRValue(RetTy); |
| |
| case ABIArgInfo::Extend: |
| case ABIArgInfo::Direct: { |
| Type RetIRTy = RetTy; |
| if (RetAI.getCoerceToType() == RetIRTy && RetAI.getDirectOffset() == 0) { |
| switch (getEvaluationKind(RetTy)) { |
| case ::cir::TypeEvaluationKind::TEK_Scalar: { |
| // If the argument doesn't match, perform a bitcast to coerce it. |
| // This can happen due to trivial type mismatches. NOTE(cir): |
| // Perhaps this section should handle CIR's boolean case. |
| Value V = newCallOp.getResult(); |
| if (V.getType() != RetIRTy) |
| llvm_unreachable("NYI"); |
| return V; |
| } |
| default: |
| llvm_unreachable("NYI"); |
| } |
| } |
| |
| // If coercing a fixed vector from a scalable vector for ABI |
| // compatibility, and the types match, use the llvm.vector.extract |
| // intrinsic to perform the conversion. |
| if (::cir::MissingFeatures::vectorType()) { |
| llvm_unreachable("NYI"); |
| } |
| |
| // FIXME(cir): Use return value slot here. |
| Value RetVal = callOp.getResult(); |
| // TODO(cir): Check for volatile return values. |
| assert(!::cir::MissingFeatures::volatileTypes()); |
| |
| // NOTE(cir): If the function returns, there should always be a valid |
| // return value present. Instead of setting the return value here, we |
| // should have the ReturnValueSlot object set it beforehand. |
| if (!RetVal) { |
| RetVal = callOp.getResult(); |
| // TODO(cir): Check for volatile return values. |
| assert(::cir::MissingFeatures::volatileTypes()); |
| } |
| |
| // An empty record can overlap other data (if declared with |
| // no_unique_address); omit the store for such types - as there is no |
| // actual data to store. |
| if (dyn_cast<StructType>(RetTy) && |
| cast<StructType>(RetTy).getNumElements() != 0) { |
| RetVal = |
| createCoercedValue(newCallOp.getResult(), RetVal.getType(), *this); |
| } |
| |
| // NOTE(cir): No need to convert from a temp to an RValue. This is |
| // done in CIRGen |
| return RetVal; |
| } |
| default: |
| llvm::errs() << "Unhandled ABIArgInfo kind: " << RetAI.getKind() << "\n"; |
| llvm_unreachable("NYI"); |
| } |
| }(); |
| |
| // NOTE(cir): Skipping Emissions, lifetime markers, and dtors here that |
| // should be handled in CIRGen. |
| |
| return Ret; |
| } |
| |
| // NOTE(cir): This method has partial parity to CodeGenFunction's |
| // GetUndefRValue defined in CGExpr.cpp. |
| Value LowerFunction::getUndefRValue(Type Ty) { |
| if (isa<VoidType>(Ty)) |
| return nullptr; |
| |
| llvm::outs() << "Missing undef handler for value type: " << Ty << "\n"; |
| llvm_unreachable("NYI"); |
| } |
| |
| ::cir::TypeEvaluationKind LowerFunction::getEvaluationKind(Type type) { |
| // FIXME(cir): Implement type classes for CIR types. |
| if (isa<StructType>(type)) |
| return ::cir::TypeEvaluationKind::TEK_Aggregate; |
| if (isa<BoolType, IntType, SingleType, DoubleType>(type)) |
| return ::cir::TypeEvaluationKind::TEK_Scalar; |
| llvm_unreachable("NYI"); |
| } |
| |
| } // namespace cir |
| } // namespace mlir |