[llvm] 23c2a5c - Allow "callbr" to return non-void values

Bill Wendling via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 24 18:30:02 PST 2020


Author: Bill Wendling
Date: 2020-02-24T18:29:06-08:00
New Revision: 23c2a5ce33f0f05e4cc43c2cbb9009e9b549839c

URL: https://github.com/llvm/llvm-project/commit/23c2a5ce33f0f05e4cc43c2cbb9009e9b549839c
DIFF: https://github.com/llvm/llvm-project/commit/23c2a5ce33f0f05e4cc43c2cbb9009e9b549839c.diff

LOG: Allow "callbr" to return non-void values

Summary:
Terminators in LLVM aren't prohibited from returning values. This means that
the "callbr" instruction, which is used for "asm goto", can support "asm goto
with outputs."

This patch removes all restrictions against "callbr" returning values. The
heavy lifting is done by the code generator. The "INLINEASM_BR" instruction's
a terminator, and the code generator doesn't allow non-terminator instructions
after a terminator. In order to correctly model the feature, we need to copy
outputs from "INLINEASM_BR" into virtual registers. Of course, those copies
aren't terminators.

To get around this issue, we split the block containing the "INLINEASM_BR"
right before the "COPY" instructions. This results in two cheats:

  - Any physical registers defined by "INLINEASM_BR" need to be marked as
    live-in into the block with the "COPY" instructions. This violates an
    assumption that physical registers aren't marked as "live-in" until after
    register allocation. But it seems as if the live-in information only
    needs to be correct after register allocation. So we're able to get away
    with this.

  - The indirect branches from the "INLINEASM_BR" are moved to the "COPY"
    block. This is to satisfy PHI nodes.

I've been told that MLIR can support this handily, but until we're able to
use it, we'll have to stick with the above.

Reviewers: jyknight, nickdesaulniers, hfinkel, MaskRay, lattner

Reviewed By: nickdesaulniers, MaskRay, lattner

Subscribers: rriddle, qcolombet, jdoerfert, MatzeB, echristo, MaskRay, xbolva00, aaron.ballman, cfe-commits, JonChesterfield, hiraditya, llvm-commits, rnk, craig.topper

Tags: #llvm, #clang

Differential Revision: https://reviews.llvm.org/D69868

Added: 
    

Modified: 
    llvm/docs/LangRef.rst
    llvm/include/llvm/CodeGen/MachineBasicBlock.h
    llvm/lib/AsmParser/LLParser.cpp
    llvm/lib/CodeGen/MachineBasicBlock.cpp
    llvm/lib/CodeGen/MachineVerifier.cpp
    llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
    llvm/lib/IR/Verifier.cpp
    llvm/test/CodeGen/X86/callbr-asm-outputs.ll
    llvm/test/CodeGen/X86/callbr-asm.ll

Removed: 
    


################################################################################
diff  --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 43dbc70d2fd1..5ad97fb87267 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -7271,14 +7271,14 @@ Syntax:
 ::
 
       <result> = callbr [cconv] [ret attrs] [addrspace(<num>)] <ty>|<fnty> <fnptrval>(<function args>) [fn attrs]
