diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -2485,13 +2485,11 @@ A ``"clang.arc.attachedcall"`` operand bundle on a call indicates the call is implicitly followed by a marker instruction and a call to an ObjC runtime -function that uses the result of the call. The operand bundle takes either the +function that uses the result of the call. The operand bundle takes a mandatory pointer to the runtime function (``@objc_retainAutoreleasedReturnValue`` or -``@objc_unsafeClaimAutoreleasedReturnValue``) or no arguments. If the bundle -doesn't take any arguments, only the marker instruction has to be emitted after -the call; the runtime function calls don't have to be emitted since they already -have been emitted. The return value of a call with this bundle is used by a call -to ``@llvm.objc.clang.arc.noop.use`` unless the called function's return type is +``@objc_unsafeClaimAutoreleasedReturnValue``). +The return value of a call with this bundle is used by a call to +``@llvm.objc.clang.arc.noop.use`` unless the called function's return type is void, in which case the operand bundle is ignored. .. code-block:: llvm @@ -2501,11 +2499,8 @@ call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_unsafeClaimAutoreleasedReturnValue) ] - ; Only the marker instruction is inserted after the call to @foo. - call i8* @foo() [ "clang.arc.attachedcall"() ] - The operand bundle is needed to ensure the call is immediately followed by the -marker instruction or the ObjC runtime call in the final output. +marker instruction and the ObjC runtime call in the final output. .. _moduleasm: diff --git a/llvm/include/llvm/Analysis/ObjCARCUtil.h b/llvm/include/llvm/Analysis/ObjCARCUtil.h --- a/llvm/include/llvm/Analysis/ObjCARCUtil.h +++ b/llvm/include/llvm/Analysis/ObjCARCUtil.h @@ -42,7 +42,7 @@ /// which is the address of the ARC runtime function. inline Optional getAttachedARCFunction(const CallBase *CB) { auto B = CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall); - if (!B.hasValue() || B->Inputs.size() == 0) + if (!B) return None; return cast(B->Inputs[0]); diff --git a/llvm/include/llvm/IR/AutoUpgrade.h b/llvm/include/llvm/IR/AutoUpgrade.h --- a/llvm/include/llvm/IR/AutoUpgrade.h +++ b/llvm/include/llvm/IR/AutoUpgrade.h @@ -14,19 +14,23 @@ #define LLVM_IR_AUTOUPGRADE_H #include "llvm/ADT/StringRef.h" +#include namespace llvm { class AttrBuilder; class CallInst; class Constant; class Function; + class GlobalVariable; class Instruction; class MDNode; class Module; - class GlobalVariable; class Type; class Value; + template class OperandBundleDefT; + using OperandBundleDef = OperandBundleDefT; + /// This is a more granular function that simply checks an intrinsic function /// for upgrading, and returns true if it requires upgrading. It may return /// null in NewFn if the all calls to the original intrinsic function @@ -98,6 +102,9 @@ /// Upgrade attributes that changed format or kind. void UpgradeAttributes(AttrBuilder &B); + /// Upgrade operand bundles (without knowing about their user instruction). + void UpgradeOperandBundles(std::vector &OperandBundles); + } // End llvm namespace #endif diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -4786,6 +4786,10 @@ } } + // Upgrade the bundles if needed. + if (!OperandBundles.empty()) + UpgradeOperandBundles(OperandBundles); + I = InvokeInst::Create(FTy, Callee, NormalBB, UnwindBB, Ops, OperandBundles); OperandBundles.clear(); @@ -4872,6 +4876,10 @@ } } + // Upgrade the bundles if needed. + if (!OperandBundles.empty()) + UpgradeOperandBundles(OperandBundles); + I = CallBrInst::Create(FTy, Callee, DefaultDest, IndirectDests, Args, OperandBundles); OperandBundles.clear(); @@ -5423,6 +5431,10 @@ } } + // Upgrade the bundles if needed. + if (!OperandBundles.empty()) + UpgradeOperandBundles(OperandBundles); + I = CallInst::Create(FTy, Callee, Args, OperandBundles); OperandBundles.clear(); InstructionList.push_back(I); diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp --- a/llvm/lib/IR/AutoUpgrade.cpp +++ b/llvm/lib/IR/AutoUpgrade.cpp @@ -4629,3 +4629,15 @@ B.addAttribute(Attribute::NullPointerIsValid); } } + +void llvm::UpgradeOperandBundles(std::vector &Bundles) { + + // clang.arc.attachedcall bundles are now required to have an operand. + // If they don't, it's okay to drop them entirely: when there is an operand, + // the "attachedcall" is meaningful and required, but without an operand, + // it's just a marker NOP. Dropping it merely prevents an optimization. + erase_if(Bundles, [&](OperandBundleDef &OBD) { + return OBD.getTag() == "clang.arc.attachedcall" && + OBD.inputs().empty(); + }); +} diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -5811,15 +5811,11 @@ "void return type", Call); - Assert((BU.Inputs.empty() || - (BU.Inputs.size() == 1 && isa(BU.Inputs.front()))), - "operand bundle \"clang.arc.attachedcall\" can take either no " - "arguments or one function as an argument", + Assert(BU.Inputs.size() == 1 && isa(BU.Inputs.front()), + "operand bundle \"clang.arc.attachedcall\" requires one function as " + "an argument", Call); - if (BU.Inputs.empty()) - return; - auto *Fn = cast(BU.Inputs.front()); Intrinsic::ID IID = Fn->getIntrinsicID(); diff --git a/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp b/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp --- a/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp @@ -709,20 +709,24 @@ bool AArch64ExpandPseudo::expandCALL_RVMARKER( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) { - // Expand CALL_RVMARKER pseudo to a branch, followed by the special `mov x29, - // x29` marker. Mark the sequence as bundle, to avoid passes moving other code - // in between. + // Expand CALL_RVMARKER pseudo to: + // - a branch to the call target, followed by + // - the special `mov x29, x29` marker, and + // - another branch, to the runtime function + // Mark the sequence as bundle, to avoid passes moving other code in between. MachineInstr &MI = *MBBI; MachineInstr *OriginalCall; - MachineOperand &CallTarget = MI.getOperand(0); + MachineOperand &RVTarget = MI.getOperand(0); + MachineOperand &CallTarget = MI.getOperand(1); assert((CallTarget.isGlobal() || CallTarget.isReg()) && "invalid operand for regular call"); + assert(RVTarget.isGlobal() && "invalid operand for attached call"); unsigned Opc = CallTarget.isGlobal() ? AArch64::BL : AArch64::BLR; OriginalCall = BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(Opc)).getInstr(); OriginalCall->addOperand(CallTarget); - unsigned RegMaskStartIdx = 1; + unsigned RegMaskStartIdx = 2; // Skip register arguments. Those are added during ISel, but are not // needed for the concrete branch. while (!MI.getOperand(RegMaskStartIdx).isRegMask()) { @@ -736,17 +740,22 @@ llvm::drop_begin(MI.operands(), RegMaskStartIdx)) OriginalCall->addOperand(MO); - auto *Marker = BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ORRXrs)) + BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ORRXrs)) .addReg(AArch64::FP, RegState::Define) .addReg(AArch64::XZR) .addReg(AArch64::FP) - .addImm(0) + .addImm(0); + + auto *RVCall = BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::BL)) + .add(RVTarget) .getInstr(); + if (MI.shouldUpdateCallSiteInfo()) - MBB.getParent()->moveCallSiteInfo(&MI, Marker); + MBB.getParent()->moveCallSiteInfo(&MI, OriginalCall); + MI.eraseFromParent(); finalizeBundle(MBB, OriginalCall->getIterator(), - std::next(Marker->getIterator())); + std::next(RVCall->getIterator())); return true; } diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -6473,12 +6473,18 @@ unsigned CallOpc = AArch64ISD::CALL; // Calls with operand bundle "clang.arc.attachedcall" are special. They should - // be expanded to the call, directly followed by a special marker sequence. - // Use the CALL_RVMARKER to do that. + // be expanded to the call, directly followed by a special marker sequence and + // a call to an ObjC library function. Use CALL_RVMARKER to do that. if (CLI.CB && objcarc::hasAttachedCallOpBundle(CLI.CB)) { assert(!IsTailCall && "tail calls cannot be marked with clang.arc.attachedcall"); CallOpc = AArch64ISD::CALL_RVMARKER; + + // Add a target global address for the retainRV/claimRV runtime function + // just before the call target. + Function *ARCFn = *objcarc::getAttachedARCFunction(CLI.CB); + auto GA = DAG.getTargetGlobalAddress(ARCFn, DL, PtrVT); + Ops.insert(Ops.begin() + 1, GA); } // Returns a chain and a flag for retval copy to use. diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -2324,8 +2324,8 @@ (BLRNoIP GPR64noip:$Rn)>, Requires<[SLSBLRMitigation]>; -def : Pat<(AArch64call_rvmarker GPR64:$Rn), - (BLR_RVMARKER GPR64:$Rn)>, +def : Pat<(AArch64call_rvmarker (i64 tglobaladdr:$rvfunc), GPR64:$Rn), + (BLR_RVMARKER tglobaladdr:$rvfunc, GPR64:$Rn)>, Requires<[NoSLSBLRMitigation]>; let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in { diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARC.h b/llvm/lib/Transforms/ObjCARC/ObjCARC.h --- a/llvm/lib/Transforms/ObjCARC/ObjCARC.h +++ b/llvm/lib/Transforms/ObjCARC/ObjCARC.h @@ -105,8 +105,8 @@ class BundledRetainClaimRVs { public: - BundledRetainClaimRVs(bool ContractPass, bool UseMarker) - : ContractPass(ContractPass), UseMarker(UseMarker) {} + BundledRetainClaimRVs(bool ContractPass) + : ContractPass(ContractPass) {} ~BundledRetainClaimRVs(); /// Insert a retainRV/claimRV call to the normal destination blocks of invokes @@ -156,9 +156,6 @@ DenseMap RVCalls; bool ContractPass; - - /// Indicates whether the target uses a special inline-asm marker. - bool UseMarker; }; } // end namespace objcarc diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp --- a/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp @@ -123,20 +123,9 @@ // can't be tail calls. if (auto *CI = dyn_cast(CB)) CI->setTailCallKind(CallInst::TCK_NoTail); - - if (UseMarker) { - // Remove the retainRV/claimRV function operand from the operand bundle - // to reflect the fact that the backend is responsible for emitting only - // the marker instruction, but not the retainRV/claimRV call. - OperandBundleDef OB("clang.arc.attachedcall", None); - auto *NewCB = CallBase::Create(CB, OB, CB); - CB->replaceAllUsesWith(NewCB); - CB->eraseFromParent(); - } } - if (!ContractPass || !UseMarker) - EraseInstruction(P.first); + EraseInstruction(P.first); } RVCalls.clear(); diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp --- a/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp @@ -434,23 +434,20 @@ LLVM_FALLTHROUGH; case ARCInstKind::RetainRV: case ARCInstKind::UnsafeClaimRV: { - bool IsInstContainedInBundle = BundledInsts->contains(Inst); - - // Return now if the target doesn't need a special inline-asm marker. Return - // true if this is a bundled retainRV/claimRV call, which is going to be - // erased at the end of this pass, to avoid undoing objc-arc-expand and + // Return true if this is a bundled retainRV/claimRV call, which is always + // redundant with the attachedcall in the bundle, and is going to be erased + // at the end of this pass. This avoids undoing objc-arc-expand and // replacing uses of the retainRV/claimRV call's argument with its result. + if (BundledInsts->contains(Inst)) + return true; + + // If this isn't a bundled call, and the target doesn't need a special + // inline-asm marker, we're done: return now, and undo objc-arc-expand. if (!RVInstMarker) - return IsInstContainedInBundle; - - // The target needs a special inline-asm marker. - - // We don't have to emit the marker if this is a bundled call since the - // backend is responsible for emitting it. Return false to undo - // objc-arc-expand. - if (IsInstContainedInBundle) return false; + // The target needs a special inline-asm marker. Insert it. + BasicBlock::iterator BBI = Inst->getIterator(); BasicBlock *InstParent = Inst->getParent(); @@ -548,7 +545,7 @@ AA = A; DT = D; PA.setAA(A); - BundledRetainClaimRVs BRV(true, RVInstMarker); + BundledRetainClaimRVs BRV(/*ContractPass=*/true); BundledInsts = &BRV; std::pair R = BundledInsts->insertAfterInvokes(F, DT); diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp --- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -2459,7 +2459,7 @@ return false; Changed = CFGChanged = false; - BundledRetainClaimRVs BRV(false, objcarc::getRVInstMarker(*F.getParent())); + BundledRetainClaimRVs BRV(/*ContractPass=*/false); BundledInsts = &BRV; LLVM_DEBUG(dbgs() << "<<< ObjCARCOpt: Visiting Function: " << F.getName() diff --git a/llvm/test/Bitcode/upgrade-arc-attachedcall-bundle.ll b/llvm/test/Bitcode/upgrade-arc-attachedcall-bundle.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/upgrade-arc-attachedcall-bundle.ll @@ -0,0 +1,23 @@ +; Test that nullary clang.arc.attachedcall operand bundles are "upgraded". + +; RUN: llvm-dis %s.bc -o - | FileCheck %s +; RUN: verify-uselistorder %s.bc + +define i8* @invalid() { +; CHECK-LABEL: define i8* @invalid() { +; CHECK-NEXT: %tmp0 = call i8* @foo(){{$}} +; CHECK-NEXT: ret i8* %tmp0 + %tmp0 = call i8* @foo() [ "clang.arc.attachedcall"() ] + ret i8* %tmp0 +} + +define i8* @valid() { +; CHECK-LABEL: define i8* @valid() { +; CHECK-NEXT: %tmp0 = call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] +; CHECK-NEXT: ret i8* %tmp0 + %tmp0 = call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] + ret i8* %tmp0 +} + +declare i8* @foo() +declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*) diff --git a/llvm/test/Bitcode/upgrade-arc-attachedcall-bundle.ll.bc b/llvm/test/Bitcode/upgrade-arc-attachedcall-bundle.ll.bc new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@