Skip to content

Commit

Permalink
[InlineAsm][AArch64]Add backend support for flag output parameters
Browse files Browse the repository at this point in the history
- The set of flag is from https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Flag-Output-Operands

Before:
- ARM64 GCC supports flag output constraints, while Clang doesn't parse condition code, as shown in https://gcc.godbolt.org/z/7jzMEK796
- LLVM ISel won't lower them either (as shown in https://gcc.godbolt.org/z/Pv4PPf56c)

After:
- Given flag output constraints in LLVM IR, condition code is parsed and flag output is lowered to 'cset'.
- Clang parse is not added in this patch.

Differential Revision: https://reviews.llvm.org/D149032
  • Loading branch information
minglotus-6 committed Apr 26, 2023
1 parent 2251659 commit 9879e58
Show file tree
Hide file tree
Showing 5 changed files with 353 additions and 3 deletions.
6 changes: 4 additions & 2 deletions llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,12 @@ bool InlineAsmLowering::lowerInlineAsm(
Inst.addReg(SourceRegs[0]);
} else {
// Otherwise, this outputs to a register (directly for C_Register /
// C_RegisterClass. Find a register that we can use.
// C_RegisterClass/C_Other.
assert(OpInfo.ConstraintType == TargetLowering::C_Register ||
OpInfo.ConstraintType == TargetLowering::C_RegisterClass);
OpInfo.ConstraintType == TargetLowering::C_RegisterClass ||
OpInfo.ConstraintType == TargetLowering::C_Other);

// Find a register that we can use.
if (OpInfo.Regs.empty()) {
LLVM_DEBUG(dbgs()
<< "Couldn't allocate output register for constraint\n");
Expand Down
71 changes: 70 additions & 1 deletion llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9864,6 +9864,72 @@ static PredicateConstraint parsePredicateConstraint(StringRef Constraint) {
return P;
}

// The set of cc code supported is from
// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Flag-Output-Operands
static AArch64CC::CondCode parseConstraintCode(llvm::StringRef Constraint) {
AArch64CC::CondCode Cond = StringSwitch<AArch64CC::CondCode>(Constraint)
.Case("{@cchi}", AArch64CC::HI)
.Case("{@cccs}", AArch64CC::HS)
.Case("{@cclo}", AArch64CC::LO)
.Case("{@ccls}", AArch64CC::LS)
.Case("{@cccc}", AArch64CC::LO)
.Case("{@cceq}", AArch64CC::EQ)
.Case("{@ccgt}", AArch64CC::GT)
.Case("{@ccge}", AArch64CC::GE)
.Case("{@cclt}", AArch64CC::LT)
.Case("{@ccle}", AArch64CC::LE)
.Case("{@cchs}", AArch64CC::HS)
.Case("{@ccne}", AArch64CC::NE)
.Case("{@ccvc}", AArch64CC::VC)
.Case("{@ccpl}", AArch64CC::PL)
.Case("{@ccvs}", AArch64CC::VS)
.Case("{@ccmi}", AArch64CC::MI)
.Default(AArch64CC::Invalid);
return Cond;
}

/// Helper function to create 'CSET', which is equivalent to 'CSINC <Wd>, WZR,
/// WZR, invert(<cond>)'.
static SDValue getSETCC(AArch64CC::CondCode CC, SDValue NZCV, const SDLoc &DL,
SelectionDAG &DAG) {
return DAG.getNode(
AArch64ISD::CSINC, DL, MVT::i32, DAG.getConstant(0, DL, MVT::i32),
DAG.getConstant(0, DL, MVT::i32),
DAG.getConstant(getInvertedCondCode(CC), DL, MVT::i32), NZCV);
}

// Lower @cc flag output via getSETCC.
SDValue AArch64TargetLowering::LowerAsmOutputForConstraint(
SDValue &Chain, SDValue &Glue, const SDLoc &DL,
const AsmOperandInfo &OpInfo, SelectionDAG &DAG) const {
AArch64CC::CondCode Cond = parseConstraintCode(OpInfo.ConstraintCode);
if (Cond == AArch64CC::Invalid)
return SDValue();
// The output variable should be a scalar integer.
if (OpInfo.ConstraintVT.isVector() || !OpInfo.ConstraintVT.isInteger() ||
OpInfo.ConstraintVT.getSizeInBits() < 8)
report_fatal_error("Flag output operand is of invalid type");

// Get NZCV register. Only update chain when copyfrom is glued.
if (Glue.getNode()) {
Glue = DAG.getCopyFromReg(Chain, DL, AArch64::NZCV, MVT::i32, Glue);
Chain = Glue.getValue(1);
} else
Glue = DAG.getCopyFromReg(Chain, DL, AArch64::NZCV, MVT::i32);
// Extract CC code.
SDValue CC = getSETCC(Cond, Glue, DL, DAG);

SDValue Result;

// Truncate or ZERO_EXTEND based on value types.
if (OpInfo.ConstraintVT.getSizeInBits() <= 32)
Result = DAG.getNode(ISD::TRUNCATE, DL, OpInfo.ConstraintVT, CC);
else
Result = DAG.getNode(ISD::ZERO_EXTEND, DL, OpInfo.ConstraintVT, CC);

return Result;
}

/// getConstraintType - Given a constraint letter, return the type of
/// constraint it is for this target.
AArch64TargetLowering::ConstraintType
Expand Down Expand Up @@ -9896,6 +9962,8 @@ AArch64TargetLowering::getConstraintType(StringRef Constraint) const {
} else if (parsePredicateConstraint(Constraint) !=
PredicateConstraint::Invalid)
return C_RegisterClass;
else if (parseConstraintCode(Constraint) != AArch64CC::Invalid)
return C_Other;
return TargetLowering::getConstraintType(Constraint);
}

