Index: llvm/include/llvm/CodeGen/TargetLowering.h =================================================================== --- llvm/include/llvm/CodeGen/TargetLowering.h +++ llvm/include/llvm/CodeGen/TargetLowering.h @@ -3640,6 +3640,12 @@ std::vector &Ops, SelectionDAG &DAG) const; + // Lower custom output constraints. If invalid, return SDValue(). + virtual SDValue LowerAsmOutputForConstraint(SDValue &Chain, SDValue *Flag, + SDLoc DL, + const AsmOperandInfo &OpInfo, + SelectionDAG &DAG) const; + //===--------------------------------------------------------------------===// // Div utility functions // Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -7645,11 +7645,9 @@ switch (OpInfo.Type) { case InlineAsm::isOutput: - if (OpInfo.ConstraintType != TargetLowering::C_RegisterClass && - OpInfo.ConstraintType != TargetLowering::C_Register) { - // Memory output, or 'other' output (e.g. 'X' constraint). - assert(OpInfo.isIndirect && "Memory output must be indirect operand"); - + if (OpInfo.ConstraintType == TargetLowering::C_Memory || + (OpInfo.ConstraintType == TargetLowering::C_Other && + OpInfo.isIndirect)) { unsigned ConstraintID = TLI.getInlineAsmMemConstraint(OpInfo.ConstraintCode); assert(ConstraintID != InlineAsm::Constraint_Unknown && @@ -7662,7 +7660,9 @@ MVT::i32)); AsmNodeOperands.push_back(OpInfo.CallOperand); break; - } else if (OpInfo.ConstraintType == TargetLowering::C_Register || + } else if ((OpInfo.ConstraintType == TargetLowering::C_Other && + !OpInfo.isIndirect) || + OpInfo.ConstraintType == TargetLowering::C_Register || OpInfo.ConstraintType == TargetLowering::C_RegisterClass) { // Otherwise, this is a register or register class output. @@ -7883,25 +7883,41 @@ ResultValues.push_back(V); }; - // Deal with assembly output fixups. + // Deal with output operands. for (SDISelAsmOperandInfo &OpInfo : ConstraintOperands) { - if (OpInfo.Type == InlineAsm::isOutput && - (OpInfo.ConstraintType == TargetLowering::C_Register || - OpInfo.ConstraintType == TargetLowering::C_RegisterClass)) { + if (OpInfo.Type == InlineAsm::isOutput) { + SDValue Val; + // Skip trivial output operands. + if (OpInfo.AssignedRegs.Regs.empty()) + continue; + + switch (OpInfo.ConstraintType) { + case TargetLowering::C_Register: + case TargetLowering::C_RegisterClass: + Val = OpInfo.AssignedRegs.getCopyFromRegs( + DAG, FuncInfo, getCurSDLoc(), Chain, &Flag, CS.getInstruction()); + break; + case TargetLowering::C_Other: + Val = TLI.LowerAsmOutputForConstraint(Chain, &Flag, getCurSDLoc(), + OpInfo, DAG); + break; + case TargetLowering::C_Memory: + break; // Already handled. + case TargetLowering::C_Unknown: + assert(false && "Unexpected unknown constraint"); + } + + // Indirect output are manifest as stores. Record output chains. if (OpInfo.isIndirect) { - // Register indirect are manifest as stores. - const RegsForValue &OutRegs = OpInfo.AssignedRegs; + const Value *Ptr = OpInfo.CallOperandVal; - SDValue OutVal = OutRegs.getCopyFromRegs(DAG, FuncInfo, getCurSDLoc(), - Chain, &Flag, IA); - SDValue Val = DAG.getStore(Chain, getCurSDLoc(), OutVal, getValue(Ptr), - MachinePointerInfo(Ptr)); - OutChains.push_back(Val); + assert(Ptr && "Expected value CallOperandVal for indirect asm operand"); + SDValue Store = DAG.getStore(Chain, getCurSDLoc(), Val, getValue(Ptr), + MachinePointerInfo(Ptr)); + OutChains.push_back(Store); } else { // generate CopyFromRegs to associated registers. assert(!CS.getType()->isVoidTy() && "Bad inline asm!"); - SDValue Val = OpInfo.AssignedRegs.getCopyFromRegs( - DAG, FuncInfo, getCurSDLoc(), Chain, &Flag, CS.getInstruction()); if (Val.getOpcode() == ISD::MERGE_VALUES) { for (const SDValue &V : Val->op_values()) handleRegAssign(V); Index: llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -3177,6 +3177,12 @@ return nullptr; } +SDValue TargetLowering::LowerAsmOutputForConstraint( + SDValue &Chain, SDValue *Flag, SDLoc DL, const AsmOperandInfo &OpInfo, + SelectionDAG &DAG) const { + return SDValue(); +} + /// Lower the specified operand into the Ops vector. /// If it is invalid, don't add anything to Ops. void TargetLowering::LowerAsmOperandForConstraint(SDValue Op, Index: llvm/lib/Target/X86/X86ISelLowering.h =================================================================== --- llvm/lib/Target/X86/X86ISelLowering.h +++ llvm/lib/Target/X86/X86ISelLowering.h @@ -917,6 +917,11 @@ return TargetLowering::getInlineAsmMemConstraint(ConstraintCode); } + /// Handle Lowering flag assembly outputs. + SDValue LowerAsmOutputForConstraint(SDValue &Chain, SDValue *Flag, SDLoc DL, + const AsmOperandInfo &Constraint, + SelectionDAG &DAG) const override; + /// Given a physical register constraint /// (e.g. {edx}), return the register number and the register class for the /// register. This should only be used for C_Register constraints. On Index: llvm/lib/Target/X86/X86ISelLowering.cpp =================================================================== --- llvm/lib/Target/X86/X86ISelLowering.cpp +++ llvm/lib/Target/X86/X86ISelLowering.cpp @@ -42195,6 +42195,40 @@ return false; } +static X86::CondCode parseConstraintCode(llvm::StringRef Constraint) { + X86::CondCode Cond = StringSwitch(Constraint) + .Case("{@cca}", X86::COND_A) + .Case("{@ccae}", X86::COND_AE) + .Case("{@ccb}", X86::COND_B) + .Case("{@ccbe}", X86::COND_BE) + .Case("{@ccc}", X86::COND_B) + .Case("{@cce}", X86::COND_E) + .Case("{@ccz}", X86::COND_NE) + .Case("{@ccg}", X86::COND_G) + .Case("{@ccge}", X86::COND_GE) + .Case("{@ccl}", X86::COND_L) + .Case("{@ccle}", X86::COND_LE) + .Case("{@ccna}", X86::COND_BE) + .Case("{@ccnae}", X86::COND_B) + .Case("{@ccnb}", X86::COND_AE) + .Case("{@ccnbe}", X86::COND_A) + .Case("{@ccnc}", X86::COND_AE) + .Case("{@ccne}", X86::COND_NE) + .Case("{@ccnz}", X86::COND_NE) + .Case("{@ccng}", X86::COND_LE) + .Case("{@ccnge}", X86::COND_L) + .Case("{@ccnl}", X86::COND_GE) + .Case("{@ccnle}", X86::COND_G) + .Case("{@ccno}", X86::COND_NO) + .Case("{@ccnp}", X86::COND_P) + .Case("{@ccns}", X86::COND_NS) + .Case("{@cco}", X86::COND_O) + .Case("{@ccp}", X86::COND_P) + .Case("{@ccs}", X86::COND_S) + .Default(X86::COND_INVALID); + return Cond; +} + /// Given a constraint letter, return the type of constraint for this target. X86TargetLowering::ConstraintType X86TargetLowering::getConstraintType(StringRef Constraint) const { @@ -42255,7 +42289,8 @@ return C_RegisterClass; } } - } + } else if (parseConstraintCode(Constraint) != X86::COND_INVALID) + return C_Other; return TargetLowering::getConstraintType(Constraint); } @@ -42426,6 +42461,33 @@ return TargetLowering::LowerXConstraint(ConstraintVT); } +// Lower @cc targets via setcc. +SDValue X86TargetLowering::LowerAsmOutputForConstraint( + SDValue &Chain, SDValue *Flag, SDLoc DL, const AsmOperandInfo &OpInfo, + SelectionDAG &DAG) const { + X86::CondCode Cond = parseConstraintCode(OpInfo.ConstraintCode); + if (Cond == X86::COND_INVALID) + return SDValue(); + // Check that return type is valid. + if (OpInfo.ConstraintVT.isVector() || !OpInfo.ConstraintVT.isInteger() || + OpInfo.ConstraintVT.getSizeInBits() < 8) + report_fatal_error("Flag output operand is of invalid type"); + + // Get EFLAGS register. Only update chain when copyfrom is glued. + SDValue EFlags; + if (Flag) { + EFlags = DAG.getCopyFromReg(Chain, DL, X86::EFLAGS, MVT::i32, *Flag); + Chain = EFlags.getValue(1); + } else + EFlags = DAG.getCopyFromReg(Chain, DL, X86::EFLAGS, MVT::i32); + // Extract CC code. + SDValue CC = getSETCC(Cond, EFlags, DL, DAG); + // Extend to 32-bits + SDValue Result = DAG.getNode(ISD::ZERO_EXTEND, DL, OpInfo.ConstraintVT, CC); + + return Result; +} + /// Lower the specified operand into the Ops vector. /// If it is invalid, don't add anything to Ops. void X86TargetLowering::LowerAsmOperandForConstraint(SDValue Op, @@ -42782,6 +42844,9 @@ } } + if (parseConstraintCode(Constraint) != X86::COND_INVALID) + return std::make_pair(0U, &X86::GR32RegClass); + // Use the default implementation in TargetLowering to convert the register // constraint into a member of a register class. std::pair Res; Index: llvm/test/CodeGen/X86/inline-asm-flag-output.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/inline-asm-flag-output.ll @@ -0,0 +1,255 @@ +; RUN: llc < %s -mtriple=i686-- -no-integrated-as + +; define i32 @test_cca(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@cca},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccae(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccae},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccb(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccb},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccbe(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccbe},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccc(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccc},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_cce(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@cce},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccz(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccz},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccg(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccg},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccge(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccge},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccl(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccl},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccle(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccle},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccna(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccna},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccnae(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccnae},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccnb(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccnb},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +define i32 @test_ccnbe(i64 %nr, i64* %addr) local_unnamed_addr #0 { +entry: + %cc = tail call i32 asm "cmp $2,$1", "={@ccnbe},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 + %tobool = icmp eq i32 %cc, 0 + %rv = zext i1 %tobool to i32 + ret i32 %rv +} + + +; define i32 @test_ccnc(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccnc},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccne(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccne},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccnz(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccnz},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccng(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccng},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccnge(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccnge},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccnl(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccnl},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccnle(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccnle},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccno(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccno},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccnp(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccnp},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccns(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccns},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_cco(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@cco},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccp(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccp},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + + +; define i32 @test_ccs(i64 %nr, i64* %addr) local_unnamed_addr #0 { +; entry: +; %cc = tail call i32 asm "cmp $2,$1", "={@ccs},=*m,r,~{cc},~{dirflag},~{fpsr},~{flags}"(i64* %addr, i64 %nr) #1 +; %tobool = icmp eq i32 %cc, 0 +; %rv = zext i1 %tobool to i32 +; ret i32 %rv +; } + +attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind }