treat conditional branches the same way as conditional moves (giving them
authorChris Lattner <sabre@nondot.org>
Tue, 31 Jan 2006 06:56:30 +0000 (06:56 +0000)
committerChris Lattner <sabre@nondot.org>
Tue, 31 Jan 2006 06:56:30 +0000 (06:56 +0000)
an operand that contains the condcode), making things significantly simpler.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@25840 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/Sparc/Sparc.h
lib/Target/Sparc/SparcISelDAGToDAG.cpp
lib/Target/Sparc/SparcInstrInfo.td
lib/Target/SparcV8/SparcV8.h
lib/Target/SparcV8/SparcV8ISelDAGToDAG.cpp
lib/Target/SparcV8/SparcV8InstrInfo.td

index 15b80504704b5e624da318d0c2d4745516ac431c..faf160b16cd2e3d15b116c8895a242ef5ef0d812 100644 (file)
@@ -82,40 +82,6 @@ namespace llvm {
     };
   }
   
-  static unsigned SPARCCondCodeToBranchInstr(V8CC::CondCodes CC) {
-    switch (CC) {
-    default: assert(0 && "Unknown condition code");
-    case V8CC::ICC_NE:  return V8::BNE;
-    case V8CC::ICC_E:   return V8::BE;
-    case V8CC::ICC_G:   return V8::BG;
-    case V8CC::ICC_LE:  return V8::BLE;
-    case V8CC::ICC_GE:  return V8::BGE;
-    case V8CC::ICC_L:   return V8::BL;
-    case V8CC::ICC_GU:  return V8::BGU;
-    case V8CC::ICC_LEU: return V8::BLEU;
-    case V8CC::ICC_CC:  return V8::BCC;
-    case V8CC::ICC_CS:  return V8::BCS;
-    case V8CC::ICC_POS: return V8::BPOS;
-    case V8CC::ICC_NEG: return V8::BNEG;
-    case V8CC::ICC_VC:  return V8::BVC;
-    case V8CC::ICC_VS:  return V8::BVS;
-    case V8CC::FCC_U:   return V8::FBU;
-    case V8CC::FCC_G:   return V8::FBG;
-    case V8CC::FCC_UG:  return V8::FBUG;
-    case V8CC::FCC_L:   return V8::FBL;
-    case V8CC::FCC_UL:  return V8::FBUL;
-    case V8CC::FCC_LG:  return V8::FBLG;
-    case V8CC::FCC_NE:  return V8::FBNE;
-    case V8CC::FCC_E:   return V8::FBE;
-    case V8CC::FCC_UE:  return V8::FBUE;
-    case V8CC::FCC_GE:  return V8::FBGE;
-    case V8CC::FCC_UGE: return V8::FBUGE;
-    case V8CC::FCC_LE:  return V8::FBLE;
-    case V8CC::FCC_ULE: return V8::FBULE;
-    case V8CC::FCC_O:   return V8::FBO;
-    }       
-  }
-  
   static const char *SPARCCondCodeToString(V8CC::CondCodes CC) {
     switch (CC) {
     default: assert(0 && "Unknown condition code");
index ff4e861e18353d14fa5510143eae538d177c6b72..520849e6645f1243a5989304c5d9ffd90b094692 100644 (file)
@@ -817,19 +817,22 @@ MachineBasicBlock *
 SparcV8TargetLowering::InsertAtEndOfBasicBlock(MachineInstr *MI,
                                                MachineBasicBlock *BB) {
   unsigned BROpcode;
+  unsigned CC;
   // Figure out the conditional branch opcode to use for this select_cc.
   switch (MI->getOpcode()) {
   default: assert(0 && "Unknown SELECT_CC!");
   case V8::SELECT_CC_Int_ICC:
   case V8::SELECT_CC_FP_ICC:
   case V8::SELECT_CC_DFP_ICC:
+    BROpcode = V8::BCOND;
   case V8::SELECT_CC_Int_FCC:
   case V8::SELECT_CC_FP_FCC:
   case V8::SELECT_CC_DFP_FCC:
-    V8CC::CondCodes CC = (V8CC::CondCodes)MI->getOperand(3).getImmedValue();
-    BROpcode = SPARCCondCodeToBranchInstr(CC);
+    BROpcode = V8::FBCOND;
     break;
   }
+
+  CC = (V8CC::CondCodes)MI->getOperand(3).getImmedValue();
   
   // To "insert" a SELECT_CC instruction, we actually have to insert the diamond
   // control-flow pattern.  The incoming instruction knows the destination vreg
@@ -847,7 +850,7 @@ SparcV8TargetLowering::InsertAtEndOfBasicBlock(MachineInstr *MI,
   MachineBasicBlock *thisMBB = BB;
   MachineBasicBlock *copy0MBB = new MachineBasicBlock(LLVM_BB);
   MachineBasicBlock *sinkMBB = new MachineBasicBlock(LLVM_BB);
-  BuildMI(BB, BROpcode, 1).addMBB(sinkMBB);
+  BuildMI(BB, BROpcode, 2).addMBB(sinkMBB).addImm(CC);
   MachineFunction *F = BB->getParent();
   F->getBasicBlockList().insert(It, copy0MBB);
   F->getBasicBlockList().insert(It, sinkMBB);
index 72f6e3335e735664020f274f8ec9380bd4b745c3..7cf61e47f312a33ac461363a6a5b868eb0989245 100644 (file)
@@ -136,9 +136,7 @@ def retflag       : SDNode<"V8ISD::RET_FLAG", SDT_V8RetFlag,
 
 // Note that these values must be kept in sync with the V8CC::CondCode enum
 // values.
-class ICC_VAL<int N> : PatLeaf<(i32 N)> {
- int ICCVal = N;
-}
+class ICC_VAL<int N> : PatLeaf<(i32 N)>;
 def ICC_NE  : ICC_VAL< 9>;  // Not Equal
 def ICC_E   : ICC_VAL< 1>;  // Equal
 def ICC_G   : ICC_VAL<10>;  // Greater
@@ -154,9 +152,7 @@ def ICC_NEG : ICC_VAL< 6>;  // Negative
 def ICC_VC  : ICC_VAL<15>;  // Overflow Clear
 def ICC_VS  : ICC_VAL< 7>;  // Overflow Set
 
-class FCC_VAL<int N> : PatLeaf<(i32 N)> {
-  int FCCVal = N;
-}
+class FCC_VAL<int N> : PatLeaf<(i32 N)>;
 def FCC_U   : FCC_VAL<23>;  // Unordered
 def FCC_G   : FCC_VAL<22>;  // Greater
 def FCC_UG  : FCC_VAL<21>;  // Unordered or Greater
@@ -550,49 +546,11 @@ let isBarrier = 1 in
   def BA   : BranchV8<0b1000, (ops brtarget:$dst),
                       "ba $dst",
                       [(br bb:$dst)]>;
-def BNE  : BranchV8<0b1001, (ops brtarget:$dst),
-                    "bne $dst",
-                    [(V8bricc bb:$dst, ICC_NE, ICC)]>;
-def BE   : BranchV8<0b0001, (ops brtarget:$dst),
-                    "be $dst",
-                    [(V8bricc bb:$dst, ICC_E, ICC)]>;
-def BG   : BranchV8<0b1010, (ops brtarget:$dst),
-                    "bg $dst",
-                    [(V8bricc bb:$dst, ICC_G, ICC)]>;
-def BLE  : BranchV8<0b0010, (ops brtarget:$dst),
-                    "ble $dst",
-                    [(V8bricc bb:$dst, ICC_LE, ICC)]>;
-def BGE  : BranchV8<0b1011, (ops brtarget:$dst),
-                    "bge $dst",
-                    [(V8bricc bb:$dst, ICC_GE, ICC)]>;
-def BL   : BranchV8<0b0011, (ops brtarget:$dst),
-                    "bl $dst",
-                    [(V8bricc bb:$dst, ICC_L, ICC)]>;
-def BGU  : BranchV8<0b1100, (ops brtarget:$dst),
-                    "bgu $dst",
-                    [(V8bricc bb:$dst, ICC_GU, ICC)]>;
-def BLEU : BranchV8<0b0100, (ops brtarget:$dst),
-                    "bleu $dst",
-                    [(V8bricc bb:$dst, ICC_LEU, ICC)]>;
-def BCC  : BranchV8<0b1101, (ops brtarget:$dst),
-                    "bcc $dst",
-                    [(V8bricc bb:$dst, ICC_CC, ICC)]>;
-def BCS  : BranchV8<0b0101, (ops brtarget:$dst),
-                    "bcs $dst",
-                    [(V8bricc bb:$dst, ICC_CS, ICC)]>;
-def BPOS : BranchV8<0b1110, (ops brtarget:$dst),
-                    "bpos $dst",
-                    [(V8bricc bb:$dst, ICC_POS, ICC)]>;
-def BNEG : BranchV8<0b0110, (ops brtarget:$dst),
-                    "bneg $dst",
-                    [(V8bricc bb:$dst, ICC_NEG, ICC)]>;
-def BVC  : BranchV8<0b1111, (ops brtarget:$dst),
-                    "bvc $dst",
-                    [(V8bricc bb:$dst, ICC_VC, ICC)]>;
-def BVS  : BranchV8<0b0111, (ops brtarget:$dst),
-                    "bvs $dst",
-                    [(V8bricc bb:$dst, ICC_VS, ICC)]>;
-
+                      
+// FIXME: the encoding for the JIT should look at the condition field.
+def BCOND : BranchV8<0, (ops brtarget:$dst, V8CC:$cc),
+                     "b$cc $dst",
+                     [(V8bricc bb:$dst, imm:$cc, ICC)]>;
 
 
 // Section B.22 - Branch on Floating-point Condition Codes Instructions, p. 121
@@ -606,49 +564,10 @@ class FPBranchV8<bits<4> cc, dag ops, string asmstr, list<dag> pattern>
   let noResults = 1;
 }
 
-def FBU  : FPBranchV8<0b0111, (ops brtarget:$dst),
-                      "fbu $dst",
-                      [(V8brfcc bb:$dst, FCC_U, FCC)]>;
-def FBG  : FPBranchV8<0b0110, (ops brtarget:$dst),
-                      "fbg $dst",
-                      [(V8brfcc bb:$dst, FCC_G, FCC)]>;
-def FBUG : FPBranchV8<0b0101, (ops brtarget:$dst),
-                      "fbug $dst",
-                      [(V8brfcc bb:$dst, FCC_UG, FCC)]>;
-def FBL  : FPBranchV8<0b0100, (ops brtarget:$dst),
-                      "fbl $dst",
-                      [(V8brfcc bb:$dst, FCC_L, FCC)]>;
-def FBUL : FPBranchV8<0b0011, (ops brtarget:$dst),
-                      "fbul $dst",
-                      [(V8brfcc bb:$dst, FCC_UL, FCC)]>;
-def FBLG : FPBranchV8<0b0010, (ops brtarget:$dst),
-                      "fblg $dst",
-                      [(V8brfcc bb:$dst, FCC_LG, FCC)]>;
-def FBNE : FPBranchV8<0b0001, (ops brtarget:$dst),
-                      "fbne $dst",
-                      [(V8brfcc bb:$dst, FCC_NE, FCC)]>;
-def FBE  : FPBranchV8<0b1001, (ops brtarget:$dst),
-                      "fbe $dst",
-                      [(V8brfcc bb:$dst, FCC_E, FCC)]>;
-def FBUE : FPBranchV8<0b1010, (ops brtarget:$dst),
-                      "fbue $dst",
-                      [(V8brfcc bb:$dst, FCC_UE, FCC)]>;
-def FBGE : FPBranchV8<0b1011, (ops brtarget:$dst),
-                      "fbge $dst",
-                      [(V8brfcc bb:$dst, FCC_GE, FCC)]>;
-def FBUGE: FPBranchV8<0b1100, (ops brtarget:$dst),
-                      "fbuge $dst",
-                      [(V8brfcc bb:$dst, FCC_UGE, FCC)]>;
-def FBLE : FPBranchV8<0b1101, (ops brtarget:$dst),
-                      "fble $dst",
-                      [(V8brfcc bb:$dst, FCC_LE, FCC)]>;
-def FBULE: FPBranchV8<0b1110, (ops brtarget:$dst),
-                      "fbule $dst",
-                      [(V8brfcc bb:$dst, FCC_ULE, FCC)]>;
-def FBO  : FPBranchV8<0b1111, (ops brtarget:$dst),
-                      "fbo $dst",
-                      [(V8brfcc bb:$dst, FCC_O, FCC)]>;
-
+// FIXME: the encoding for the JIT should look at the condition field.
+def FBCOND  : FPBranchV8<0, (ops brtarget:$dst, V8CC:$cc),
+                      "f$cc $dst",
+                      [(V8brfcc bb:$dst, imm:$cc, FCC)]>;
 
 
 // Section B.24 - Call and Link Instruction, p. 125
index 15b80504704b5e624da318d0c2d4745516ac431c..faf160b16cd2e3d15b116c8895a242ef5ef0d812 100644 (file)
@@ -82,40 +82,6 @@ namespace llvm {
     };
   }
   
-  static unsigned SPARCCondCodeToBranchInstr(V8CC::CondCodes CC) {
-    switch (CC) {
-    default: assert(0 && "Unknown condition code");
-    case V8CC::ICC_NE:  return V8::BNE;
-    case V8CC::ICC_E:   return V8::BE;
-    case V8CC::ICC_G:   return V8::BG;
-    case V8CC::ICC_LE:  return V8::BLE;
-    case V8CC::ICC_GE:  return V8::BGE;
-    case V8CC::ICC_L:   return V8::BL;
-    case V8CC::ICC_GU:  return V8::BGU;
-    case V8CC::ICC_LEU: return V8::BLEU;
-    case V8CC::ICC_CC:  return V8::BCC;
-    case V8CC::ICC_CS:  return V8::BCS;
-    case V8CC::ICC_POS: return V8::BPOS;
-    case V8CC::ICC_NEG: return V8::BNEG;
-    case V8CC::ICC_VC:  return V8::BVC;
-    case V8CC::ICC_VS:  return V8::BVS;
-    case V8CC::FCC_U:   return V8::FBU;
-    case V8CC::FCC_G:   return V8::FBG;
-    case V8CC::FCC_UG:  return V8::FBUG;
-    case V8CC::FCC_L:   return V8::FBL;
-    case V8CC::FCC_UL:  return V8::FBUL;
-    case V8CC::FCC_LG:  return V8::FBLG;
-    case V8CC::FCC_NE:  return V8::FBNE;
-    case V8CC::FCC_E:   return V8::FBE;
-    case V8CC::FCC_UE:  return V8::FBUE;
-    case V8CC::FCC_GE:  return V8::FBGE;
-    case V8CC::FCC_UGE: return V8::FBUGE;
-    case V8CC::FCC_LE:  return V8::FBLE;
-    case V8CC::FCC_ULE: return V8::FBULE;
-    case V8CC::FCC_O:   return V8::FBO;
-    }       
-  }
-  
   static const char *SPARCCondCodeToString(V8CC::CondCodes CC) {
     switch (CC) {
     default: assert(0 && "Unknown condition code");
index ff4e861e18353d14fa5510143eae538d177c6b72..520849e6645f1243a5989304c5d9ffd90b094692 100644 (file)
@@ -817,19 +817,22 @@ MachineBasicBlock *
 SparcV8TargetLowering::InsertAtEndOfBasicBlock(MachineInstr *MI,
                                                MachineBasicBlock *BB) {
   unsigned BROpcode;
+  unsigned CC;
   // Figure out the conditional branch opcode to use for this select_cc.
   switch (MI->getOpcode()) {
   default: assert(0 && "Unknown SELECT_CC!");
   case V8::SELECT_CC_Int_ICC:
   case V8::SELECT_CC_FP_ICC:
   case V8::SELECT_CC_DFP_ICC:
+    BROpcode = V8::BCOND;
   case V8::SELECT_CC_Int_FCC:
   case V8::SELECT_CC_FP_FCC:
   case V8::SELECT_CC_DFP_FCC:
-    V8CC::CondCodes CC = (V8CC::CondCodes)MI->getOperand(3).getImmedValue();
-    BROpcode = SPARCCondCodeToBranchInstr(CC);
+    BROpcode = V8::FBCOND;
     break;
   }
+
+  CC = (V8CC::CondCodes)MI->getOperand(3).getImmedValue();
   
   // To "insert" a SELECT_CC instruction, we actually have to insert the diamond
   // control-flow pattern.  The incoming instruction knows the destination vreg
@@ -847,7 +850,7 @@ SparcV8TargetLowering::InsertAtEndOfBasicBlock(MachineInstr *MI,
   MachineBasicBlock *thisMBB = BB;
   MachineBasicBlock *copy0MBB = new MachineBasicBlock(LLVM_BB);
   MachineBasicBlock *sinkMBB = new MachineBasicBlock(LLVM_BB);
-  BuildMI(BB, BROpcode, 1).addMBB(sinkMBB);
+  BuildMI(BB, BROpcode, 2).addMBB(sinkMBB).addImm(CC);
   MachineFunction *F = BB->getParent();
   F->getBasicBlockList().insert(It, copy0MBB);
   F->getBasicBlockList().insert(It, sinkMBB);
index 72f6e3335e735664020f274f8ec9380bd4b745c3..7cf61e47f312a33ac461363a6a5b868eb0989245 100644 (file)
@@ -136,9 +136,7 @@ def retflag       : SDNode<"V8ISD::RET_FLAG", SDT_V8RetFlag,
 
 // Note that these values must be kept in sync with the V8CC::CondCode enum
 // values.
-class ICC_VAL<int N> : PatLeaf<(i32 N)> {
- int ICCVal = N;
-}
+class ICC_VAL<int N> : PatLeaf<(i32 N)>;
 def ICC_NE  : ICC_VAL< 9>;  // Not Equal
 def ICC_E   : ICC_VAL< 1>;  // Equal
 def ICC_G   : ICC_VAL<10>;  // Greater
@@ -154,9 +152,7 @@ def ICC_NEG : ICC_VAL< 6>;  // Negative
 def ICC_VC  : ICC_VAL<15>;  // Overflow Clear
 def ICC_VS  : ICC_VAL< 7>;  // Overflow Set
 
-class FCC_VAL<int N> : PatLeaf<(i32 N)> {
-  int FCCVal = N;
-}
+class FCC_VAL<int N> : PatLeaf<(i32 N)>;
 def FCC_U   : FCC_VAL<23>;  // Unordered
 def FCC_G   : FCC_VAL<22>;  // Greater
 def FCC_UG  : FCC_VAL<21>;  // Unordered or Greater
@@ -550,49 +546,11 @@ let isBarrier = 1 in
   def BA   : BranchV8<0b1000, (ops brtarget:$dst),
                       "ba $dst",
                       [(br bb:$dst)]>;
-def BNE  : BranchV8<0b1001, (ops brtarget:$dst),
-                    "bne $dst",
-                    [(V8bricc bb:$dst, ICC_NE, ICC)]>;
-def BE   : BranchV8<0b0001, (ops brtarget:$dst),
-                    "be $dst",
-                    [(V8bricc bb:$dst, ICC_E, ICC)]>;
-def BG   : BranchV8<0b1010, (ops brtarget:$dst),
-                    "bg $dst",
-                    [(V8bricc bb:$dst, ICC_G, ICC)]>;
-def BLE  : BranchV8<0b0010, (ops brtarget:$dst),
-                    "ble $dst",
-                    [(V8bricc bb:$dst, ICC_LE, ICC)]>;
-def BGE  : BranchV8<0b1011, (ops brtarget:$dst),
-                    "bge $dst",
-                    [(V8bricc bb:$dst, ICC_GE, ICC)]>;
-def BL   : BranchV8<0b0011, (ops brtarget:$dst),
-                    "bl $dst",
-                    [(V8bricc bb:$dst, ICC_L, ICC)]>;
-def BGU  : BranchV8<0b1100, (ops brtarget:$dst),
-                    "bgu $dst",
-                    [(V8bricc bb:$dst, ICC_GU, ICC)]>;
-def BLEU : BranchV8<0b0100, (ops brtarget:$dst),
-                    "bleu $dst",
-                    [(V8bricc bb:$dst, ICC_LEU, ICC)]>;
-def BCC  : BranchV8<0b1101, (ops brtarget:$dst),
-                    "bcc $dst",
-                    [(V8bricc bb:$dst, ICC_CC, ICC)]>;
-def BCS  : BranchV8<0b0101, (ops brtarget:$dst),
-                    "bcs $dst",
-                    [(V8bricc bb:$dst, ICC_CS, ICC)]>;
-def BPOS : BranchV8<0b1110, (ops brtarget:$dst),
-                    "bpos $dst",
-                    [(V8bricc bb:$dst, ICC_POS, ICC)]>;
-def BNEG : BranchV8<0b0110, (ops brtarget:$dst),
-                    "bneg $dst",
-                    [(V8bricc bb:$dst, ICC_NEG, ICC)]>;
-def BVC  : BranchV8<0b1111, (ops brtarget:$dst),
-                    "bvc $dst",
-                    [(V8bricc bb:$dst, ICC_VC, ICC)]>;
-def BVS  : BranchV8<0b0111, (ops brtarget:$dst),
-                    "bvs $dst",
-                    [(V8bricc bb:$dst, ICC_VS, ICC)]>;
-
+                      
+// FIXME: the encoding for the JIT should look at the condition field.
+def BCOND : BranchV8<0, (ops brtarget:$dst, V8CC:$cc),
+                     "b$cc $dst",
+                     [(V8bricc bb:$dst, imm:$cc, ICC)]>;
 
 
 // Section B.22 - Branch on Floating-point Condition Codes Instructions, p. 121
@@ -606,49 +564,10 @@ class FPBranchV8<bits<4> cc, dag ops, string asmstr, list<dag> pattern>
   let noResults = 1;
 }
 
-def FBU  : FPBranchV8<0b0111, (ops brtarget:$dst),
-                      "fbu $dst",
-                      [(V8brfcc bb:$dst, FCC_U, FCC)]>;
-def FBG  : FPBranchV8<0b0110, (ops brtarget:$dst),
-                      "fbg $dst",
-                      [(V8brfcc bb:$dst, FCC_G, FCC)]>;
-def FBUG : FPBranchV8<0b0101, (ops brtarget:$dst),
-                      "fbug $dst",
-                      [(V8brfcc bb:$dst, FCC_UG, FCC)]>;
-def FBL  : FPBranchV8<0b0100, (ops brtarget:$dst),
-                      "fbl $dst",
-                      [(V8brfcc bb:$dst, FCC_L, FCC)]>;
-def FBUL : FPBranchV8<0b0011, (ops brtarget:$dst),
-                      "fbul $dst",
-                      [(V8brfcc bb:$dst, FCC_UL, FCC)]>;
-def FBLG : FPBranchV8<0b0010, (ops brtarget:$dst),
-                      "fblg $dst",
-                      [(V8brfcc bb:$dst, FCC_LG, FCC)]>;
-def FBNE : FPBranchV8<0b0001, (ops brtarget:$dst),
-                      "fbne $dst",
-                      [(V8brfcc bb:$dst, FCC_NE, FCC)]>;
-def FBE  : FPBranchV8<0b1001, (ops brtarget:$dst),
-                      "fbe $dst",
-                      [(V8brfcc bb:$dst, FCC_E, FCC)]>;
-def FBUE : FPBranchV8<0b1010, (ops brtarget:$dst),
-                      "fbue $dst",
-                      [(V8brfcc bb:$dst, FCC_UE, FCC)]>;
-def FBGE : FPBranchV8<0b1011, (ops brtarget:$dst),
-                      "fbge $dst",
-                      [(V8brfcc bb:$dst, FCC_GE, FCC)]>;
-def FBUGE: FPBranchV8<0b1100, (ops brtarget:$dst),
-                      "fbuge $dst",
-                      [(V8brfcc bb:$dst, FCC_UGE, FCC)]>;
-def FBLE : FPBranchV8<0b1101, (ops brtarget:$dst),
-                      "fble $dst",
-                      [(V8brfcc bb:$dst, FCC_LE, FCC)]>;
-def FBULE: FPBranchV8<0b1110, (ops brtarget:$dst),
-                      "fbule $dst",
-                      [(V8brfcc bb:$dst, FCC_ULE, FCC)]>;
-def FBO  : FPBranchV8<0b1111, (ops brtarget:$dst),
-                      "fbo $dst",
-                      [(V8brfcc bb:$dst, FCC_O, FCC)]>;
-
+// FIXME: the encoding for the JIT should look at the condition field.
+def FBCOND  : FPBranchV8<0, (ops brtarget:$dst, V8CC:$cc),
+                      "f$cc $dst",
+                      [(V8brfcc bb:$dst, imm:$cc, FCC)]>;
 
 
 // Section B.24 - Call and Link Instruction, p. 125