Expand Down Expand Up @@ -9993,7 +10061,8 @@ AArch64TargetLowering::getRegForInlineAsmConstraint(
: std::make_pair(0U, &AArch64::PPRRegClass);
}
}
if (StringRef("{cc}").equals_insensitive(Constraint))
if (StringRef("{cc}").equals_insensitive(Constraint) ||
parseConstraintCode(Constraint) != AArch64CC::Invalid)
return std::make_pair(unsigned(AArch64::NZCV), &AArch64::CCRRegClass);

// Use the default implementation in TargetLowering to convert the register
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/AArch64/AArch64ISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,12 @@ class AArch64TargetLowering : public TargetLowering {
return TargetLowering::getInlineAsmMemConstraint(ConstraintCode);
}

/// Handle Lowering flag assembly outputs.
SDValue LowerAsmOutputForConstraint(SDValue &Chain, SDValue &Flag,
const SDLoc &DL,
const AsmOperandInfo &Constraint,
SelectionDAG &DAG) const override;

bool shouldExtendGSIndex(EVT VT, EVT &EltTy) const override;
bool shouldRemoveExtendFromGSIndex(EVT IndexVT, EVT DataVT) const override;
bool isVectorLoadExtDesirable(SDValue ExtVal) const override;
Expand Down
14 changes: 14 additions & 0 deletions llvm/test/CodeGen/AArch64/GlobalISel/arm64-fallback.ll
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,20 @@ define i32 @gc_intr() gc "statepoint-example" {
ret i32 %ret
}

declare void @llvm.assume(i1)

; FALLBACK-WITH-REPORT-ERR: <unknown>:0:0: unable to translate instruction: call: ' %0 = tail call { i64, i32 } asm "subs $0, $0, #3", "=r,={@cchi},0,~{dirflag},~{fpsr},~{flags}"(i64 %a)' (in function: inline_asm_with_output_constraint)
; FALLBACK-WITH-REPORT-ERR: warning: Instruction selection used fallback path for inline_asm_with_output_constraint
; FALLBACK-WITH-REPORT-OUT-LABEL: inline_asm_with_output_constraint
define i32 @inline_asm_with_output_constraint(i64 %a) {
entry:
%0 = tail call { i64, i32 } asm "subs $0, $0, #3", "=r,={@cchi},0,~{dirflag},~{fpsr},~{flags}"(i64 %a)
%asmresult1 = extractvalue { i64, i32 } %0, 1
%1 = icmp ult i32 %asmresult1, 2
tail call void @llvm.assume(i1 %1)
ret i32 %asmresult1
}

attributes #1 = { "target-features"="+sve" }
attributes #2 = { "target-features"="+ls64" }

Expand Down
Loading

0 comments on commit 9879e58

Please sign in to comment.