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
::
<result> = invoke [cconv] [ret attrs] <ptr to function ty> <function ptr val>(<function args>) [fn attrs]
- to label <normal label> unwind label <exception label>
+ [operand bundles] to label <normal label> unwind label <exception label>
Overview:
"""""""""
#. The optional :ref:`function attributes <fnattrs>` list. Only
'``noreturn``', '``nounwind``', '``readonly``' and '``readnone``'
attributes are valid here.
+#. The optional :ref:`operand bundles <opbundles>` list.
Semantics:
""""""""""
::
<result> = [tail | musttail] call [cconv] [ret attrs] <ty> [<fnty>*] <fnptrval>(<function args>) [fn attrs]
+ [ operand bundles ]
Overview:
"""""""""
#. The optional :ref:`function attributes <fnattrs>` list. Only
'``noreturn``', '``nounwind``', '``readonly``' and '``readnone``'
attributes are valid here.
+#. The optional :ref:`operand bundles <opbundles>` list.
Semantics:
""""""""""
TYPE_BLOCK_ID_NEW,
USELIST_BLOCK_ID,
+
+ MODULE_STRTAB_BLOCK_ID,
+ FUNCTION_SUMMARY_BLOCK_ID,
+
+ OPERAND_BUNDLE_TAGS_BLOCK_ID
};
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]
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 {
return false;
}
+/// ParseOptionalOperandBundles
+/// ::= /*empty*/
+/// ::= '[' OperandBundle [, OperandBundle ]* ']'
+///
+/// OperandBundle
+/// ::= bundle-tag '(' ')'
+/// ::= bundle-tag '(' Type Value [, Type Value ]* ')'
+///
+/// bundle-tag ::= String Constant
+bool LLParser::ParseOptionalOperandBundles(
+ SmallVectorImpl<OperandBundleDef> &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.
LocTy RetTypeLoc;
ValID CalleeID;
SmallVector<ParamInfo, 16> ArgList;
+ SmallVector<OperandBundleDef, 2> 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") ||
// 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;
LocTy RetTypeLoc;
ValID CalleeID;
SmallVector<ParamInfo, 16> ArgList;
+ SmallVector<OperandBundleDef, 2> 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
// 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);
bool IsMustTailCall = false,
bool InVarArgsFunc = false);
+ bool
+ ParseOptionalOperandBundles(SmallVectorImpl<OperandBundleDef> &BundleList,
+ PerFunctionState &PFS);
+
bool ParseExceptionArgs(SmallVectorImpl<Value *> &Args,
PerFunctionState &PFS);
bool StripDebugInfo = false;
+ std::vector<std::string> BundleTags;
+
public:
std::error_code error(BitcodeError E, const Twine &Message);
std::error_code error(BitcodeError E);
std::error_code parseAttributeGroupBlock();
std::error_code parseTypeTable();
std::error_code parseTypeTableBody();
+ std::error_code parseOperandBundleTags();
ErrorOr<Value *> recordValue(SmallVectorImpl<uint64_t> &Record,
unsigned NameIndex, Triple &TT);
}
}
+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<uint64_t, 64> 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();
+ }
+}
+
/// Associate a value with its name from the given index in the provided record.
ErrorOr<Value *> BitcodeReader::recordValue(SmallVectorImpl<uint64_t> &Record,
unsigned NameIndex, Triple &TT) {
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;
return nullptr;
};
+ std::vector<OperandBundleDef> OperandBundles;
+
// Read all the records.
SmallVector<uint64_t, 64> Record;
while (1) {
}
}
- I = InvokeInst::Create(Callee, NormalBB, UnwindBB, Ops);
+ I = InvokeInst::Create(Callee, NormalBB, UnwindBB, Ops, OperandBundles);
+ OperandBundles.clear();
InstructionList.push_back(I);
cast<InvokeInst>(I)
->setCallingConv(static_cast<CallingConv::ID>(~(1U << 13) & CCInfo));
}
}
- I = CallInst::Create(FTy, Callee, Args);
+ I = CallInst::Create(FTy, Callee, Args, OperandBundles);
+ OperandBundles.clear();
InstructionList.push_back(I);
cast<CallInst>(I)->setCallingConv(
static_cast<CallingConv::ID>((~(1U << 14) & CCInfo) >> 1));
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<Value *> &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
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.
OutOfRecordLoop:
+ if (!OperandBundles.empty())
+ return error("Operand bundles found with no consumer");
+
// Check the function list for unresolved values.
if (Argument *A = dyn_cast<Argument>(ValueList.back())) {
if (!A->getParent()) {
#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"
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<StringRef, 8> Tags;
+ M->getOperandBundleTags(Tags);
+
+ if (Tags.empty())
+ return;
+
+ Stream.EnterSubblock(bitc::OPERAND_BUNDLE_TAGS_BLOCK_ID, 3);
+
+ SmallVector<uint64_t, 64> 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<uint64_t> &Vals, uint64_t V) {
if ((int64_t)V >= 0)
Vals.push_back(V << 1);
return false;
}
+static void WriteOperandBundles(BitstreamWriter &Stream, ImmutableCallSite CS,
+ unsigned InstID, ValueEnumerator &VE) {
+ SmallVector<unsigned, 64> 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,
const InvokeInst *II = cast<InvokeInst>(&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()));
const CallInst &CI = cast<CallInst>(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()));
if (VE.shouldPreserveUseListOrder())
WriteUseListBlock(nullptr, VE, Stream);
+ WriteOperandBundleTags(M, Stream);
+
// Emit function bodies.
DenseMap<const Function *, uint64_t> FunctionIndex;
for (Module::const_iterator F = M->begin(), E = M->end(); F != E; ++F)
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,
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();
Out << ')';
if (PAL.hasAttributes(AttributeSet::FunctionIndex))
Out << " #" << Machine.getAttributeGroupSlot(PAL.getFnAttributes());
+
+ writeOperandBundles(CI);
+
} else if (const InvokeInst *II = dyn_cast<InvokeInst>(&I)) {
Operand = II->getCalledValue();
FunctionType *FTy = cast<FunctionType>(II->getFunctionType());
if (PAL.hasAttributes(AttributeSet::FunctionIndex))
Out << " #" << Machine.getAttributeGroupSlot(PAL.getFnAttributes());
+ writeOperandBundles(II);
+
Out << "\n to ";
writeOperand(II->getNormalDest(), true);
Out << " unwind ";
; 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"
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 }
--- /dev/null
+; 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
+}
--- /dev/null
+; 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
+}