Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1438,6 +1438,53 @@ the ELF x86-64 abi, but it can be disabled for some compilation units. + +.. _opbundles: + +Operand Bundles +--------------- + +Note: operand bundles are a work in progress, and they should be +considered experimental at this time. + +Operand bundles are tagged sets of SSA values that can be associated +with certain LLVM instructions (currently only ``call``s and +``invoke``s). In a way they are like metadata, but dropping them is +incorrect and will change program semantics. + +Syntax:: + operand bundle set ::= '[' operand bundle ']' + operand bundle ::= tag '(' [ bundle operand ] (, bundle operand )* ')' + bundle operand ::= SSA value + tag ::= string constant + +Operand bundles are **not** part of a function's signature, and a +given function may be called from multiple places with different kinds +of operand bundles. This reflects the fact that the operand bundles +are conceptually a part of the ``call`` (or ``invoke``), not the +callee being dispatched to. + +Operand bundles are a generic mechanism intended to support +runtime-introspection-like functionality for managed languages. While +the exact semantics of an operand bundle depend on the bundle tag, +there are certain limitations to how much the presence of an operand +bundle can influence the semantics of a program. These restrictions +are described as the semantics of an "unknown" operand bundle. As +long as the behavior of an operand bundle is describable within these +restrictions, LLVM does not need to have special knowledge of the +operand bundle to not miscompile programs containing it. + + - The bundle operands for an unknown operand bundle escape in unknown + ways before control is transferred to the callee or invokee. + + - Calls and invokes with operand bundles have unknown read / write + effect on the heap on entry and exit (even if the call target is + ``readnone`` or ``readonly``). + + - An operand bundle at a call site cannot change the implementation + of the called function. Inter-procedural optimizations work as + usual as long as they take into account the first two properties. + .. _moduleasm: Module-Level Inline Assembly @@ -5061,7 +5108,7 @@ :: = invoke [cconv] [ret attrs] () [fn attrs] - to label unwind label + [operand bundles] to label unwind label Overview: """"""""" @@ -5115,6 +5162,7 @@ #. The optional :ref:`function attributes ` list. Only '``noreturn``', '``nounwind``', '``readonly``' and '``readnone``' attributes are valid here. +#. The optional :ref:`operand bundles ` list. Semantics: """""""""" @@ -8309,6 +8357,7 @@ :: = [tail | musttail] call [cconv] [ret attrs] [*] () [fn attrs] + [ operand bundles ] Overview: """"""""" @@ -8388,6 +8437,7 @@ #. The optional :ref:`function attributes ` list. Only '``noreturn``', '``nounwind``', '``readonly``' and '``readnone``' attributes are valid here. +#. The optional :ref:`operand bundles ` list. Semantics: """""""""" Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -42,7 +42,9 @@ TYPE_BLOCK_ID_NEW, - USELIST_BLOCK_ID + USELIST_BLOCK_ID, + + OPERAND_BUNDLE_TAGS_BLOCK_ID }; @@ -126,6 +128,10 @@ TYPE_CODE_TOKEN = 22 // TOKEN }; + enum OperandBundleTagCode { + OPERAND_BUNDLE_TAG = 1, // TAG: [strchr x N] + }; + // The type symbol table only has one code (TST_ENTRY_CODE). enum TypeSymtabCodes { TST_CODE_ENTRY = 1 // TST_ENTRY: [typeid, namechar x N] @@ -363,6 +369,8 @@ FUNC_CODE_INST_CLEANUPPAD = 52, // CLEANUPPAD: [num,args...] FUNC_CODE_INST_CATCHENDPAD = 53, // CATCHENDPAD: [] or [bb#] FUNC_CODE_INST_CLEANUPENDPAD = 54, // CLEANUPENDPAD: [val] or [val,bb#] + + FUNC_CODE_OPERAND_BUNDLE = 55, // OPERAND_BUNDLE: [tag#, value...] }; enum UseListCodes { Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ lib/AsmParser/LLParser.h @@ -406,6 +406,10 @@ bool IsMustTailCall = false, bool InVarArgsFunc = false); + bool + ParseOptionalOperandBundles(SmallVectorImpl &BundleList, + PerFunctionState &PFS); + bool ParseExceptionArgs(SmallVectorImpl &Args, PerFunctionState &PFS); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -1940,7 +1940,61 @@ return false; } +/// ParseOptionalOperandBundles +/// ::= /*empty*/ +/// ::= '[' OperandBundle [, OperandBundle ]* ']' +/// +/// OperandBundle +/// ::= bundle-tag '(' ')' +/// ::= bundle-tag '(' Type Value [, Type Value ]* ')' +/// +/// bundle-tag ::= String Constant +bool LLParser::ParseOptionalOperandBundles( + SmallVectorImpl &BundleList, PerFunctionState &PFS) { + LocTy BeginLoc = Lex.getLoc(); + if (!EatIfPresent(lltok::lsquare)) + return false; + + while (Lex.getKind() != lltok::rsquare) { + // If this isn't the first operand bundle, we need a comma. + if (!BundleList.empty() && + ParseToken(lltok::comma, "expected ',' in input list")) + return true; + + std::string Tag; + if (ParseStringConstant(Tag)) + return true; + + BundleList.emplace_back(); + auto &OBI = BundleList.back(); + OBI.Tag = std::move(Tag); + + if (ParseToken(lltok::lparen, "expected '(' in operand bundle")) + return true; + + while (Lex.getKind() != lltok::rparen) { + // If this isn't the first input, we need a comma. + if (!OBI.Inputs.empty() && + ParseToken(lltok::comma, "expected ',' in input list")) + return true; + + Type *Ty = nullptr; + Value *Input = nullptr; + if (ParseType(Ty) || ParseValue(Ty, Input, PFS)) + return true; + OBI.Inputs.push_back(Input); + } + + Lex.Lex(); // Lex the ')'. + } + + if (BundleList.empty()) + return Error(BeginLoc, "operand bundle set must not be empty"); + + Lex.Lex(); // Lex the ']'. + return false; +} /// ParseArgumentList - Parse the argument list for a function type or function /// prototype. @@ -4996,15 +5050,15 @@ LocTy RetTypeLoc; ValID CalleeID; SmallVector ArgList; + SmallVector BundleList; BasicBlock *NormalBB, *UnwindBB; - if (ParseOptionalCallingConv(CC) || - ParseOptionalReturnAttrs(RetAttrs) || + if (ParseOptionalCallingConv(CC) || ParseOptionalReturnAttrs(RetAttrs) || ParseType(RetType, RetTypeLoc, true /*void allowed*/) || - ParseValID(CalleeID) || - ParseParameterList(ArgList, PFS) || + ParseValID(CalleeID) || ParseParameterList(ArgList, PFS) || ParseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false, NoBuiltinLoc) || + ParseOptionalOperandBundles(BundleList, PFS) || ParseToken(lltok::kw_to, "expected 'to' in invoke") || ParseTypeAndBasicBlock(NormalBB, PFS) || ParseToken(lltok::kw_unwind, "expected 'unwind' in invoke") || @@ -5080,7 +5134,8 @@ // Finish off the Attribute and check them AttributeSet PAL = AttributeSet::get(Context, Attrs); - InvokeInst *II = InvokeInst::Create(Ty, Callee, NormalBB, UnwindBB, Args); + InvokeInst *II = + InvokeInst::Create(Ty, Callee, NormalBB, UnwindBB, Args, BundleList); II->setCallingConv(CC); II->setAttributes(PAL); ForwardRefAttrGroups[II] = FwdRefAttrGrps; @@ -5594,18 +5649,18 @@ LocTy RetTypeLoc; ValID CalleeID; SmallVector ArgList; + SmallVector BundleList; LocTy CallLoc = Lex.getLoc(); if ((TCK != CallInst::TCK_None && ParseToken(lltok::kw_call, "expected 'tail call'")) || - ParseOptionalCallingConv(CC) || - ParseOptionalReturnAttrs(RetAttrs) || + ParseOptionalCallingConv(CC) || ParseOptionalReturnAttrs(RetAttrs) || ParseType(RetType, RetTypeLoc, true /*void allowed*/) || ParseValID(CalleeID) || ParseParameterList(ArgList, PFS, TCK == CallInst::TCK_MustTail, PFS.getFunction().isVarArg()) || - ParseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false, - BuiltinLoc)) + ParseFnAttributeValuePairs(FnAttrs, FwdRefAttrGrps, false, BuiltinLoc) || + ParseOptionalOperandBundles(BundleList, PFS)) return true; // If RetType is a non-function pointer type, then this is the short syntax @@ -5677,7 +5732,7 @@ // Finish off the Attribute and check them AttributeSet PAL = AttributeSet::get(Context, Attrs); - CallInst *CI = CallInst::Create(Ty, Callee, Args); + CallInst *CI = CallInst::Create(Ty, Callee, Args, BundleList); CI->setTailCallKind(TCK); CI->setCallingConv(CC); CI->setAttributes(PAL); Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -225,6 +225,8 @@ bool StripDebugInfo = false; + std::vector BundleTags; + public: std::error_code error(BitcodeError E, const Twine &Message); std::error_code error(BitcodeError E); @@ -369,6 +371,7 @@ std::error_code parseAttributeGroupBlock(); std::error_code parseTypeTable(); std::error_code parseTypeTableBody(); + std::error_code parseOperandBundleTags(); std::error_code parseValueSymbolTable(); std::error_code parseConstants(); @@ -1583,6 +1586,42 @@ } } +std::error_code BitcodeReader::parseOperandBundleTags() { + if (Stream.EnterSubBlock(bitc::OPERAND_BUNDLE_TAGS_BLOCK_ID)) + return error("Invalid record"); + + if (!BundleTags.empty()) + return error("Invalid multiple blocks"); + + SmallVector Record; + + while (1) { + BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); + + switch (Entry.Kind) { + case BitstreamEntry::SubBlock: // Handled for us already. + case BitstreamEntry::Error: + return error("Malformed block"); + case BitstreamEntry::EndBlock: + return std::error_code(); + case BitstreamEntry::Record: + // The interesting case. + break; + } + + // Tags are implicitly mapped to integers by their order. + + if (Stream.readRecord(Entry.ID, Record) != bitc::OPERAND_BUNDLE_TAG) + return error("Invalid record"); + + // OPERAND_BUNDLE_TAG: [strchr x N] + BundleTags.emplace_back(); + if (convertToString(Record, 0, BundleTags.back())) + return error("Invalid record"); + Record.clear(); + } +} + std::error_code BitcodeReader::parseValueSymbolTable() { if (Stream.EnterSubBlock(bitc::VALUE_SYMTAB_BLOCK_ID)) return error("Invalid record"); @@ -2897,6 +2936,10 @@ if (std::error_code EC = parseUseLists()) return EC; break; + case bitc::OPERAND_BUNDLE_TAGS_BLOCK_ID: + if (std::error_code EC = parseOperandBundleTags()) + return EC; + break; } continue; @@ -3412,6 +3455,8 @@ return nullptr; }; + std::vector OperandBundles; + // Read all the records. SmallVector Record; while (1) { @@ -4181,7 +4226,8 @@ } } - I = InvokeInst::Create(Callee, NormalBB, UnwindBB, Ops); + I = InvokeInst::Create(Callee, NormalBB, UnwindBB, Ops, OperandBundles); + OperandBundles.clear(); InstructionList.push_back(I); cast(I) ->setCallingConv(static_cast(~(1U << 13) & CCInfo)); @@ -4564,7 +4610,8 @@ } } - I = CallInst::Create(FTy, Callee, Args); + I = CallInst::Create(FTy, Callee, Args, OperandBundles); + OperandBundles.clear(); InstructionList.push_back(I); cast(I)->setCallingConv( static_cast((~(1U << 14) & CCInfo) >> 1)); @@ -4589,6 +4636,30 @@ InstructionList.push_back(I); break; } + + case bitc::FUNC_CODE_OPERAND_BUNDLE: { + // A call or an invoke can be optionally prefixed with some variable + // number of operand bundle blocks. These blocks are read into + // OperandBundles and consumed at the next call or invoke instruction. + + if (Record.size() < 1 || Record[0] >= BundleTags.size()) + return error("Invalid record"); + + OperandBundles.emplace_back(); + OperandBundles.back().Tag = BundleTags[Record[0]]; + + std::vector &Inputs = OperandBundles.back().Inputs; + + unsigned OpNum = 1; + while (OpNum != Record.size()) { + Value *Op; + if (getValueTypePair(Record, OpNum, NextValueNo, Op)) + return error("Invalid record"); + Inputs.push_back(Op); + } + + continue; + } } // Add instruction to end of current BB. If there is no current BB, reject @@ -4597,6 +4668,10 @@ delete I; return error("Invalid instruction with no BB"); } + if (!OperandBundles.empty()) { + delete I; + return error("Operand bundles found with no consumer"); + } CurBB->getInstList().push_back(I); // If this was a terminator instruction, move to the next block. @@ -4613,6 +4688,9 @@ OutOfRecordLoop: + if (!OperandBundles.empty()) + return error("Operand bundles found with no consumer"); + // Check the function list for unresolved values. if (Argument *A = dyn_cast(ValueList.back())) { if (!A->getParent()) { Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -16,11 +16,13 @@ #include "llvm/ADT/Triple.h" #include "llvm/Bitcode/BitstreamWriter.h" #include "llvm/Bitcode/LLVMBitCodes.h" +#include "llvm/IR/CallSite.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/UseListOrder.h" @@ -1357,6 +1359,33 @@ Stream.ExitBlock(); } +static void WriteOperandBundleTags(const Module *M, BitstreamWriter &Stream) { + // Write metadata kinds + // + // OPERAND_BUNDLE_TAGS_BLOCK_ID : N x OPERAND_BUNDLE_TAG + // + // OPERAND_BUNDLE_TAG - [strchr x N] + + SmallVector Tags; + M->getOperandBundleTags(Tags); + + if (Tags.empty()) + return; + + Stream.EnterSubblock(bitc::OPERAND_BUNDLE_TAGS_BLOCK_ID, 3); + + SmallVector Record; + + for (auto Tag : Tags) { + Record.append(Tag.begin(), Tag.end()); + + Stream.EmitRecord(bitc::OPERAND_BUNDLE_TAG, Record, 0); + Record.clear(); + } + + Stream.ExitBlock(); +} + static void emitSignedInt64(SmallVectorImpl &Vals, uint64_t V) { if ((int64_t)V >= 0) Vals.push_back(V << 1); @@ -1665,6 +1694,23 @@ return false; } +static void WriteOperandBundles(BitstreamWriter &Stream, ImmutableCallSite CS, + unsigned InstID, ValueEnumerator &VE) { + SmallVector Record; + LLVMContext &C = CS.getInstruction()->getContext(); + + for (unsigned i = 0, e = CS.getNumOperandBundles(); i != e; ++i) { + const auto &Bundle = CS.getOperandBundle(i); + Record.push_back(C.getOperandBundleTagID(Bundle.Tag)); + + for (auto &Input : Bundle.Inputs) + PushValueAndType(Input, InstID, Record, VE); + + Stream.EmitRecord(bitc::FUNC_CODE_OPERAND_BUNDLE, Record); + Record.clear(); + } +} + /// pushValue - Like PushValueAndType, but where the type of the value is /// omitted (perhaps it was already encoded in an earlier operand). static void pushValue(const Value *V, unsigned InstID, @@ -1827,6 +1873,10 @@ const InvokeInst *II = cast(&I); const Value *Callee = II->getCalledValue(); FunctionType *FTy = II->getFunctionType(); + + if (II->hasOperandBundles()) + WriteOperandBundles(Stream, II, InstID, VE); + Code = bitc::FUNC_CODE_INST_INVOKE; Vals.push_back(VE.getAttributeID(II->getAttributes())); @@ -2036,6 +2086,9 @@ const CallInst &CI = cast(I); FunctionType *FTy = CI.getFunctionType(); + if (CI.hasOperandBundles()) + WriteOperandBundles(Stream, &CI, InstID, VE); + Code = bitc::FUNC_CODE_INST_CALL; Vals.push_back(VE.getAttributeID(CI.getAttributes())); @@ -2457,6 +2510,8 @@ if (VE.shouldPreserveUseListOrder()) WriteUseListBlock(nullptr, VE, Stream); + WriteOperandBundleTags(M, Stream); + // Emit function bodies. for (Module::const_iterator F = M->begin(), E = M->end(); F != E; ++F) if (!F->isDeclaration()) Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -2022,6 +2022,7 @@ void writeOperand(const Value *Op, bool PrintType); void writeParamOperand(const Value *Operand, AttributeSet Attrs,unsigned Idx); + void writeOperandBundles(ImmutableCallSite CS); void writeAtomic(AtomicOrdering Ordering, SynchronizationScope SynchScope); void writeAtomicCmpXchg(AtomicOrdering SuccessOrdering, AtomicOrdering FailureOrdering, @@ -2157,6 +2158,43 @@ WriteAsOperandInternal(Out, Operand, &TypePrinter, &Machine, TheModule); } +void AssemblyWriter::writeOperandBundles(ImmutableCallSite CS) { + if (!CS.hasOperandBundles()) + return; + + Out << " [ "; + + bool FirstBundle = true; + for (unsigned i = 0, e = CS.getNumOperandBundles(); i != e; ++i) { + OperandBundleUse BU = CS.getOperandBundle(i); + + if (!FirstBundle) + Out << ", "; + FirstBundle = false; + + Out << '"'; + PrintEscapedString(BU.Tag, Out); + Out << '"'; + + Out << '('; + + bool FirstInput = true; + for (const auto &Input : BU.Inputs) { + if (!FirstInput) + Out << ", "; + FirstInput = false; + + TypePrinter.print(Input->getType(), Out); + Out << " "; + WriteAsOperandInternal(Out, Input, &TypePrinter, &Machine, TheModule); + } + + Out << ')'; + } + + Out << " ]"; +} + void AssemblyWriter::printModule(const Module *M) { Machine.initialize(); @@ -2944,6 +2982,9 @@ Out << ')'; if (PAL.hasAttributes(AttributeSet::FunctionIndex)) Out << " #" << Machine.getAttributeGroupSlot(PAL.getFnAttributes()); + + writeOperandBundles(CI); + } else if (const InvokeInst *II = dyn_cast(&I)) { Operand = II->getCalledValue(); FunctionType *FTy = cast(II->getFunctionType()); @@ -2978,6 +3019,8 @@ if (PAL.hasAttributes(AttributeSet::FunctionIndex)) Out << " #" << Machine.getAttributeGroupSlot(PAL.getFnAttributes()); + writeOperandBundles(II); + Out << "\n to "; writeOperand(II->getNormalDest(), true); Out << " unwind "; Index: test/Bitcode/compatibility.ll =================================================================== --- test/Bitcode/compatibility.ll +++ test/Bitcode/compatibility.ll @@ -6,7 +6,7 @@ ; http://llvm.org/docs/DeveloperPolicy.html#ir-backwards-compatibility ; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s -; RUN: verify-uselistorder < %s +; RUN-PR24755: verify-uselistorder < %s target datalayout = "E" ; CHECK: target datalayout = "E" @@ -1313,6 +1313,158 @@ ret void } +declare void @op_bundle_callee_0() +declare void @op_bundle_callee_1(i32,i32) + +define void @call_with_operand_bundle0(i32* %ptr) { +; CHECK-LABEL: call_with_operand_bundle0( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + call void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] +; CHECK: call void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] + ret void +} + +define void @call_with_operand_bundle1(i32* %ptr) { +; CHECK-LABEL: call_with_operand_bundle1( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + + call void @op_bundle_callee_0() + call void @op_bundle_callee_0() [ "foo"() ] + call void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] +; CHECK: @op_bundle_callee_0(){{$}} +; CHECK-NEXT: call void @op_bundle_callee_0() [ "foo"() ] +; CHECK-NEXT: call void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] + ret void +} + +define void @call_with_operand_bundle2(i32* %ptr) { +; CHECK-LABEL: call_with_operand_bundle2( + entry: + call void @op_bundle_callee_0() [ "foo"() ] +; CHECK: call void @op_bundle_callee_0() [ "foo"() ] + ret void +} + +define void @call_with_operand_bundle3(i32* %ptr) { +; CHECK-LABEL: call_with_operand_bundle3( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + call void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] +; CHECK: call void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + ret void +} + +define void @call_with_operand_bundle4(i32* %ptr) { +; CHECK-LABEL: call_with_operand_bundle4( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + call void @op_bundle_callee_1(i32 10, i32 %x) [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] +; CHECK: call void @op_bundle_callee_1(i32 10, i32 %x) [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + ret void +} + +; Invoke versions of the above tests: + + +define void @invoke_with_operand_bundle0(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @invoke_with_operand_bundle0( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + invoke void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] to label %normal unwind label %exception +; CHECK: invoke void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] + +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} + +define void @invoke_with_operand_bundle1(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @invoke_with_operand_bundle1( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + + invoke void @op_bundle_callee_0() to label %normal unwind label %exception +; CHECK: invoke void @op_bundle_callee_0(){{$}} + +exception: + %cleanup = landingpad i8 cleanup + br label %normal + +normal: + invoke void @op_bundle_callee_0() [ "foo"() ] to label %normal1 unwind label %exception1 +; CHECK: invoke void @op_bundle_callee_0() [ "foo"() ] + +exception1: + %cleanup1 = landingpad i8 cleanup + br label %normal1 + +normal1: + invoke void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] to label %normal2 unwind label %exception2 +; CHECK: invoke void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + +exception2: + %cleanup2 = landingpad i8 cleanup + br label %normal2 + +normal2: + ret void +} + +define void @invoke_with_operand_bundle2(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @invoke_with_operand_bundle2( + entry: + invoke void @op_bundle_callee_0() [ "foo"() ] to label %normal unwind label %exception +; CHECK: invoke void @op_bundle_callee_0() [ "foo"() ] + +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} + +define void @invoke_with_operand_bundle3(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @invoke_with_operand_bundle3( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + invoke void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] to label %normal unwind label %exception +; CHECK: invoke void @op_bundle_callee_0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} + +define void @invoke_with_operand_bundle4(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @invoke_with_operand_bundle4( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + invoke void @op_bundle_callee_1(i32 10, i32 %x) [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + to label %normal unwind label %exception +; CHECK: invoke void @op_bundle_callee_1(i32 10, i32 %x) [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} + + ; CHECK: attributes #0 = { alignstack=4 } ; CHECK: attributes #1 = { alignstack=8 } ; CHECK: attributes #2 = { alwaysinline } Index: test/Bitcode/operand-bundles.ll =================================================================== --- /dev/null +++ test/Bitcode/operand-bundles.ll @@ -0,0 +1,152 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s + +declare void @callee0() +declare void @callee1(i32,i32) + +define void @f0(i32* %ptr) { +; CHECK-LABEL: @f0( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + call void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] +; CHECK: call void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] + ret void +} + +define void @f1(i32* %ptr) { +; CHECK-LABEL: @f1( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + + call void @callee0() + call void @callee0() [ "foo"() ] + call void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] +; CHECK: @callee0(){{$}} +; CHECK-NEXT: call void @callee0() [ "foo"() ] +; CHECK-NEXT: call void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] + ret void +} + +define void @f2(i32* %ptr) { +; CHECK-LABEL: @f2( + entry: + call void @callee0() [ "foo"() ] +; CHECK: call void @callee0() [ "foo"() ] + ret void +} + +define void @f3(i32* %ptr) { +; CHECK-LABEL: @f3( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + call void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] +; CHECK: call void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + ret void +} + +define void @f4(i32* %ptr) { +; CHECK-LABEL: @f4( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + call void @callee1(i32 10, i32 %x) [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] +; CHECK: call void @callee1(i32 10, i32 %x) [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + ret void +} + +; Invoke versions of the above tests: + + +define void @g0(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @g0( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + invoke void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] to label %normal unwind label %exception +; CHECK: invoke void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] + +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} + +define void @g1(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @g1( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + + invoke void @callee0() to label %normal unwind label %exception +; CHECK: invoke void @callee0(){{$}} + +exception: + %cleanup = landingpad i8 cleanup + br label %normal + +normal: + invoke void @callee0() [ "foo"() ] to label %normal1 unwind label %exception1 +; CHECK: invoke void @callee0() [ "foo"() ] + +exception1: + %cleanup1 = landingpad i8 cleanup + br label %normal1 + +normal1: + invoke void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] to label %normal2 unwind label %exception2 +; CHECK: invoke void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + +exception2: + %cleanup2 = landingpad i8 cleanup + br label %normal2 + +normal2: + ret void +} + +define void @g2(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @g2( + entry: + invoke void @callee0() [ "foo"() ] to label %normal unwind label %exception +; CHECK: invoke void @callee0() [ "foo"() ] + +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} + +define void @g3(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @g3( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + invoke void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] to label %normal unwind label %exception +; CHECK: invoke void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} + +define void @g4(i32* %ptr) personality i8 3 { +; CHECK-LABEL: @g4( + entry: + %l = load i32, i32* %ptr + %x = add i32 42, 1 + invoke void @callee1(i32 10, i32 %x) [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + to label %normal unwind label %exception +; CHECK: invoke void @callee1(i32 10, i32 %x) [ "foo"(i32 42, i64 100, i32 %x), "foo"(i32 42, float 0.000000e+00, i32 %l) ] + +exception: + %cleanup = landingpad i8 cleanup + br label %normal +normal: + ret void +} Index: test/Verifier/operand-bundles.ll =================================================================== --- /dev/null +++ test/Verifier/operand-bundles.ll @@ -0,0 +1,36 @@ +; RUN: not opt -verify < %s 2>&1 | FileCheck %s + +; Operand bundles uses are like regular uses, and need to be dominated +; by their defs. + +declare void @g() + +define void @f0(i32* %ptr) { +; CHECK: Instruction does not dominate all uses! +; CHECK-NEXT: %x = add i32 42, 1 +; CHECK-NEXT: call void @g() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] + + entry: + %l = load i32, i32* %ptr + call void @g() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.0, i64 100, i32 %l) ] + %x = add i32 42, 1 + ret void +} + +define void @f1(i32* %ptr) personality i8 3 { +; CHECK: Instruction does not dominate all uses! +; CHECK-NEXT: %x = add i32 42, 1 +; CHECK-NEXT: invoke void @g() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ] + + entry: + %l = load i32, i32* %ptr + invoke void @g() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.0, i64 100, i32 %l) ] to label %normal unwind label %exception + +exception: + %cleanup = landingpad i8 cleanup + br label %normal + +normal: + %x = add i32 42, 1 + ret void +}