mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
Revert "Allow output constraints on "asm goto""
This reverts commit 52366088a8e42c2f1e96e8430b84b8b65ec3f7bc. I accidentally pushed this before supporting changes.
This commit is contained in:
parent
1e81c3e696
commit
77dae6a102
@ -7099,14 +7099,14 @@ Syntax:
|
||||
::
|
||||
|
||||
<result> = callbr [cconv] [ret attrs] [addrspace(<num>)] <ty>|<fnty> <fnptrval>(<function args>) [fn attrs]
|
||||
[operand bundles] to label <fallthrough label> [indirect labels]
|
||||
[operand bundles] to label <normal label> [other labels]
|
||||
|
||||
Overview:
|
||||
"""""""""
|
||||
|
||||
The '``callbr``' instruction causes control to transfer to a specified
|
||||
function, with the possibility of control flow transfer to either the
|
||||
'``fallthrough``' label or one of the '``indirect``' labels.
|
||||
'``normal``' label or one of the '``other``' labels.
|
||||
|
||||
This instruction should only be used to implement the "goto" feature of gcc
|
||||
style inline assembly. Any other usage is an error in the IR verifier.
|
||||
@ -7133,17 +7133,17 @@ This instruction requires several arguments:
|
||||
type can be omitted if the function is not varargs.
|
||||
#. '``fnptrval``': An LLVM value containing a pointer to a function to
|
||||
be called. In most cases, this is a direct function call, but
|
||||
other ``callbr``'s are just as possible, calling an arbitrary pointer
|
||||
indirect ``callbr``'s are just as possible, calling an arbitrary pointer
|
||||
to function value.
|
||||
#. '``function args``': argument list whose types match the function
|
||||
signature argument types and parameter attributes. All arguments must
|
||||
be of :ref:`first class <t_firstclass>` type. If the function signature
|
||||
indicates the function accepts a variable number of arguments, the
|
||||
extra arguments can be specified.
|
||||
#. '``fallthrough label``': the label reached when the inline assembly's
|
||||
execution exits the bottom.
|
||||
#. '``indirect labels``': the labels reached when a callee transfers control
|
||||
to a location other than the '``fallthrough label``'. The blockaddress
|
||||
#. '``normal label``': the label reached when the called function
|
||||
executes a '``ret``' instruction.
|
||||
#. '``other labels``': the labels reached when a callee transfers control
|
||||
to a location other than the normal '``normal label``'. The blockaddress
|
||||
constant for these should also be in the list of '``function args``'.
|
||||
#. The optional :ref:`function attributes <fnattrs>` list.
|
||||
#. The optional :ref:`operand bundles <opbundles>` list.
|
||||
@ -7156,13 +7156,6 @@ instruction in most regards. The primary difference is that it
|
||||
establishes an association with additional labels to define where control
|
||||
flow goes after the call.
|
||||
|
||||
Outputs of a '``callbr``' instruction are valid only on the '``fallthrough``'
|
||||
path. Use of outputs on the '``indirect``' path(s) results in :ref:`poison
|
||||
values <poisonvalues>`. Care should be taken if the '``fallthrough``' block is
|
||||
also listed in the '``indirect``' blocks. The outputs may or may not result in
|
||||
:ref:`poison values <poisonvalues>` depending on the behavior of the inline
|
||||
assembly.
|
||||
|
||||
The only use of this today is to implement the "goto" feature of gcc inline
|
||||
assembly where additional labels can be provided as locations for the inline
|
||||
assembly to jump to.
|
||||
@ -7170,15 +7163,10 @@ assembly to jump to.
|
||||
Example:
|
||||
""""""""
|
||||
|
||||
.. code-block:: llvm
|
||||
.. code-block:: text
|
||||
|
||||
; "asm goto" without output constraints.
|
||||
callbr void asm "", "r,X"(i32 %x, i8 *blockaddress(@foo, %indirect))
|
||||
to label %fallthrough [label %indirect]
|
||||
|
||||
; "asm goto" with output constraints.
|
||||
<result> = callbr i32 asm "", "=r,r,X"(i32 %x, i8 *blockaddress(@foo, %indirect))
|
||||
to label %fallthrough [label %indirect]
|
||||
callbr void asm "", "r,x"(i32 %x, i8 *blockaddress(@foo, %fail))
|
||||
to label %normal [label %fail]
|
||||
|
||||
.. _i_resume:
|
||||
|
||||
|
@ -129,9 +129,6 @@ private:
|
||||
/// Indicate that this basic block is the entry block of a cleanup funclet.
|
||||
bool IsCleanupFuncletEntry = false;
|
||||
|
||||
/// Indicate that this basic block is the indirect dest of an INLINEASM_BR.
|
||||
bool IsInlineAsmBrIndirectPad = false;
|
||||
|
||||
/// since getSymbol is a relatively heavy-weight operation, the symbol
|
||||
/// is only computed once and is cached.
|
||||
mutable MCSymbol *CachedMCSymbol = nullptr;
|
||||
@ -411,12 +408,6 @@ public:
|
||||
/// Indicates if this is the entry block of a cleanup funclet.
|
||||
void setIsCleanupFuncletEntry(bool V = true) { IsCleanupFuncletEntry = V; }
|
||||
|
||||
/// Returns true if this is the indirect dest of an INLINEASM_BR.
|
||||
bool isInlineAsmBrIndirectPad() const { return IsInlineAsmBrIndirectPad; }
|
||||
|
||||
/// Indicates if this is the indirect dest of an INLINEASM_BR.
|
||||
void setIsInlineAsmBrIndirectPad(bool V = true) { IsInlineAsmBrIndirectPad = V; }
|
||||
|
||||
/// Returns true if it is legal to hoist instructions into this block.
|
||||
bool isLegalToHoistInto() const;
|
||||
|
||||
|
@ -6419,6 +6419,9 @@ bool LLParser::ParseCallBr(Instruction *&Inst, PerFunctionState &PFS) {
|
||||
/*IsCall=*/true))
|
||||
return true;
|
||||
|
||||
if (isa<InlineAsm>(Callee) && !Ty->getReturnType()->isVoidTy())
|
||||
return Error(RetTypeLoc, "asm-goto outputs not supported");
|
||||
|
||||
// Set up the Attribute for the function.
|
||||
SmallVector<Value *, 8> Args;
|
||||
SmallVector<AttributeSet, 8> ArgAttrs;
|
||||
|
@ -1109,17 +1109,6 @@ bool MachineBasicBlock::canSplitCriticalEdge(
|
||||
if (Succ->isEHPad())
|
||||
return false;
|
||||
|
||||
// Splitting the critical edge to a callbr's indirect block isn't advised.
|
||||
// Don't do it in this generic function.
|
||||
if (Succ->hasAddressTaken())
|
||||
if (auto *cbr = dyn_cast<CallBrInst>(getBasicBlock()->getTerminator()))
|
||||
if (auto *bb = Succ->getBasicBlock())
|
||||
if (cbr->getDefaultDest() != bb)
|
||||
if (llvm::any_of(cbr->getIndirectDests(), [&](const BasicBlock *succ){
|
||||
return succ == bb;
|
||||
}))
|
||||
return false;
|
||||
|
||||
const MachineFunction *MF = getParent();
|
||||
|
||||
// Performance might be harmed on HW that implements branching using exec mask
|
||||
|
@ -633,30 +633,17 @@ MachineVerifier::visitMachineBasicBlockBefore(const MachineBasicBlock *MBB) {
|
||||
}
|
||||
|
||||
// Count the number of landing pad successors.
|
||||
SmallPtrSet<const MachineBasicBlock*, 4> LandingPadSuccs;
|
||||
for (const auto *succ : MBB->successors()) {
|
||||
if (succ->isEHPad())
|
||||
LandingPadSuccs.insert(succ);
|
||||
if (!FunctionBlocks.count(succ))
|
||||
SmallPtrSet<MachineBasicBlock*, 4> LandingPadSuccs;
|
||||
for (MachineBasicBlock::const_succ_iterator I = MBB->succ_begin(),
|
||||
E = MBB->succ_end(); I != E; ++I) {
|
||||
if ((*I)->isEHPad())
|
||||
LandingPadSuccs.insert(*I);
|
||||
if (!FunctionBlocks.count(*I))
|
||||
report("MBB has successor that isn't part of the function.", MBB);
|
||||
if (!MBBInfoMap[succ].Preds.count(MBB)) {
|
||||
if (!MBBInfoMap[*I].Preds.count(MBB)) {
|
||||
report("Inconsistent CFG", MBB);
|
||||
errs() << "MBB is not in the predecessor list of the successor "
|
||||
<< printMBBReference(*succ) << ".\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Count the number of INLINEASM_BR indirect pad successors.
|
||||
SmallPtrSet<const MachineBasicBlock*, 4> IndirectPadSuccs;
|
||||
for (const auto *succ : MBB->successors()) {
|
||||
if (succ->isInlineAsmBrIndirectPad())
|
||||
IndirectPadSuccs.insert(succ);
|
||||
if (!FunctionBlocks.count(succ))
|
||||
report("MBB has successor that isn't part of the function.", MBB);
|
||||
if (!MBBInfoMap[succ].Preds.count(MBB)) {
|
||||
report("Inconsistent CFG", MBB);
|
||||
errs() << "MBB is not in the predecessor list of the successor "
|
||||
<< printMBBReference(*succ) << ".\n";
|
||||
<< printMBBReference(*(*I)) << ".\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,13 +684,11 @@ MachineVerifier::visitMachineBasicBlockBefore(const MachineBasicBlock *MBB) {
|
||||
// It's possible that the block legitimately ends with a noreturn
|
||||
// call or an unreachable, in which case it won't actually fall
|
||||
// out the bottom of the function.
|
||||
} else if (MBB->succ_size() == LandingPadSuccs.size() ||
|
||||
MBB->succ_size() == IndirectPadSuccs.size()) {
|
||||
} else if (MBB->succ_size() == LandingPadSuccs.size()) {
|
||||
// It's possible that the block legitimately ends with a noreturn
|
||||
// call or an unreachable, in which case it won't actually fall
|
||||
// out of the block.
|
||||
} else if (MBB->succ_size() != 1 + LandingPadSuccs.size() &&
|
||||
MBB->succ_size() != 1 + IndirectPadSuccs.size()) {
|
||||
} else if (MBB->succ_size() != 1+LandingPadSuccs.size()) {
|
||||
report("MBB exits via unconditional fall-through but doesn't have "
|
||||
"exactly one CFG successor!", MBB);
|
||||
} else if (!MBB->isSuccessor(&*MBBI)) {
|
||||
@ -725,10 +710,7 @@ MachineVerifier::visitMachineBasicBlockBefore(const MachineBasicBlock *MBB) {
|
||||
// landingpad, accept it as valid control flow.
|
||||
if (MBB->succ_size() != 1+LandingPadSuccs.size() &&
|
||||
(MBB->succ_size() != 1 || LandingPadSuccs.size() != 1 ||
|
||||
*MBB->succ_begin() != *LandingPadSuccs.begin()) &&
|
||||
MBB->succ_size() != 1 + IndirectPadSuccs.size() &&
|
||||
(MBB->succ_size() != 1 || IndirectPadSuccs.size() != 1 ||
|
||||
*MBB->succ_begin() != *IndirectPadSuccs.begin())) {
|
||||
*MBB->succ_begin() != *LandingPadSuccs.begin())) {
|
||||
report("MBB exits via unconditional branch but doesn't have "
|
||||
"exactly one CFG successor!", MBB);
|
||||
} else if (!MBB->isSuccessor(TBB)) {
|
||||
@ -858,14 +840,8 @@ void MachineVerifier::visitMachineBundleBefore(const MachineInstr *MI) {
|
||||
if (!FirstTerminator)
|
||||
FirstTerminator = MI;
|
||||
} else if (FirstTerminator && !MI->isDebugEntryValue()) {
|
||||
// An "INLINEASM_BR" will fallthrough to the successor block executing any
|
||||
// "COPY" instructions that exist so that they can be assigned in the
|
||||
// fallthrough block..
|
||||
if (FirstTerminator->getOpcode() != TargetOpcode::INLINEASM_BR ||
|
||||
MI->getOpcode() != TargetOpcode::COPY) {
|
||||
report("Non-terminator instruction after the first terminator", MI);
|
||||
errs() << "First terminator was:\t" << *FirstTerminator;
|
||||
}
|
||||
report("Non-terminator instruction after the first terminator", MI);
|
||||
errs() << "First terminator was:\t" << *FirstTerminator;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2847,7 +2847,6 @@ void SelectionDAGBuilder::visitCallBr(const CallBrInst &I) {
|
||||
assert(isa<InlineAsm>(I.getCalledValue()) &&
|
||||
"Only know how to handle inlineasm callbr");
|
||||
visitInlineAsm(&I);
|
||||
CopyToExportRegsIfNeeded(&I);
|
||||
|
||||
// Retrieve successors.
|
||||
MachineBasicBlock *Return = FuncInfo.MBBMap[I.getDefaultDest()];
|
||||
@ -2857,7 +2856,6 @@ void SelectionDAGBuilder::visitCallBr(const CallBrInst &I) {
|
||||
for (unsigned i = 0, e = I.getNumIndirectDests(); i < e; ++i) {
|
||||
MachineBasicBlock *Target = FuncInfo.MBBMap[I.getIndirectDest(i)];
|
||||
addSuccessorWithProb(CallBrMBB, Target);
|
||||
Target->setIsInlineAsmBrIndirectPad();
|
||||
}
|
||||
CallBrMBB->normalizeSuccProbs();
|
||||
|
||||
|
@ -2501,6 +2501,8 @@ void Verifier::visitIndirectBrInst(IndirectBrInst &BI) {
|
||||
void Verifier::visitCallBrInst(CallBrInst &CBI) {
|
||||
Assert(CBI.isInlineAsm(), "Callbr is currently only used for asm-goto!",
|
||||
&CBI);
|
||||
Assert(CBI.getType()->isVoidTy(), "Callbr return value is not supported!",
|
||||
&CBI);
|
||||
for (unsigned i = 0, e = CBI.getNumSuccessors(); i != e; ++i)
|
||||
Assert(CBI.getSuccessor(i)->getType()->isLabelTy(),
|
||||
"Callbr successors must all have pointer type!", &CBI);
|
||||
|
@ -1,118 +1,18 @@
|
||||
; RUN: llc -mtriple=i686-- -verify-machineinstrs < %s | FileCheck %s
|
||||
; RUN: not llc -mtriple=i686-- < %s 2> %t
|
||||
; RUN: FileCheck %s < %t
|
||||
|
||||
; A test for asm-goto output
|
||||
; CHECK: error: asm-goto outputs not supported
|
||||
|
||||
; CHECK-LABEL: test1:
|
||||
; CHECK: movl 4(%esp), %eax
|
||||
; CHECK-NEXT: addl $4, %eax
|
||||
; CHECK-NEXT: #APP
|
||||
; CHECK-NEXT: xorl %eax, %eax
|
||||
; CHECK-NEXT: jmp .Ltmp0
|
||||
; CHECK-NEXT: #NO_APP
|
||||
; CHECK-NEXT: .LBB0_1:
|
||||
; CHECK-NEXT: retl
|
||||
; CHECK-NEXT: .Ltmp0:
|
||||
define i32 @test1(i32 %x) {
|
||||
; A test for asm-goto output prohibition
|
||||
|
||||
define i32 @test(i32 %a) {
|
||||
entry:
|
||||
%add = add nsw i32 %x, 4
|
||||
%ret = callbr i32 asm "xorl $1, $0; jmp ${2:l}", "=r,r,X,~{dirflag},~{fpsr},~{flags}"(i32 %add, i8* blockaddress(@test1, %abnormal))
|
||||
to label %normal [label %abnormal]
|
||||
%0 = add i32 %a, 4
|
||||
%1 = callbr i32 asm "xorl $1, $1; jmp ${1:l}", "=&r,r,X,~{dirflag},~{fpsr},~{flags}"(i32 %0, i8* blockaddress(@test, %fail)) to label %normal [label %fail]
|
||||
|
||||
normal:
|
||||
ret i32 %ret
|
||||
ret i32 %1
|
||||
|
||||
abnormal:
|
||||
fail:
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
; CHECK-LABEL: test2:
|
||||
; CHECK: # %bb.1: # %if.then
|
||||
; CHECK-NEXT: #APP
|
||||
; CHECK-NEXT: testl %esi, %esi
|
||||
; CHECK-NEXT: testl %edi, %esi
|
||||
; CHECK-NEXT: jne .Ltmp1
|
||||
; CHECK-NEXT: #NO_APP
|
||||
; CHECK-NEXT: .LBB1_2:
|
||||
; CHECK-NEXT: jmp .LBB1_4
|
||||
; CHECK-NEXT: .LBB1_3: # %if.else
|
||||
; CHECK-NEXT: #APP
|
||||
; CHECK-NEXT: testl %esi, %edi
|
||||
; CHECK-NEXT: testl %esi, %edi
|
||||
; CHECK-NEXT: jne .Ltmp2
|
||||
; CHECK-NEXT: #NO_APP
|
||||
; CHECK-NEXT: .LBB1_4:
|
||||
; CHECK-NEXT: movl %esi, %eax
|
||||
; CHECK-NEXT: addl %edi, %eax
|
||||
; CHECK-NEXT: jmp .LBB1_6
|
||||
; CHECK-NEXT: .Ltmp1:
|
||||
; CHECK-NEXT: .LBB1_5: # %label_true
|
||||
; CHECK-NEXT: movl $-2, %eax
|
||||
; CHECK-NEXT: .Ltmp2:
|
||||
; CHECK-NEXT: .LBB1_6: # %return
|
||||
define i32 @test2(i32 %out1, i32 %out2) {
|
||||
entry:
|
||||
%cmp = icmp slt i32 %out1, %out2
|
||||
br i1 %cmp, label %if.then, label %if.else
|
||||
|
||||
if.then: ; preds = %entry
|
||||
%0 = callbr { i32, i32 } asm sideeffect "testl $0, $0; testl $1, $2; jne ${5:l}", "={si},={di},r,0,1,X,X,~{dirflag},~{fpsr},~{flags}"(i32 %out1, i32 %out1, i32 %out2, i8* blockaddress(@test2, %label_true), i8* blockaddress(@test2, %return))
|
||||
to label %if.end [label %label_true, label %return]
|
||||
|
||||
if.else: ; preds = %entry
|
||||
%1 = callbr { i32, i32 } asm sideeffect "testl $0, $1; testl $2, $3; jne ${7:l}", "={si},={di},r,r,0,1,X,X,~{dirflag},~{fpsr},~{flags}"(i32 %out1, i32 %out2, i32 %out1, i32 %out2, i8* blockaddress(@test2, %label_true), i8* blockaddress(@test2, %return))
|
||||
to label %if.end [label %label_true, label %return]
|
||||
|
||||
if.end: ; preds = %if.else, %if.then
|
||||
%.sink11 = phi { i32, i32 } [ %0, %if.then ], [ %1, %if.else ]
|
||||
%asmresult3 = extractvalue { i32, i32 } %.sink11, 0
|
||||
%asmresult4 = extractvalue { i32, i32 } %.sink11, 1
|
||||
%add = add nsw i32 %asmresult4, %asmresult3
|
||||
br label %return
|
||||
|
||||
label_true: ; preds = %if.else, %if.then
|
||||
br label %return
|
||||
|
||||
return: ; preds = %if.then, %if.else, %label_true, %if.end
|
||||
%retval.0 = phi i32 [ %add, %if.end ], [ -2, %label_true ], [ -1, %if.else ], [ -1, %if.then ]
|
||||
ret i32 %retval.0
|
||||
}
|
||||
|
||||
; CHECK-LABEL: test3:
|
||||
; CHECK: # %bb.1: # %true
|
||||
; CHECK-NEXT: #APP
|
||||
; CHECK-NEXT: .short %esi
|
||||
; CHECK-NEXT: .short %edi
|
||||
; CHECK-NEXT: #NO_APP
|
||||
; CHECK-NEXT: .LBB2_2:
|
||||
; CHECK-NEXT: movl %edi, %eax
|
||||
; CHECK-NEXT: jmp .LBB2_5
|
||||
; CHECK-NEXT: .LBB2_3: # %false
|
||||
; CHECK-NEXT: #APP
|
||||
; CHECK-NEXT: .short %eax
|
||||
; CHECK-NEXT: .short %edx
|
||||
; CHECK-NEXT: #NO_APP
|
||||
; CHECK-NEXT: .LBB2_4:
|
||||
; CHECK-NEXT: movl %edx, %eax
|
||||
; CHECK-NEXT: jmp .LBB2_5
|
||||
; CHECK-NEXT: .Ltmp3: # Block address taken
|
||||
; CHECK-NEXT: .LBB2_6: # %indirect
|
||||
; CHECK-NEXT: movl $42, %eax
|
||||
; CHECK-NEXT: .LBB2_5: # %asm.fallthrough
|
||||
define i32 @test3(i1 %cmp) {
|
||||
entry:
|
||||
br i1 %cmp, label %true, label %false
|
||||
|
||||
true:
|
||||
%0 = callbr { i32, i32 } asm sideeffect ".word $0, $1", "={si},={di},X" (i8* blockaddress(@test3, %indirect)) to label %asm.fallthrough [label %indirect]
|
||||
|
||||
false:
|
||||
%1 = callbr { i32, i32 } asm sideeffect ".word $0, $1", "={ax},={dx},X" (i8* blockaddress(@test3, %indirect)) to label %asm.fallthrough [label %indirect]
|
||||
|
||||
asm.fallthrough:
|
||||
%vals = phi { i32, i32 } [ %0, %true ], [ %1, %false ]
|
||||
%v = extractvalue { i32, i32 } %vals, 1
|
||||
ret i32 %v
|
||||
|
||||
indirect:
|
||||
ret i32 42
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
||||
; RUN: llc < %s -mtriple=i686-- -O3 -verify-machineinstrs | FileCheck %s
|
||||
; RUN: llc < %s -mtriple=i686-- -O3 | FileCheck %s
|
||||
|
||||
; Tests for using callbr as an asm-goto wrapper
|
||||
|
||||
@ -159,54 +159,3 @@ quux: ; preds = %asm.fallthrough, %e
|
||||
cleanup: ; preds = %asm.fallthrough, %quux
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @test5(i32 %out1, i32 %out2) {
|
||||
; Test 5 - asm-goto with output constraints.
|
||||
; CHECK-LABEL: test5:
|
||||
; CHECK: # %bb.0: # %entry
|
||||
; CHECK-NEXT: movl $-1, %eax
|
||||
; CHECK-NEXT: movl 4(%esp), %ecx
|
||||
; CHECK-NEXT: #APP
|
||||
; CHECK-NEXT: testl %ecx, %ecx
|
||||
; CHECK-NEXT: testl %edx, %ecx
|
||||
; CHECK-NEXT: jne .Ltmp6
|
||||
; CHECK-NEXT: #NO_APP
|
||||
; CHECK-NEXT: .LBB4_1:
|
||||
; CHECK-NEXT: #APP
|
||||
; CHECK-NEXT: testl %ecx, %edx
|
||||
; CHECK-NEXT: testl %ecx, %edx
|
||||
; CHECK-NEXT: jne .Ltmp7
|
||||
; CHECK-NEXT: #NO_APP
|
||||
; CHECK-NEXT: .LBB4_2:
|
||||
; CHECK-NEXT: addl %edx, %ecx
|
||||
; CHECK-NEXT: movl %ecx, %eax
|
||||
; CHECK-NEXT: retl
|
||||
; CHECK-NEXT: .Ltmp6:
|
||||
; CHECK-NEXT: .LBB4_3:
|
||||
; CHECK-NEXT: movl $-2, %eax
|
||||
; CHECK-NEXT: .Ltmp7:
|
||||
; CHECK-NEXT: .LBB4_4:
|
||||
; CHECK-NEXT: retl
|
||||
entry:
|
||||
%0 = callbr { i32, i32 } asm sideeffect "testl $0, $0; testl $1, $2; jne ${3:l}", "=r,=r,r,X,X,~{dirflag},~{fpsr},~{flags}"(i32 %out1, i8* blockaddress(@test5, %label_true), i8* blockaddress(@test5, %return))
|
||||
to label %asm.fallthrough [label %label_true, label %return]
|
||||
|
||||
asm.fallthrough: ; preds = %entry
|
||||
%asmresult = extractvalue { i32, i32 } %0, 0
|
||||
%asmresult1 = extractvalue { i32, i32 } %0, 1
|
||||
%1 = callbr { i32, i32 } asm sideeffect "testl $0, $1; testl $2, $3; jne ${5:l}", "=r,=r,r,r,X,X,~{dirflag},~{fpsr},~{flags}"(i32 %asmresult, i32 %asmresult1, i8* blockaddress(@test5, %label_true), i8* blockaddress(@test5, %return))
|
||||
to label %asm.fallthrough2 [label %label_true, label %return]
|
||||
|
||||
asm.fallthrough2: ; preds = %asm.fallthrough
|
||||
%asmresult3 = extractvalue { i32, i32 } %1, 0
|
||||
%asmresult4 = extractvalue { i32, i32 } %1, 1
|
||||
%add = add nsw i32 %asmresult3, %asmresult4
|
||||
br label %return
|
||||
|
||||
label_true: ; preds = %asm.fallthrough, %entry
|
||||
br label %return
|
||||
|
||||
return: ; preds = %entry, %asm.fallthrough, %label_true, %asm.fallthrough2
|
||||
%retval.0 = phi i32 [ %add, %asm.fallthrough2 ], [ -2, %label_true ], [ -1, %asm.fallthrough ], [ -1, %entry ]
|
||||
ret i32 %retval.0
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user