-                    [operand bundles] to label <normal label> [other labels]
+                    [operand bundles] to label <fallthrough label> [indirect labels]
 
 Overview:
 """""""""
 
 The '``callbr``' instruction causes control to transfer to a specified
 function, with the possibility of control flow transfer to either the
-'``normal``' label or one of the '``other``' labels.
+'``fallthrough``' label or one of the '``indirect``' 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.
@@ -7305,17 +7305,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
-   indirect ``callbr``'s are just as possible, calling an arbitrary pointer
+   other ``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.
-#. '``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
+#. '``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
    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.
@@ -7328,6 +7328,10 @@ instruction in most regards. The primary 
diff erence 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>`.
+
 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.
@@ -7335,10 +7339,15 @@ assembly to jump to.
 Example:
 """"""""
 
-.. code-block:: text
+.. code-block:: llvm
+
+      ; "asm goto" without output constraints.
+      callbr void asm "", "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]
+      ; "asm goto" with output constraints.
+      <result> = callbr i32 asm "", "=r,r,X"(i32 %x, i8 *blockaddress(@foo, %indirect))
+                  to label %fallthrough [label %indirect]
 
 .. _i_resume:
 

diff  --git a/llvm/include/llvm/CodeGen/MachineBasicBlock.h b/llvm/include/llvm/CodeGen/MachineBasicBlock.h
index c964f4284ae5..8f2f4bb34a33 100644
--- a/llvm/include/llvm/CodeGen/MachineBasicBlock.h
+++ b/llvm/include/llvm/CodeGen/MachineBasicBlock.h
@@ -130,6 +130,12 @@ class MachineBasicBlock
   /// Indicate that this basic block is the entry block of a cleanup funclet.
   bool IsCleanupFuncletEntry = false;
 
+  /// Default target of the callbr of a basic block.
+  bool InlineAsmBrDefaultTarget = false;
+
+  /// List of indirect targets of the callbr of a basic block.
+  SmallPtrSet<const MachineBasicBlock*, 4> InlineAsmBrIndirectTargets;
+
   /// since getSymbol is a relatively heavy-weight operation, the symbol
   /// is only computed once and is cached.
   mutable MCSymbol *CachedMCSymbol = nullptr;
@@ -409,6 +415,33 @@ class MachineBasicBlock
   /// 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 isInlineAsmBrIndirectTarget(const MachineBasicBlock *Tgt) const {
+    return InlineAsmBrIndirectTargets.count(Tgt);
+  }
+
+  /// Indicates if this is the indirect dest of an INLINEASM_BR.
+  void addInlineAsmBrIndirectTarget(const MachineBasicBlock *Tgt) {
+    InlineAsmBrIndirectTargets.insert(Tgt);
+  }
+
+  /// Transfers indirect targets to INLINEASM_BR's copy block.
+  void transferInlineAsmBrIndirectTargets(MachineBasicBlock *CopyBB) {
+    for (auto *Target : InlineAsmBrIndirectTargets)
+      CopyBB->addInlineAsmBrIndirectTarget(Target);
+    return InlineAsmBrIndirectTargets.clear();
+  }
+
+  /// Returns true if this is the default dest of an INLINEASM_BR.
+  bool isInlineAsmBrDefaultTarget() const {
+    return InlineAsmBrDefaultTarget;
+  }
+
+  /// Indicates if this is the default deft of an INLINEASM_BR.
+  void setInlineAsmBrDefaultTarget() {
+    InlineAsmBrDefaultTarget = true;
+  }
+
   /// Returns true if it is legal to hoist instructions into this block.
   bool isLegalToHoistInto() const;
 

diff  --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 096fa6a9a5b5..a9ccc350052c 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -6423,9 +6423,6 @@ 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;

diff  --git a/llvm/lib/CodeGen/MachineBasicBlock.cpp b/llvm/lib/CodeGen/MachineBasicBlock.cpp
index ffa68aa14fbb..c2f459e313cc 100644
--- a/llvm/lib/CodeGen/MachineBasicBlock.cpp
+++ b/llvm/lib/CodeGen/MachineBasicBlock.cpp
@@ -1113,8 +1113,12 @@ bool MachineBasicBlock::canSplitCriticalEdge(
   if (Succ->isEHPad())
     return false;
 
-  const MachineFunction *MF = getParent();
+  // Splitting the critical edge to a callbr's indirect block isn't advised.
+  // Don't do it in this generic function.
+  if (isInlineAsmBrIndirectTarget(Succ))
+    return false;
 
+  const MachineFunction *MF = getParent();
   // Performance might be harmed on HW that implements branching using exec mask
   // where both sides of the branches are always executed.
   if (MF->getTarget().requiresStructuredCFG())

diff  --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index af3b8fd5db6e..69ecb45a317c 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -622,6 +622,7 @@ MachineVerifier::visitMachineBasicBlockBefore(const MachineBasicBlock *MBB) {
     // it is an entry block or landing pad.
     for (const auto &LI : MBB->liveins()) {
       if (isAllocatable(LI.PhysReg) && !MBB->isEHPad() &&
+          !MBB->isInlineAsmBrDefaultTarget() &&
           MBB->getIterator() != MBB->getParent()->begin()) {
         report("MBB has allocatable live-in, but isn't entry or landing-pad.", MBB);
         report_context(LI.PhysReg);
@@ -630,17 +631,30 @@ MachineVerifier::visitMachineBasicBlockBefore(const MachineBasicBlock *MBB) {
   }
 
   // Count the number of landing pad successors.
-  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))
+  SmallPtrSet<const MachineBasicBlock*, 4> LandingPadSuccs;
+  for (const auto *succ : MBB->successors()) {
+    if (succ->isEHPad())
+      LandingPadSuccs.insert(succ);
+    if (!FunctionBlocks.count(succ))
       report("MBB has successor that isn't part of the function.", MBB);
-    if (!MBBInfoMap[*I].Preds.count(MBB)) {
+    if (!MBBInfoMap[succ].Preds.count(MBB)) {
       report("Inconsistent CFG", MBB);
       errs() << "MBB is not in the predecessor list of the successor "
-             << printMBBReference(*(*I)) << ".\n";
+             << printMBBReference(*succ) << ".\n";
+    }
+  }
+
+  // Count the number of INLINEASM_BR indirect target successors.
+  SmallPtrSet<const MachineBasicBlock*, 4> IndirectTargetSuccs;
+  for (const auto *succ : MBB->successors()) {
+    if (MBB->isInlineAsmBrIndirectTarget(succ))
+      IndirectTargetSuccs.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";
     }
   }
 
@@ -681,11 +695,15 @@ 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()) {
+      } else if (MBB->succ_size() == LandingPadSuccs.size() ||
+                 MBB->succ_size() == IndirectTargetSuccs.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()) {
+      } else if ((LandingPadSuccs.size() &&
+                  MBB->succ_size() != 1 + LandingPadSuccs.size()) ||
+                 (IndirectTargetSuccs.size() &&
+                  MBB->succ_size() != 1 + IndirectTargetSuccs.size())) {
         report("MBB exits via unconditional fall-through but doesn't have "
                "exactly one CFG successor!", MBB);
       } else if (!MBB->isSuccessor(&*MBBI)) {
@@ -707,7 +725,10 @@ 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_begin() != *LandingPadSuccs.begin()) &&
+          MBB->succ_size() != 1 + IndirectTargetSuccs.size() &&
+          (MBB->succ_size() != 1 || IndirectTargetSuccs.size() != 1 ||
+           *MBB->succ_begin() != *IndirectTargetSuccs.begin())) {
         report("MBB exits via unconditional branch but doesn't have "
                "exactly one CFG successor!", MBB);
       } else if (!MBB->isSuccessor(TBB)) {

diff  --git a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp
index 7bded0005a51..cf72770a9a1a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp
@@ -1027,6 +1027,69 @@ EmitSchedule(MachineBasicBlock::iterator &InsertPos) {
     }
   }
 
+  // Split after an INLINEASM_BR block with outputs. This allows us to keep the
+  // copy to/from register instructions from being between two terminator
+  // instructions, which causes the machine instruction verifier agita.
+  auto TI = llvm::find_if(*BB, [](const MachineInstr &MI){
+    return MI.getOpcode() == TargetOpcode::INLINEASM_BR;
+  });
+  auto SplicePt = TI != BB->end() ? std::next(TI) : BB->end();
+  if (TI != BB->end() && SplicePt != BB->end() &&
+      TI->getOpcode() == TargetOpcode::INLINEASM_BR &&
+      SplicePt->getOpcode() == TargetOpcode::COPY) {
+    MachineBasicBlock *FallThrough = BB->getFallThrough();
+    if (!FallThrough)
+      for (const MachineOperand &MO : BB->back().operands())
+        if (MO.isMBB()) {
+          FallThrough = MO.getMBB();
+          break;
+        }
+    assert(FallThrough && "Cannot find default dest block for callbr!");
+
+    MachineBasicBlock *CopyBB = MF.CreateMachineBasicBlock(BB->getBasicBlock());
+    MachineFunction::iterator BBI(*BB);
+    MF.insert(++BBI, CopyBB);
+
+    CopyBB->splice(CopyBB->begin(), BB, SplicePt, BB->end());
+    CopyBB->setInlineAsmBrDefaultTarget();
+
+    CopyBB->addSuccessor(FallThrough, BranchProbability::getOne());
+    BB->addSuccessor(CopyBB, BranchProbability::getOne());
+
+    // Mark all physical registers defined in the original block as being live
+    // on entry to the copy block.
+    for (const auto &MI : *CopyBB)
+      for (const MachineOperand &MO : MI.operands())
+        if (MO.isReg()) {
+          Register reg = MO.getReg();
+          if (Register::isPhysicalRegister(reg)) {
+            CopyBB->addLiveIn(reg);
+            break;
+          }
+        }
+
+    // Bit of a hack: The copy block we created here exists only because we want
+    // the CFG to work with the current system. However, the successors to the
+    // block with the INLINEASM_BR instruction expect values to come from *that*
+    // block, not this usurper block. Thus we steal its successors and add them
+    // to the copy so that everyone is happy.
+    for (auto *Succ : BB->successors())
+      if (Succ != CopyBB && !CopyBB->isSuccessor(Succ))
+        CopyBB->addSuccessor(Succ, BranchProbability::getZero());
+
+    for (auto *Succ : CopyBB->successors())
+      if (BB->isSuccessor(Succ))
+        BB->removeSuccessor(Succ);
+
+    CopyBB->normalizeSuccProbs();
+    BB->normalizeSuccProbs();
+
+    BB->transferInlineAsmBrIndirectTargets(CopyBB);
+
+    InsertPos = CopyBB->end();
+    return CopyBB;
+  }
+
   InsertPos = Emitter.getInsertPos();
   return Emitter.getBlock();
 }

diff  --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 002610ea24f2..6582c0cd4f29 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -2860,15 +2860,18 @@ 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()];
+  Return->setInlineAsmBrDefaultTarget();
 
   // Update successor info.
   addSuccessorWithProb(CallBrMBB, Return);
   for (unsigned i = 0, e = I.getNumIndirectDests(); i < e; ++i) {
     MachineBasicBlock *Target = FuncInfo.MBBMap[I.getIndirectDest(i)];
     addSuccessorWithProb(CallBrMBB, Target);
+    CallBrMBB->addInlineAsmBrIndirectTarget(Target);
   }
   CallBrMBB->normalizeSuccProbs();
 
@@ -8175,6 +8178,7 @@ void SelectionDAGBuilder::visitInlineAsm(ImmutableCallSite CS) {
 
   unsigned ArgNo = 0;   // ArgNo - The argument of the CallInst.
   unsigned ResNo = 0;   // ResNo - The result number of the next output.
+  unsigned NumMatchingOps = 0;
   for (auto &T : TargetConstraints) {
     ConstraintOperands.push_back(SDISelAsmOperandInfo(T));
     SDISelAsmOperandInfo &OpInfo = ConstraintOperands.back();
@@ -8188,8 +8192,12 @@ void SelectionDAGBuilder::visitInlineAsm(ImmutableCallSite CS) {
       // only in asm's.
       const Instruction *I = CS.getInstruction();
       if (isa<CallBrInst>(I) &&
-          (ArgNo - 1) >= (cast<CallBrInst>(I)->getNumArgOperands() -
-                          cast<CallBrInst>(I)->getNumIndirectDests())) {
+          ArgNo - 1 >= (cast<CallBrInst>(I)->getNumArgOperands() -
+                        cast<CallBrInst>(I)->getNumIndirectDests() -
+                        NumMatchingOps) &&
+          (NumMatchingOps == 0 ||
+           ArgNo - 1 < (cast<CallBrInst>(I)->getNumArgOperands() -
+                        NumMatchingOps))) {
         const auto *BA = cast<BlockAddress>(OpInfo.CallOperandVal);
         EVT VT = TLI.getValueType(DAG.getDataLayout(), BA->getType(), true);
         OpInfo.CallOperand = DAG.getTargetBlockAddress(BA, VT);
@@ -8220,6 +8228,9 @@ void SelectionDAGBuilder::visitInlineAsm(ImmutableCallSite CS) {
       OpInfo.ConstraintVT = MVT::Other;
     }
 
+    if (OpInfo.hasMatchingInput())
+      ++NumMatchingOps;
+
     if (!HasSideEffect)
       HasSideEffect = OpInfo.hasMemory(TLI);
 

diff  --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index fefb3a4751c3..ad97ae87fbbe 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2533,8 +2533,6 @@ 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);

diff  --git a/llvm/test/CodeGen/X86/callbr-asm-outputs.ll b/llvm/test/CodeGen/X86/callbr-asm-outputs.ll
index 7595deba3364..cdf742bb6ba2 100644
--- a/llvm/test/CodeGen/X86/callbr-asm-outputs.ll
+++ b/llvm/test/CodeGen/X86/callbr-asm-outputs.ll
@@ -1,18 +1,160 @@
-; RUN: not llc -mtriple=i686-- < %s 2> %t
-; RUN: FileCheck %s < %t
+; RUN: llc -mtriple=i686-- -verify-machineinstrs < %s | FileCheck %s
 
-; CHECK: error: asm-goto outputs not supported
+; A test for asm-goto output
 
-; A test for asm-goto output prohibition
-
-define i32 @test(i32 %a) {
+; 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-LABEL: .Ltmp0: # Address of block that was removed by CodeGen
+define i32 @test1(i32 %x) {
 entry:
-  %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]
+  %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]
 
 normal:
-  ret i32 %1
+  ret i32 %ret
 
-fail:
+abnormal:
   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:  .Ltmp2:
+; CHECK-NEXT:  # %bb.5: # %return
+; CHECK-LABEL: .Ltmp1: # Address of block that was removed by CodeGen
+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 ${3:l}", "={si},={di},r,X,X,0,1,~{dirflag},~{fpsr},~{flags}"(i32 %out1, i8* blockaddress(@test2, %label_true), i8* blockaddress(@test2, %return), i32 %out1, i32 %out2)
+          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 ${5:l}", "={si},={di},r,r,X,X,0,1,~{dirflag},~{fpsr},~{flags}"(i32 %out1, i32 %out2, i8* blockaddress(@test2, %label_true), i8* blockaddress(@test2, %return), i32 %out1, i32 %out2)
+          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:  .LBB2_5: # %asm.fallthrough
+; CHECK-LABEL: .Ltmp3: # Address of block that was removed by CodeGen
+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
+}
+
+; Test 4 - asm-goto with output constraints.
+; CHECK-LABEL: test4:
+; 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 .Ltmp4
+; CHECK-NEXT:      #NO_APP
+; CHECK-NEXT:  .LBB3_1:
+; CHECK-NEXT:      #APP
+; CHECK-NEXT:      testl %ecx, %edx
+; CHECK-NEXT:      testl %ecx, %edx
+; CHECK-NEXT:      jne .Ltmp5
+; CHECK-NEXT:      #NO_APP
+; CHECK-NEXT:  .LBB3_2:
+; CHECK-NEXT:      addl %edx, %ecx
+; CHECK-NEXT:      movl %ecx, %eax
+; CHECK-NEXT:  .Ltmp5:
+; CHECK-NEXT:  # %bb.3: # %return
+; CHECK-NEXT:      retl
+; CHECK-LABEL: .Ltmp4: # Address of block that was removed by CodeGen
+define i32 @test4(i32 %out1, i32 %out2) {
+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(@test4, %label_true), i8* blockaddress(@test4, %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(@test4, %label_true), i8* blockaddress(@test4, %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
+}

diff  --git a/llvm/test/CodeGen/X86/callbr-asm.ll b/llvm/test/CodeGen/X86/callbr-asm.ll
index 8938aefca087..23ea7a267fb8 100644
--- a/llvm/test/CodeGen/X86/callbr-asm.ll
+++ b/llvm/test/CodeGen/X86/callbr-asm.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -mtriple=i686-- -O3 | FileCheck %s
+; RUN: llc < %s -mtriple=i686-- -O3 -verify-machineinstrs | FileCheck %s
 
 ; Tests for using callbr as an asm-goto wrapper
 


        


More information about the llvm-commits mailing list