82 changes: 80 additions & 2 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ class BitcodeReader : public GVMaterializer {

bool StripDebugInfo = false;

std::vector<std::string> BundleTags;

public:
std::error_code error(BitcodeError E, const Twine &Message);
std::error_code error(BitcodeError E);
Expand Down Expand Up @@ -370,6 +372,7 @@ class BitcodeReader : public GVMaterializer {
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);
Expand Down Expand Up @@ -1586,6 +1589,42 @@ std::error_code BitcodeReader::parseTypeTableBody() {
}
}

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) {
Expand Down Expand Up @@ -3019,6 +3058,10 @@ std::error_code BitcodeReader::parseModule(bool Resume,
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;

Expand Down Expand Up @@ -3556,6 +3599,8 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) {
return nullptr;
};

std::vector<OperandBundleDef> OperandBundles;

// Read all the records.
SmallVector<uint64_t, 64> Record;
while (1) {
Expand Down Expand Up @@ -4325,7 +4370,8 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) {
}
}

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));
Expand Down Expand Up @@ -4708,7 +4754,8 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) {
}
}

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));
Expand All @@ -4733,6 +4780,30 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) {
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
Expand All @@ -4741,6 +4812,10 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) {
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.
Expand All @@ -4757,6 +4832,9 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) {

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()) {
Expand Down
55 changes: 55 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -1392,6 +1394,33 @@ static void WriteModuleMetadataStore(const Module *M, BitstreamWriter &Stream) {
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);
Expand Down Expand Up @@ -1700,6 +1729,23 @@ static bool PushValueAndType(const Value *V, unsigned InstID,
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,
Expand Down Expand Up @@ -1862,6 +1908,10 @@ static void WriteInstruction(const Instruction &I, 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()));
Expand Down Expand Up @@ -2071,6 +2121,9 @@ static void WriteInstruction(const Instruction &I, unsigned InstID,
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()));
Expand Down Expand Up @@ -2591,6 +2644,8 @@ static void WriteModule(const Module *M, BitstreamWriter &Stream,
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)
Expand Down
43 changes: 43 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2022,6 +2022,7 @@ class AssemblyWriter {

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,
Expand Down Expand Up @@ -2157,6 +2158,43 @@ void AssemblyWriter::writeParamOperand(const Value *Operand,
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();

Expand Down Expand Up @@ -2944,6 +2982,9 @@ void AssemblyWriter::printInstruction(const Instruction &I) {
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());
Expand Down Expand Up @@ -2978,6 +3019,8 @@ void AssemblyWriter::printInstruction(const Instruction &I) {
if (PAL.hasAttributes(AttributeSet::FunctionIndex))
Out << " #" << Machine.getAttributeGroupSlot(PAL.getFnAttributes());

writeOperandBundles(II);

Out << "\n to ";
writeOperand(II->getNormalDest(), true);
Out << " unwind ";
Expand Down
154 changes: 153 additions & 1 deletion llvm/test/Bitcode/compatibility.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -1313,6 +1313,158 @@ define void @misc.metadata() {
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 }
Expand Down
152 changes: 152 additions & 0 deletions llvm/test/Bitcode/operand-bundles.ll
Original file line number Diff line number Diff line change
@@ -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
}
36 changes: 36 additions & 0 deletions llvm/test/Verifier/operand-bundles.ll
Original file line number Diff line number Diff line change
@@ -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
}