diff --git a/include/llvm/CodeGen/LiveIntervalAnalysis.h b/include/llvm/CodeGen/LiveIntervalAnalysis.h index c77513ca986..31eadcf7f83 100644 --- a/include/llvm/CodeGen/LiveIntervalAnalysis.h +++ b/include/llvm/CodeGen/LiveIntervalAnalysis.h @@ -28,6 +28,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Allocator.h" #include +#include namespace llvm { @@ -64,6 +65,22 @@ namespace llvm { AliasAnalysis *aa_; LiveVariables* lv_; + /// AsmsWithEarlyClobber - maps a virtual register number to all the + /// inline asm's that have the register marked earlyclobber. + /// + std::multimap AsmsThatEarlyClobber; + + /// AsmsWithEarlyClobberConflict - maps a virtual register number + /// to all the inline asm's that have earlyclobber operands elsewhere + /// and use the register as a (non-earlyclobber) input. + /// + /// Note: earlyclobber operands may not be assigned the same register as + /// each other, or as earlyclobber-conflict operands. However two + /// earlyclobber-conflict operands may be assigned the same register if + /// they happen to contain the same value. + /// + std::multimap AsmsWithEarlyClobberConflict; + /// Special pool allocator for VNInfo's (LiveInterval val#). /// BumpPtrAllocator VNInfoAllocator; @@ -336,6 +353,11 @@ namespace llvm { unsigned getNumConflictsWithPhysReg(const LiveInterval &li, unsigned PhysReg) const; + /// noEarlyclobberConflict - see whether virtual reg VReg has a conflict + /// with hard reg HReg because HReg is used as an earlyclobber register in + /// asm that also has VReg live into or across it. + bool noEarlyclobberConflict(unsigned VReg, VirtRegMap &vrm, unsigned HReg); + /// computeNumbering - Compute the index numbering. void computeNumbering(); diff --git a/include/llvm/CodeGen/MachineOperand.h b/include/llvm/CodeGen/MachineOperand.h index b0f7cc69f9c..555f320027d 100644 --- a/include/llvm/CodeGen/MachineOperand.h +++ b/include/llvm/CodeGen/MachineOperand.h @@ -73,6 +73,12 @@ private: /// for description of earlyclobber. bool IsEarlyClobber : 1; + /// OverlapsEarlyClobber - True if this MO_Register operand is used as an + /// input to an inline asm that has the earlyclobber bit set on some other + /// operand. Flag is not valid for any other case. See gcc doc + /// for description of earlyclobber. + bool OverlapsEarlyClobber : 1; + /// SubReg - Subregister number, only valid for MO_Register. A value of 0 /// indicates the MO_Register has no subReg. unsigned char SubReg; @@ -182,6 +188,11 @@ public: return IsEarlyClobber; } + bool overlapsEarlyClobber() const { + assert(isRegister() && "Wrong MachineOperand accessor"); + return OverlapsEarlyClobber; + } + /// getNextOperandForReg - Return the next MachineOperand in the function that /// uses or defines this register. MachineOperand *getNextOperandForReg() const { @@ -232,6 +243,11 @@ public: IsEarlyClobber = Val; } + void setOverlapsEarlyClobber(bool Val = true) { + assert(isRegister() && "Wrong MachineOperand accessor"); + OverlapsEarlyClobber = Val; + } + //===--------------------------------------------------------------------===// // Accessors for various operand types. //===--------------------------------------------------------------------===// @@ -337,13 +353,15 @@ public: static MachineOperand CreateReg(unsigned Reg, bool isDef, bool isImp = false, bool isKill = false, bool isDead = false, unsigned SubReg = 0, - bool isEarlyClobber = false) { + bool isEarlyClobber = false, + bool overlapsEarlyClobber = false) { MachineOperand Op(MachineOperand::MO_Register); Op.IsDef = isDef; Op.IsImp = isImp; Op.IsKill = isKill; Op.IsDead = isDead; Op.IsEarlyClobber = isEarlyClobber; + Op.OverlapsEarlyClobber = overlapsEarlyClobber; Op.Contents.Reg.RegNo = Reg; Op.Contents.Reg.Prev = 0; Op.Contents.Reg.Next = 0; @@ -390,6 +408,7 @@ public: IsKill = MO.IsKill; IsDead = MO.IsDead; IsEarlyClobber = MO.IsEarlyClobber; + OverlapsEarlyClobber = MO.OverlapsEarlyClobber; SubReg = MO.SubReg; ParentMI = MO.ParentMI; Contents = MO.Contents; diff --git a/include/llvm/CodeGen/ScheduleDAG.h b/include/llvm/CodeGen/ScheduleDAG.h index 67f3dbb69fb..01218ac9976 100644 --- a/include/llvm/CodeGen/ScheduleDAG.h +++ b/include/llvm/CodeGen/ScheduleDAG.h @@ -361,7 +361,8 @@ namespace llvm { void AddOperand(MachineInstr *MI, SDValue Op, unsigned IIOpNum, const TargetInstrDesc *II, - DenseMap &VRBaseMap); + DenseMap &VRBaseMap, + bool overlapsEarlyClobber = false); void AddMemOperand(MachineInstr *MI, const MachineMemOperand &MO); diff --git a/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 98f4b7d13cd..42a61c624c6 100644 --- a/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1325,7 +1325,8 @@ void AsmPrinter::printInlineAsm(const MachineInstr *MI) const { false, false, false); else { AsmPrinter *AP = const_cast(this); - if ((OpFlags & 7) == 4 /*ADDR MODE*/) { + if ((OpFlags & 7) == 4 /*ADDR MODE*/ || + (OpFlags & 7) == 7) /*ADDR MODE OVERLAPS EARLYCLOBBER*/ { Error = AP->PrintAsmMemoryOperand(MI, OpNo, AsmPrinterVariant, Modifier[0] ? Modifier : 0); } else { diff --git a/lib/CodeGen/LiveIntervalAnalysis.cpp b/lib/CodeGen/LiveIntervalAnalysis.cpp index 577400c674b..cb83194d97a 100644 --- a/lib/CodeGen/LiveIntervalAnalysis.cpp +++ b/lib/CodeGen/LiveIntervalAnalysis.cpp @@ -673,7 +673,10 @@ exit: /// registers. for some ordering of the machine instructions [1,N] a /// live interval is an interval [i, j) where 1 <= i <= j < N for /// which a variable is live -void LiveIntervals::computeIntervals() { +void LiveIntervals::computeIntervals() { + AsmsThatEarlyClobber.clear(); + AsmsWithEarlyClobberConflict.clear(); + DOUT << "********** COMPUTING LIVE INTERVALS **********\n" << "********** Function: " << ((Value*)mf_->getFunction())->getName() << '\n'; @@ -710,8 +713,17 @@ void LiveIntervals::computeIntervals() { for (int i = MI->getNumOperands() - 1; i >= 0; --i) { MachineOperand &MO = MI->getOperand(i); // handle register defs - build intervals - if (MO.isRegister() && MO.getReg() && MO.isDef()) + if (MO.isRegister() && MO.getReg() && MO.isDef()) { handleRegisterDef(MBB, MI, MIIndex, MO, i); + if (MO.isEarlyClobber()) { + AsmsThatEarlyClobber.insert(std::make_pair(MO.getReg(), MI)); + } + } + if (MO.isRegister() && !MO.isDef() && + MO.getReg() && TargetRegisterInfo::isVirtualRegister(MO.getReg()) && + MO.overlapsEarlyClobber()) { + AsmsWithEarlyClobberConflict.insert(std::make_pair(MO.getReg(), MI)); + } } MIIndex += InstrSlots::NUM; @@ -740,6 +752,72 @@ bool LiveIntervals::findLiveInMBBs(const LiveRange &LR, return ResVal; } +/// noEarlyclobberConflict - see whether virtual reg VReg has a conflict with +/// hard reg HReg because of earlyclobbers. +/// +/// Earlyclobber operands may not be assigned the same register as +/// each other, or as earlyclobber-conflict operands (i.e. those that +/// are non-earlyclobbered inputs to an asm that also has earlyclobbers). +/// +/// Thus there are two cases to check for: +/// 1. VReg is an earlyclobber-conflict register and HReg is an earlyclobber +/// register in some asm that also has VReg as an input. +/// 2. VReg is an earlyclobber register and HReg is an earlyclobber-conflict +/// input elsewhere in some asm. +/// In both cases HReg can be assigned by the user, or assigned early in +/// register allocation. +/// +/// Dropping the distinction between earlyclobber and earlyclobber-conflict, +/// keeping only one multimap, looks promising, but two earlyclobber-conflict +/// operands may be assigned the same register if they happen to contain the +/// same value, and that implementation would prevent this. +/// +bool LiveIntervals::noEarlyclobberConflict(unsigned VReg, VirtRegMap &vrm, + unsigned HReg) { + typedef std::multimap::iterator It; + + // Short circuit the most common case. + if (AsmsWithEarlyClobberConflict.size()!=0) { + std::pair x = AsmsWithEarlyClobberConflict.equal_range(VReg); + for (It I = x.first; I!=x.second; I++) { + MachineInstr* MI = I->second; + for (int i = MI->getNumOperands() - 1; i >= 0; --i) { + MachineOperand &MO = MI->getOperand(i); + if (MO.isRegister() && MO.isEarlyClobber()) { + unsigned PhysReg = MO.getReg(); + if (PhysReg && TargetRegisterInfo::isVirtualRegister(PhysReg)) { + if (!vrm.hasPhys(PhysReg)) + continue; + PhysReg = vrm.getPhys(PhysReg); + } + if (PhysReg==HReg) + return false; + } + } + } + } + // Short circuit the most common case. + if (AsmsThatEarlyClobber.size()!=0) { + std::pair x = AsmsThatEarlyClobber.equal_range(VReg); + for (It I = x.first; I!=x.second; I++) { + MachineInstr* MI = I->second; + for (int i = MI->getNumOperands() - 1; i >= 0; --i) { + MachineOperand &MO = MI->getOperand(i); + if (MO.isRegister() && MO.overlapsEarlyClobber()) { + unsigned PhysReg = MO.getReg(); + if (PhysReg && TargetRegisterInfo::isVirtualRegister(PhysReg)) { + if (!vrm.hasPhys(PhysReg)) + continue; + PhysReg = vrm.getPhys(PhysReg); + } + if (PhysReg==HReg) + return false; + } + } + } + } + return true; +} LiveInterval* LiveIntervals::createInterval(unsigned reg) { float Weight = TargetRegisterInfo::isPhysicalRegister(reg) ? diff --git a/lib/CodeGen/MachineInstr.cpp b/lib/CodeGen/MachineInstr.cpp index 7f2a7b6a077..a365f20300f 100644 --- a/lib/CodeGen/MachineInstr.cpp +++ b/lib/CodeGen/MachineInstr.cpp @@ -109,6 +109,7 @@ void MachineOperand::ChangeToRegister(unsigned Reg, bool isDef, bool isImp, // register's use/def lists. if (isRegister()) { assert(!isEarlyClobber()); + assert(!isEarlyClobber() && !overlapsEarlyClobber()); setReg(Reg); } else { // Otherwise, change this to a register and set the reg#. @@ -128,6 +129,7 @@ void MachineOperand::ChangeToRegister(unsigned Reg, bool isDef, bool isImp, IsKill = isKill; IsDead = isDead; IsEarlyClobber = false; + OverlapsEarlyClobber = false; SubReg = 0; } @@ -183,13 +185,20 @@ void MachineOperand::print(std::ostream &OS, const TargetMachine *TM) const { OS << "%mreg" << getReg(); } - if (isDef() || isKill() || isDead() || isImplicit() || isEarlyClobber()) { + if (isDef() || isKill() || isDead() || isImplicit() || isEarlyClobber() || + overlapsEarlyClobber()) { OS << "<"; bool NeedComma = false; + if (overlapsEarlyClobber()) { + NeedComma = true; + OS << "overlapsearly"; + } if (isImplicit()) { + if (NeedComma) OS << ","; OS << (isDef() ? "imp-def" : "imp-use"); NeedComma = true; } else if (isDef()) { + if (NeedComma) OS << ","; if (isEarlyClobber()) OS << "earlyclobber,"; OS << "def"; diff --git a/lib/CodeGen/RegAllocLinearScan.cpp b/lib/CodeGen/RegAllocLinearScan.cpp index 2cfa755d169..a99ccab37c7 100644 --- a/lib/CodeGen/RegAllocLinearScan.cpp +++ b/lib/CodeGen/RegAllocLinearScan.cpp @@ -1050,7 +1050,8 @@ unsigned RALinScan::getFreePhysReg(LiveInterval *cur) { TargetRegisterClass::iterator E = RC->allocation_order_end(*mf_); assert(I != E && "No allocatable register in this register class!"); for (; I != E; ++I) - if (prt_->isRegAvail(*I)) { + if (prt_->isRegAvail(*I) && + li_->noEarlyclobberConflict(cur->reg, *vrm_, *I)) { FreeReg = *I; if (FreeReg < inactiveCounts.size()) FreeRegInactiveCount = inactiveCounts[FreeReg]; @@ -1070,7 +1071,8 @@ unsigned RALinScan::getFreePhysReg(LiveInterval *cur) { for (; I != E; ++I) { unsigned Reg = *I; if (prt_->isRegAvail(Reg) && Reg < inactiveCounts.size() && - FreeRegInactiveCount < inactiveCounts[Reg]) { + FreeRegInactiveCount < inactiveCounts[Reg] && + li_->noEarlyclobberConflict(cur->reg, *vrm_, Reg)) { FreeReg = Reg; FreeRegInactiveCount = inactiveCounts[Reg]; if (FreeRegInactiveCount == MaxInactiveCount) diff --git a/lib/CodeGen/SelectionDAG/ScheduleDAGEmit.cpp b/lib/CodeGen/SelectionDAG/ScheduleDAGEmit.cpp index 4ea29063ab5..d929aaff7b8 100644 --- a/lib/CodeGen/SelectionDAG/ScheduleDAGEmit.cpp +++ b/lib/CodeGen/SelectionDAG/ScheduleDAGEmit.cpp @@ -231,7 +231,8 @@ unsigned ScheduleDAG::getVR(SDValue Op, void ScheduleDAG::AddOperand(MachineInstr *MI, SDValue Op, unsigned IIOpNum, const TargetInstrDesc *II, - DenseMap &VRBaseMap) { + DenseMap &VRBaseMap, + bool overlapsEarlyClobber) { if (Op.isMachineOpcode()) { // Note that this case is redundant with the final else block, but we // include it because it is the most common and it makes the logic @@ -244,7 +245,9 @@ void ScheduleDAG::AddOperand(MachineInstr *MI, SDValue Op, const TargetInstrDesc &TID = MI->getDesc(); bool isOptDef = IIOpNum < TID.getNumOperands() && TID.OpInfo[IIOpNum].isOptionalDef(); - MI->addOperand(MachineOperand::CreateReg(VReg, isOptDef)); + MI->addOperand(MachineOperand::CreateReg(VReg, isOptDef, false, false, + false, 0, false, + overlapsEarlyClobber)); // Verify that it is right. assert(TargetRegisterInfo::isVirtualRegister(VReg) && "Not a vreg?"); @@ -278,7 +281,9 @@ void ScheduleDAG::AddOperand(MachineInstr *MI, SDValue Op, const ConstantFP *CFP = F->getConstantFPValue(); MI->addOperand(MachineOperand::CreateFPImm(CFP)); } else if (RegisterSDNode *R = dyn_cast(Op)) { - MI->addOperand(MachineOperand::CreateReg(R->getReg(), false)); + MI->addOperand(MachineOperand::CreateReg(R->getReg(), false, false, + false, false, 0, false, + overlapsEarlyClobber)); } else if (GlobalAddressSDNode *TGA = dyn_cast(Op)) { MI->addOperand(MachineOperand::CreateGA(TGA->getGlobal(),TGA->getOffset())); } else if (BasicBlockSDNode *BB = dyn_cast(Op)) { @@ -314,7 +319,9 @@ void ScheduleDAG::AddOperand(MachineInstr *MI, SDValue Op, Op.getValueType() != MVT::Flag && "Chain and flag operands should occur at end of operand list!"); unsigned VReg = getVR(Op, VRBaseMap); - MI->addOperand(MachineOperand::CreateReg(VReg, false)); + MI->addOperand(MachineOperand::CreateReg(VReg, false, false, + false, false, 0, false, + overlapsEarlyClobber)); // Verify that it is right. Note that the reg class of the physreg and the // vreg don't necessarily need to match, but the target copy insertion has @@ -596,6 +603,7 @@ void ScheduleDAG::EmitNode(SDNode *Node, bool IsClone, // Add all of the operand registers to the instruction. for (unsigned i = 2; i != NumOps;) { + bool overlapsEarlyClobber = false; unsigned Flags = cast(Node->getOperand(i))->getZExtValue(); unsigned NumVals = Flags >> 3; @@ -618,13 +626,18 @@ void ScheduleDAG::EmitNode(SDNode *Node, bool IsClone, false, 0, true)); } break; + case 7: // Addressing mode overlapping earlyclobber. + case 5: // Use of register overlapping earlyclobber. + overlapsEarlyClobber = true; + // fall through case 1: // Use of register. case 3: // Immediate. case 4: // Addressing mode. // The addressing mode has been selected, just add all of the // operands to the machine instruction. for (; NumVals; --NumVals, ++i) - AddOperand(MI, Node->getOperand(i), 0, 0, VRBaseMap); + AddOperand(MI, Node->getOperand(i), 0, 0, VRBaseMap, + overlapsEarlyClobber); break; } } diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp index 62a6b4f1808..9492b33527f 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp @@ -4909,8 +4909,10 @@ void SelectionDAGLowering::visitInlineAsm(CallSite CS) { assert(OpInfo.isIndirect && "Memory output must be indirect operand"); // Add information to the INLINEASM node to know about this output. - unsigned ResOpType = 4/*MEM*/ | (1 << 3); - AsmNodeOperands.push_back(DAG.getTargetConstant(ResOpType, + unsigned ResOpType = SawEarlyClobber ? + 7 /* MEM OVERLAPS EARLYCLOBBER */ : + 4/*MEM*/; + AsmNodeOperands.push_back(DAG.getTargetConstant(ResOpType | (1<<3), TLI.getPointerTy())); AsmNodeOperands.push_back(OpInfo.CallOperand); break; @@ -4963,7 +4965,8 @@ void SelectionDAGLowering::visitInlineAsm(CallSite CS) { cast(AsmNodeOperands[CurOp])->getZExtValue(); assert(((NumOps & 7) == 2 /*REGDEF*/ || (NumOps & 7) == 6 /*EARLYCLOBBER REGDEF*/ || - (NumOps & 7) == 4 /*MEM*/) && + (NumOps & 7) == 4 /*MEM*/ || + (NumOps & 7) == 7 /*MEM OVERLAPS EARLYCLOBBER*/) && "Skipped past definitions?"); CurOp += (NumOps>>3)+1; } @@ -4985,14 +4988,17 @@ void SelectionDAGLowering::visitInlineAsm(CallSite CS) { // Use the produced MatchedRegs object to MatchedRegs.getCopyToRegs(InOperandVal, DAG, Chain, &Flag); - MatchedRegs.AddInlineAsmOperands(1 /*REGUSE*/, DAG, AsmNodeOperands); + MatchedRegs.AddInlineAsmOperands(SawEarlyClobber ? + 1 /*REGUSE*/ : + 5 /*REGUSE OVERLAPS EARLYCLOBBER*/, + DAG, AsmNodeOperands); break; } else { - assert((NumOps & 7) == 4/*MEM*/ && "Unknown matching constraint!"); + assert(((NumOps & 7) == 7/*MEM OVERLAPS EARLYCLOBBER */ || + (NumOps & 7) == 4) && "Unknown matching constraint!"); assert((NumOps >> 3) == 1 && "Unexpected number of operands"); // Add information to the INLINEASM node to know about this input. - unsigned ResOpType = 4/*MEM*/ | (1 << 3); - AsmNodeOperands.push_back(DAG.getTargetConstant(ResOpType, + AsmNodeOperands.push_back(DAG.getTargetConstant(NumOps, TLI.getPointerTy())); AsmNodeOperands.push_back(AsmNodeOperands[CurOp+1]); break; @@ -5024,8 +5030,10 @@ void SelectionDAGLowering::visitInlineAsm(CallSite CS) { "Memory operands expect pointer values"); // Add information to the INLINEASM node to know about this input. - unsigned ResOpType = 4/*MEM*/ | (1 << 3); - AsmNodeOperands.push_back(DAG.getTargetConstant(ResOpType, + unsigned ResOpType = SawEarlyClobber ? + 7 /* MEM OVERLAPS EARLYCLOBBER */ : + 4/*MEM*/; + AsmNodeOperands.push_back(DAG.getTargetConstant(ResOpType | (1<<3), TLI.getPointerTy())); AsmNodeOperands.push_back(InOperandVal); break; @@ -5043,16 +5051,18 @@ void SelectionDAGLowering::visitInlineAsm(CallSite CS) { OpInfo.AssignedRegs.getCopyToRegs(InOperandVal, DAG, Chain, &Flag); - OpInfo.AssignedRegs.AddInlineAsmOperands(1/*REGUSE*/, DAG, - AsmNodeOperands); + OpInfo.AssignedRegs.AddInlineAsmOperands(SawEarlyClobber ? + 5 /*REGUSE OVERLAPS EARLYCLOBBER*/: + 1/*REGUSE*/, + DAG, AsmNodeOperands); break; } case InlineAsm::isClobber: { // Add the clobbered value to the operand list, so that the register // allocator is aware that the physreg got clobbered. if (!OpInfo.AssignedRegs.Regs.empty()) - OpInfo.AssignedRegs.AddInlineAsmOperands(2/*REGDEF*/, DAG, - AsmNodeOperands); + OpInfo.AssignedRegs.AddInlineAsmOperands(6 /* EARLYCLOBBER REGDEF */, + DAG, AsmNodeOperands); break; } } diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index 140bad27f5e..c2938e35f88 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -1113,7 +1113,8 @@ SelectInlineAsmMemoryOperands(std::vector &Ops) { while (i != e) { unsigned Flags = cast(InOps[i])->getZExtValue(); - if ((Flags & 7) != 4 /*MEM*/) { + if ((Flags & 7) != 4 /*MEM*/ && + (Flags & 7) != 7 /*MEM OVERLAPS EARLYCLOBBER*/) { // Just skip over this operand, copying the operands verbatim. Ops.insert(Ops.end(), InOps.begin()+i, InOps.begin()+i+(Flags >> 3) + 1); i += (Flags >> 3) + 1; @@ -1128,7 +1129,7 @@ SelectInlineAsmMemoryOperands(std::vector &Ops) { // Add this to the output node. MVT IntPtrTy = CurDAG->getTargetLoweringInfo().getPointerTy(); - Ops.push_back(CurDAG->getTargetConstant(4/*MEM*/ | (SelOps.size() << 3), + Ops.push_back(CurDAG->getTargetConstant((Flags & 7) | (SelOps.size()<< 3), IntPtrTy)); Ops.insert(Ops.end(), SelOps.begin(), SelOps.end()); i += 2; diff --git a/test/CodeGen/X86/2008-09-17-inline-asm-1.ll b/test/CodeGen/X86/2008-09-17-inline-asm-1.ll new file mode 100644 index 00000000000..96052a60437 --- /dev/null +++ b/test/CodeGen/X86/2008-09-17-inline-asm-1.ll @@ -0,0 +1,24 @@ +; RUN: llvm-as < %s | llc -o - -march=x86 | not grep "movl %eax, %eax" +; RUN: llvm-as < %s | llc -o - -march=x86 | not grep "movl %edx, %edx" +; RUN: llvm-as < %s | llc -o - -march=x86 | not grep "movl (%eax), %eax" +; RUN: llvm-as < %s | llc -o - -march=x86 | not grep "movl (%edx), %edx" + +; %0 must not be put in EAX or EDX. +; In the first asm, $0 and $2 must not be put in EAX. +; In the second asm, $0 and $2 must not be put in EDX. +; This is kind of hard to test thoroughly, but the things above should continue +; to pass, I think. +; ModuleID = '' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128" +target triple = "i386-apple-darwin8" +@x = common global i32 0 ; [#uses=1] + +define i32 @aci(i32* %pw) nounwind { +entry: + %0 = load i32* @x, align 4 ; [#uses=1] + %asmtmp = tail call { i32, i32 } asm "movl $0, %eax\0A\090:\0A\09test %eax, %eax\0A\09je 1f\0A\09movl %eax, $2\0A\09incl $2\0A\09lock\0A\09cmpxchgl $2, $0\0A\09jne 0b\0A\091:", "=*m,=&{ax},=&r,*m,~{dirflag},~{fpsr},~{flags},~{memory},~{cc}"(i32* %pw, i32* %pw) nounwind ; <{ i32, i32 }> [#uses=0] + %asmtmp2 = tail call { i32, i32 } asm "movl $0, %edx\0A\090:\0A\09test %edx, %edx\0A\09je 1f\0A\09movl %edx, $2\0A\09incl $2\0A\09lock\0A\09cmpxchgl $2, $0\0A\09jne 0b\0A\091:", "=*m,=&{dx},=&r,*m,~{dirflag},~{fpsr},~{flags},~{memory},~{cc}"(i32* %pw, i32* %pw) nounwind ; <{ i32, i32 }> [#uses=1] + %asmresult3 = extractvalue { i32, i32 } %asmtmp2, 0 ; [#uses=1] + %1 = add i32 %asmresult3, %0 ; [#uses=1] + ret i32 %1 +}