Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1438,7 +1438,32 @@ the ELF x86-64 abi, but it can be disabled for some compilation units. -.. _moduleasm: + +.. _opbundles: + +Operand Bundles +--------------- + +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. The +exact semantics of an operand bundle is dependent on the bundle tag. Module-Level Inline Assembly ---------------------------- @@ -5017,7 +5042,7 @@ :: = invoke [cconv] [ret attrs] () [fn attrs] - to label unwind label + [ operand bundles ] to label unwind label Overview: """"""""" @@ -5071,6 +5096,7 @@ #. The optional :ref:`function attributes ` list. Only '``noreturn``', '``nounwind``', '``readonly``' and '``readnone``' attributes are valid here. +#. The optional :ref:`operand bundles ` list. Semantics: """""""""" @@ -8259,6 +8285,7 @@ :: = [tail | musttail] call [cconv] [ret attrs] [*] () [fn attrs] + [ operand bundles ] Overview: """"""""" @@ -8338,6 +8365,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 @@ -363,6 +363,13 @@ 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_INST_CALL_WITH_OP_BUNDLE = 55, // CALL_WITH_OP_BUNDLE: CALL, [operand bundle] + FUNC_CODE_INST_INVOKE_WITH_OP_BUNDLE = 56, // INVOKE_WITH_OP_BUNDLE: INVOKE, [operand bundle] + + FUNC_CODE_OPERAND_BUNDLES_HEADER = 57, // BUNDLES_HEADER: [num] + FUNC_CODE_OPERAND_BUNDLE_TAG = 58, // BUNDLE_TAG: [strchr x N] + FUNC_CODE_OPERAND_BUNDLE_CONTENTS = 59, // BUNDLE_CONTENTS: [ values ...] }; enum UseListCodes { Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ lib/AsmParser/LLParser.h @@ -406,6 +406,15 @@ bool IsMustTailCall = false, bool InVarArgsFunc = false); + struct OperandBundleInfo { + std::string Tag; + SmallVector Inputs; + }; + + bool + ParseOptionalOperandBundles(SmallVectorImpl &ArgList, + PerFunctionState &PFS); + bool ParseExceptionArgs(SmallVectorImpl &Args, PerFunctionState &PFS); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -1928,7 +1928,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. @@ -4983,15 +5037,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") || @@ -5067,7 +5121,12 @@ // Finish off the Attribute and check them AttributeSet PAL = AttributeSet::get(Context, Attrs); - InvokeInst *II = InvokeInst::Create(Ty, Callee, NormalBB, UnwindBB, Args); + SmallVector Bundles; + for (auto &ParsedBundle : BundleList) + Bundles.emplace_back(ParsedBundle.Tag, ParsedBundle.Inputs); + + InvokeInst *II = + InvokeInst::Create(Ty, Callee, NormalBB, UnwindBB, Args, Bundles); II->setCallingConv(CC); II->setAttributes(PAL); ForwardRefAttrGroups[II] = FwdRefAttrGrps; @@ -5581,18 +5640,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 @@ -5664,7 +5723,11 @@ // Finish off the Attribute and check them AttributeSet PAL = AttributeSet::get(Context, Attrs); - CallInst *CI = CallInst::Create(Ty, Callee, Args); + SmallVector Bundles; + for (auto &ParsedBundle : BundleList) + Bundles.emplace_back(ParsedBundle.Tag, ParsedBundle.Inputs); + + CallInst *CI = CallInst::Create(Ty, Callee, Args, Bundles); 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 @@ -388,6 +388,14 @@ std::error_code findFunctionInStream( Function *F, DenseMap::iterator DeferredFunctionInfoIterator); + + struct OperandBundleInfo { + std::string Tag; + SmallVector Inputs; + }; + + std::error_code parseBundleInfos(SmallVectorImpl &, + unsigned AbbrevID, unsigned InstNum); }; } // namespace @@ -4127,8 +4135,11 @@ break; } - case bitc::FUNC_CODE_INST_INVOKE: { + case bitc::FUNC_CODE_INST_INVOKE_WITH_OP_BUNDLE: { + case bitc::FUNC_CODE_INST_INVOKE: // INVOKE: [attrs, cc, normBB, unwindBB, fnty, op0,op1,op2, ...] + // INVOKE_WITH_OP_BUNDLE: [paramattrs, cc, fnty, fnid, arg0, arg1...] + // [ OP bundle header ][ OP bundle 0 ] [ OP bundle 1 ] ... if (Record.size() < 4) return error("Invalid record"); unsigned OpNum = 0; @@ -4180,7 +4191,19 @@ } } - I = InvokeInst::Create(Callee, NormalBB, UnwindBB, Ops); + SmallVector BundleInfos; + SmallVector Bundles; + + if (BitCode == bitc::FUNC_CODE_INST_INVOKE_WITH_OP_BUNDLE) { + if (std::error_code EC = + parseBundleInfos(BundleInfos, Entry.ID, NextValueNo)) + return EC; + + for (auto &ParsedBundle : BundleInfos) + Bundles.emplace_back(ParsedBundle.Tag, ParsedBundle.Inputs); + } + + I = InvokeInst::Create(Callee, NormalBB, UnwindBB, Ops, Bundles); InstructionList.push_back(I); cast(I) ->setCallingConv(static_cast(~(1U << 13) & CCInfo)); @@ -4507,8 +4530,11 @@ InstructionList.push_back(I); break; } - case bitc::FUNC_CODE_INST_CALL: { + case bitc::FUNC_CODE_INST_CALL_WITH_OP_BUNDLE: { + case bitc::FUNC_CODE_INST_CALL: // CALL: [paramattrs, cc, fnty, fnid, arg0, arg1...] + // CALL_WITH_OP_BUNDLE: [paramattrs, cc, fnty, fnid, arg0, arg1...] + // [ OP bundle header ] [ OP bundle 0 ] [ OP bundle 1 ] ... if (Record.size() < 3) return error("Invalid record"); @@ -4563,7 +4589,19 @@ } } - I = CallInst::Create(FTy, Callee, Args); + SmallVector BundleInfos; + SmallVector Bundles; + + if (BitCode == bitc::FUNC_CODE_INST_CALL_WITH_OP_BUNDLE) { + if (std::error_code EC = + parseBundleInfos(BundleInfos, Entry.ID, NextValueNo)) + return EC; + + for (auto &ParsedBundle : BundleInfos) + Bundles.emplace_back(ParsedBundle.Tag, ParsedBundle.Inputs); + } + + I = CallInst::Create(FTy, Callee, Args, Bundles); InstructionList.push_back(I); cast(I)->setCallingConv( static_cast((~(1U << 14) & CCInfo) >> 1)); @@ -4775,6 +4813,63 @@ return std::error_code(); } +std::error_code +BitcodeReader::parseBundleInfos(SmallVectorImpl &Output, + unsigned AbbrevID, unsigned InstNum) { + SmallVector Record; + + auto ParseNextRecord = [&](unsigned RecordKind, const char *ErrorMsg) { + Record.clear(); + + BitstreamEntry Entry = Stream.advance(); + if (Entry.Kind != BitstreamEntry::Record) + return error(ErrorMsg); + + if (Stream.readRecord(AbbrevID, Record) != RecordKind) + return error("Expected operand bundle header"); + + return std::error_code(); + }; + + if (std::error_code EC = + ParseNextRecord(bitc::FUNC_CODE_OPERAND_BUNDLES_HEADER, + "Expected operand bundle header")) + return EC; + + unsigned OpBundleCount = (unsigned)Record[0]; + Output.reserve(OpBundleCount); + + for (unsigned i = 0; i < OpBundleCount; i++) { + if (std::error_code EC = ParseNextRecord(bitc::FUNC_CODE_OPERAND_BUNDLE_TAG, + "Expected operand bundle tag")) + return EC; + + std::string Tag; + convertToString(Record, 0, Tag); + + if (std::error_code EC = + ParseNextRecord(bitc::FUNC_CODE_OPERAND_BUNDLE_CONTENTS, + "Expected operand bundle contents")) + return EC; + + SmallVector Inputs; + + unsigned OpNum = 0; + while (OpNum != Record.size()) { + Value *Op; + if (getValueTypePair(Record, OpNum, InstNum, Op)) + return error("Invalid record"); + Inputs.push_back(Op); + } + + Output.emplace_back(); + Output.back().Tag = std::move(Tag); + Output.back().Inputs = std::move(Inputs); + } + + return std::error_code(); +} + std::vector BitcodeReader::getIdentifiedStructTypes() const { return IdentifiedStructTypes; } Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -16,6 +16,7 @@ #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" @@ -1682,12 +1683,36 @@ emitSignedInt64(Vals, diff); } +static void EmitOperandBundles(BitstreamWriter &Stream, ImmutableCallSite CS, + unsigned InstID, ValueEnumerator &VE, + unsigned Abbrev) { + SmallVector Record; + + Record.push_back(CS.getNumOperandBundles()); + Stream.EmitRecord(bitc::FUNC_CODE_OPERAND_BUNDLES_HEADER, Record, Abbrev); + Record.clear(); + + for (unsigned i = 0, e = CS.getNumOperandBundles(); i != e; ++i) { + const auto &Bundle = CS.getOperandBundle(i); + + WriteStringRecord(bitc::FUNC_CODE_OPERAND_BUNDLE_TAG, Bundle.Tag, + Abbrev, Stream); + + for (auto &Input : Bundle.Inputs) + PushValueAndType(Input, InstID, Record, VE); + + Stream.EmitRecord(bitc::FUNC_CODE_OPERAND_BUNDLE_CONTENTS, Record, Abbrev); + Record.clear(); + } +} + /// WriteInstruction - Emit an instruction to the specified stream. static void WriteInstruction(const Instruction &I, unsigned InstID, ValueEnumerator &VE, BitstreamWriter &Stream, SmallVectorImpl &Vals) { unsigned Code = 0; unsigned AbbrevToUse = 0; + bool RecordEmissionPending = true; VE.setInstructionID(&I); switch (I.getOpcode()) { default: @@ -1827,7 +1852,8 @@ const InvokeInst *II = cast(&I); const Value *Callee = II->getCalledValue(); FunctionType *FTy = II->getFunctionType(); - Code = bitc::FUNC_CODE_INST_INVOKE; + Code = II->hasOperandBundles() ? bitc::FUNC_CODE_INST_INVOKE_WITH_OP_BUNDLE + : bitc::FUNC_CODE_INST_INVOKE; Vals.push_back(VE.getAttributeID(II->getAttributes())); Vals.push_back(II->getCallingConv() | 1 << 13); @@ -1846,6 +1872,12 @@ i != e; ++i) PushValueAndType(I.getOperand(i), InstID, Vals, VE); // vararg } + + Stream.EmitRecord(Code, Vals, AbbrevToUse); + RecordEmissionPending = false; + + if (II->hasOperandBundles()) + EmitOperandBundles(Stream, II, InstID, VE, AbbrevToUse); break; } case Instruction::Resume: @@ -2036,7 +2068,8 @@ const CallInst &CI = cast(I); FunctionType *FTy = CI.getFunctionType(); - Code = bitc::FUNC_CODE_INST_CALL; + Code = CI.hasOperandBundles() ? bitc::FUNC_CODE_INST_CALL_WITH_OP_BUNDLE + : bitc::FUNC_CODE_INST_CALL; Vals.push_back(VE.getAttributeID(CI.getAttributes())); Vals.push_back((CI.getCallingConv() << 1) | unsigned(CI.isTailCall()) | @@ -2059,6 +2092,11 @@ i != e; ++i) PushValueAndType(CI.getArgOperand(i), InstID, Vals, VE); // varargs } + + Stream.EmitRecord(Code, Vals, AbbrevToUse); + RecordEmissionPending = false; + if (CI.hasOperandBundles()) + EmitOperandBundles(Stream, &CI, InstID, VE, AbbrevToUse); break; } case Instruction::VAArg: @@ -2069,7 +2107,9 @@ break; } - Stream.EmitRecord(Code, Vals, AbbrevToUse); + if (RecordEmissionPending) + Stream.EmitRecord(Code, Vals, AbbrevToUse); + Vals.clear(); } Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -2020,6 +2020,7 @@ void writeOperand(const Value *Op, bool PrintType); void writeParamOperand(const Value *Operand, AttributeSet Attrs,unsigned Idx); + void writeOperandBundles(ArrayRef Bundles); void writeAtomic(AtomicOrdering Ordering, SynchronizationScope SynchScope); void writeAtomicCmpXchg(AtomicOrdering SuccessOrdering, AtomicOrdering FailureOrdering, @@ -2155,6 +2156,39 @@ WriteAsOperandInternal(Out, Operand, &TypePrinter, &Machine, TheModule); } +void AssemblyWriter::writeOperandBundles( + ArrayRef Bundles) { + Out << " [ "; + + bool FirstBundle = true; + for (auto &B : Bundles) { + if (!FirstBundle) + Out << ", "; + FirstBundle = false; + + Out << '"'; + PrintEscapedString(B.Tag, Out); + Out << '"'; + + Out << '('; + + bool FirstInput = true; + for (const auto &Input : B.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(); @@ -2938,6 +2972,14 @@ Out << ')'; if (PAL.hasAttributes(AttributeSet::FunctionIndex)) Out << " #" << Machine.getAttributeGroupSlot(PAL.getFnAttributes()); + + if (CI->hasOperandBundles()) { + SmallVector Bundles; + for (unsigned i = 0, e = CI->getNumOperandBundles(); i != e; ++i) + Bundles.push_back(CI->getOperandBundle(i)); + writeOperandBundles(Bundles); + } + } else if (const InvokeInst *II = dyn_cast(&I)) { Operand = II->getCalledValue(); FunctionType *FTy = cast(II->getFunctionType()); @@ -2972,6 +3014,13 @@ if (PAL.hasAttributes(AttributeSet::FunctionIndex)) Out << " #" << Machine.getAttributeGroupSlot(PAL.getFnAttributes()); + if (II->hasOperandBundles()) { + SmallVector Bundles; + for (unsigned i = 0, e = II->getNumOperandBundles(); i != e; ++i) + Bundles.push_back(II->getOperandBundle(i)); + writeOperandBundles(Bundles); + } + Out << "\n to "; writeOperand(II->getNormalDest(), true); Out << " unwind "; Index: test/Bitcode/compatibility.ll =================================================================== --- test/Bitcode/compatibility.ll +++ test/Bitcode/compatibility.ll @@ -1215,6 +1215,158 @@ declare void @llvm.tokenfoo(token) ; CHECK: declare void @llvm.tokenfoo(token) +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 +}