diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -127,6 +127,9 @@ /// Function must not be optimized. def OptimizeNone : EnumAttr<"optnone">; +/// ??? +def Preallocated : EnumAttr<"preallocated">; + /// Function does not access memory. def ReadNone : EnumAttr<"readnone">; diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1044,6 +1044,11 @@ return getTagID() == LLVMContext::OB_cfguardtarget; } + /// Return true if this is a "callsetup" operand bundle. + bool isCallSetupOperandBundle() const { + return getTagID() == LLVMContext::OB_callsetup; + } + private: /// Pointer to an entry in LLVMContextImpl::getOrInsertBundleTag. StringMapEntry *Tag; diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -503,6 +503,10 @@ llvm_i32_ty], []>; +def int_call_setup : Intrinsic<[llvm_token_ty], [llvm_i32_ty]>; +def int_call_alloc : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_i32_ty]>; +def int_call_teardown : Intrinsic<[], [llvm_token_ty]>; + //===------------------- Standard C Library Intrinsics --------------------===// // diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -89,6 +89,7 @@ OB_funclet = 1, // "funclet" OB_gc_transition = 2, // "gc-transition" OB_cfguardtarget = 3, // "cfguardtarget" + OB_callsetup = 4, // "callsetup" }; /// getMDKindID - Return a unique non-zero ID for the specified metadata kind. 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 @@ -1614,11 +1614,13 @@ unsigned AttrCount = 0; AttrCount += Attrs.hasAttribute(Attribute::ByVal); AttrCount += Attrs.hasAttribute(Attribute::InAlloca); + AttrCount += Attrs.hasAttribute(Attribute::Preallocated); AttrCount += Attrs.hasAttribute(Attribute::StructRet) || Attrs.hasAttribute(Attribute::InReg); AttrCount += Attrs.hasAttribute(Attribute::Nest); - Assert(AttrCount <= 1, "Attributes 'byval', 'inalloca', 'inreg', 'nest', " - "and 'sret' are incompatible!", + Assert(AttrCount <= 1, + "Attributes 'byval', 'inalloca', 'preallocated', 'inreg', 'nest', " + "and 'sret' are incompatible!", V); Assert(!(Attrs.hasAttribute(Attribute::InAlloca) && @@ -1720,9 +1722,11 @@ !RetAttrs.hasAttribute(Attribute::NoFree) && !RetAttrs.hasAttribute(Attribute::Returned) && !RetAttrs.hasAttribute(Attribute::InAlloca) && + !RetAttrs.hasAttribute(Attribute::Preallocated) && !RetAttrs.hasAttribute(Attribute::SwiftSelf) && !RetAttrs.hasAttribute(Attribute::SwiftError)), - "Attributes 'byval', 'inalloca', 'nest', 'sret', 'nocapture', 'nofree'" + "Attributes 'byval', 'inalloca', 'preallocated', 'nest', 'sret', " + "'nocapture', 'nofree'" "'returned', 'swiftself', and 'swifterror' do not apply to return " "values!", V); @@ -3020,9 +3024,11 @@ visitIntrinsicCall(ID, Call); // Verify that a callsite has at most one "deopt", at most one "funclet", at - // most one "gc-transition", and at most one "cfguardtarget" operand bundle. + // most one "gc-transition", at most one "cfguardtarget", + // and at most one "callsetup" operand bundle. bool FoundDeoptBundle = false, FoundFuncletBundle = false, - FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false; + FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false, + FoundCallSetupBundle = false; for (unsigned i = 0, e = Call.getNumOperandBundles(); i < e; ++i) { OperandBundleUse BU = Call.getOperandBundleAt(i); uint32_t Tag = BU.getTagID(); @@ -3047,6 +3053,14 @@ FoundCFGuardTargetBundle = true; Assert(BU.Inputs.size() == 1, "Expected exactly one cfguardtarget bundle operand", Call); + } else if (Tag == LLVMContext::OB_callsetup) { + Assert(!FoundCallSetupBundle, + "Multiple callsetup operand bundles", Call); + FoundCallSetupBundle = true; + Assert(BU.Inputs.size() == 1, + "Expected exactly one callsetup bundle operand", Call); + Assert(BU.Inputs.front().get()->getType()->isTokenTy(), + "callsetup bundle operands should be a token", Call); } } @@ -3077,9 +3091,9 @@ static AttrBuilder getParameterABIAttributes(int I, AttributeList Attrs) { static const Attribute::AttrKind ABIAttrs[] = { - Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca, - Attribute::InReg, Attribute::Returned, Attribute::SwiftSelf, - Attribute::SwiftError}; + Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca, + Attribute::Preallocated, Attribute::InReg, Attribute::Returned, + Attribute::SwiftSelf, Attribute::SwiftError}; AttrBuilder Copy; for (auto AK : ABIAttrs) { if (Attrs.hasParamAttribute(I, AK)) @@ -3119,7 +3133,7 @@ "cannot guarantee tail call due to mismatched calling conv", &CI); // - All ABI-impacting function attributes, such as sret, byval, inreg, - // returned, and inalloca, must match. + // returned, preallocated, and inalloca, must match. AttributeList CallerAttrs = F->getAttributes(); AttributeList CalleeAttrs = CI.getAttributes(); for (int I = 0, E = CallerTy->getNumParams(); I != E; ++I) { @@ -4414,6 +4428,37 @@ } break; } + case Intrinsic::call_setup: { + auto *NumArgs = dyn_cast(Call.getArgOperand(0)); + Assert(NumArgs != nullptr, "llvm.call.setup argument must be a constant"); + bool FoundCall = false; + for (User *U : Call.users()) { + auto *UseCall = dyn_cast(U); + Assert(UseCall != nullptr, "Uses of llvm.call.setup must be calls"); + const Function *Fn = UseCall->getCalledFunction(); + if (Fn->getIntrinsicID() == Intrinsic::call_alloc) { + auto *AllocArgIndex = dyn_cast(UseCall->getArgOperand(1)); + Assert(AllocArgIndex != nullptr, "llvm.call.alloc arg index must be a constant"); + auto AllocArgIndexInt = AllocArgIndex->getValue(); + Assert(AllocArgIndexInt.sge(0) && + AllocArgIndexInt.slt(NumArgs->getValue()), + "llvm.call.alloc arg index must be between 0 and corresponding " + "llvm.call.setup's argument count"); + } else { + Assert(!FoundCall, "Can have at most one call corresponding to a llvm.call.setup"); + FoundCall = true; + Assert(NumArgs->equalsInt(Fn->arg_size()), "llvm.call.setup arg size must be equal to number of arguments at call site"); + auto CallSetupBundle = + UseCall->getOperandBundle(LLVMContext::OB_callsetup); + Assert(CallSetupBundle, "Use of llvm.call.setup outside intrinsics " + "must be in \"callsetup\" operand bundle"); + Assert(CallSetupBundle->Inputs.front().get() == &Call, + "callsetup bundle must have token from corresponding " + "llvm.call.setup"); + } + } + break; + } case Intrinsic::gcroot: case Intrinsic::gcwrite: case Intrinsic::gcread: diff --git a/llvm/test/Verifier/call_setup_invalid.ll b/llvm/test/Verifier/call_setup_invalid.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/call_setup_invalid.ll @@ -0,0 +1,58 @@ +; RUN: not opt -S %s -verify 2>&1 | FileCheck %s + +declare token @llvm.call.setup(i32) +declare i8* @llvm.call.alloc(token, i32) + +declare void @asdf(i32, i32) +declare void @foo0() +declare void @foo1(i8*) +declare void @foo2(i8*, i8*) +declare i32 @blackbox() + +; CHECK: callsetup bundle operands should be a token +define void @call_setup_bundle_token() { + %cs0 = call token @llvm.call.setup(i32 0) + %cs1 = call token @llvm.call.setup(i32 0) + %i = call i32 @blackbox() + call void @foo0() ["callsetup"(i32 %i)] + ret void +} + +; CHECK: Expected exactly one callsetup bundle operand +define void @call_setup_bundle_one_token() { + %cs0 = call token @llvm.call.setup(i32 0) + %cs1 = call token @llvm.call.setup(i32 0) + call void @foo0() ["callsetup"(token %cs0, token %cs1)] + ret void +} + +; CHECK: Multiple callsetup operand bundles +define void @call_setup_multiple_bundles() { + %cs0 = call token @llvm.call.setup(i32 0) + %cs1 = call token @llvm.call.setup(i32 0) + call void @foo0() ["callsetup"(token %cs0), "callsetup"(token %cs1)] + ret void +} + +; CHECK: Can have at most one call +define void @call_setup_one_call() { + %cs = call token @llvm.call.setup(i32 1) + %a0 = call i8* @llvm.call.alloc(token %cs, i32 0) + call void @foo1(i8* %a0) ["callsetup"(token %cs)] + call void @foo1(i8* %a0) ["callsetup"(token %cs)] + ret void +} + +; CHECK: must be a constant +define void @call_setup_constant() { + %ac = call i32 @blackbox() + %cs = call token @llvm.call.setup(i32 %ac) + ret void +} + +; CHECK: must be between 0 and corresponding +define void @call_setup_arg_index_in_bounds() { + %cs = call token @llvm.call.setup(i32 2) + %a0 = call i8* @llvm.call.alloc(token %cs, i32 2) + ret void +} diff --git a/llvm/test/Verifier/call_setup_valid.ll b/llvm/test/Verifier/call_setup_valid.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/call_setup_valid.ll @@ -0,0 +1,19 @@ +; RUN: opt -S %s -verify + +declare token @llvm.call.setup(i32) +declare i8* @llvm.call.alloc(token, i32) + +declare void @f1(i8*) + +define void @callsetup() { + %cs = call token @llvm.call.setup(i32 1) + %a0 = call i8* @llvm.call.alloc(token %cs, i32 0) + call void @f1(i8* %a0) ["callsetup"(token %cs)] + ret void +} + +define void @callsetup_setup_without_call() { + %cs = call token @llvm.call.setup(i32 1) + %a0 = call i8* @llvm.call.alloc(token %cs, i32 0) + ret void +}