Skip to content

Commit 6712e20

Browse files
anoopkg6anoopkg6uweigand
authored
Add support for flag output operand "=@cc" for SystemZ. (llvm#125970)
Added Support for flag output operand "=@cc", inline assembly constraint for SystemZ. - Clang now accepts "=@cc" assembly operands, and sets 2-bits condition code for output operand for SyatemZ. - Clang currently emits an assertion that flag output operands are boolean values, i.e. in the range [0, 2). Generalize this mechanism to allow targets to specify arbitrary range assertions for any inline assembly output operand. This will be used to assert that SystemZ two-bit condition-code values are in the range [0, 4). - SystemZ backend lowers "@cc" targets by using ipm sequence to extract condition code from PSW. - DAGCombine tries to optimize lowered ipm sequence by combining CCReg and computing effective CCMask and CCValid in combineCCMask for select_ccmask and br_ccmask. - Cost computation is done for merging conditionals for branch instruction in SelectionDAG, as split may cause branches conditions evaluation goes across basic block and difficult to combine. --------- Co-authored-by: anoopkg6 <[email protected]> Co-authored-by: Ulrich Weigand <[email protected]>
1 parent 3793e75 commit 6712e20

File tree

14 files changed

+2867
-100
lines changed

14 files changed

+2867
-100
lines changed

clang/include/clang/Basic/TargetInfo.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,25 @@ class TargetInfo : public TransferrableTargetInfo,
12111211
TiedOperand = N;
12121212
// Don't copy Name or constraint string.
12131213
}
1214+
1215+
// For output operand constraints, the target can set bounds to indicate
1216+
// that the result value is guaranteed to fall within a certain range.
1217+
// This will cause corresponding assertions to be emitted that will allow
1218+
// for potential optimization based of that guarantee.
1219+
//
1220+
// NOTE: This re-uses the `ImmRange` fields to store the range, which are
1221+
// otherwise unused for constraint types used for output operands.
1222+
void setOutputOperandBounds(unsigned Min, unsigned Max) {
1223+
ImmRange.Min = Min;
1224+
ImmRange.Max = Max;
1225+
ImmRange.isConstrained = true;
1226+
}
1227+
std::optional<std::pair<unsigned, unsigned>>
1228+
getOutputOperandBounds() const {
1229+
return ImmRange.isConstrained
1230+
? std::make_pair(ImmRange.Min, ImmRange.Max)
1231+
: std::optional<std::pair<unsigned, unsigned>>();
1232+
}
12141233
};
12151234

12161235
/// Validate register name used for global register variables.

clang/lib/Basic/Targets/AArch64.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,7 @@ bool AArch64TargetInfo::validateAsmConstraint(
15681568
if (const unsigned Len = matchAsmCCConstraint(Name)) {
15691569
Name += Len - 1;
15701570
Info.setAllowsRegister();
1571+
Info.setOutputOperandBounds(0, 2);
15711572
return true;
15721573
}
15731574
}

clang/lib/Basic/Targets/SystemZ.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ bool SystemZTargetInfo::validateAsmConstraint(
9999
case 'T': // Likewise, plus an index
100100
Info.setAllowsMemory();
101101
return true;
102+
case '@':
103+
// CC condition changes.
104+
if (StringRef(Name) == "@cc") {
105+
Name += 2;
106+
Info.setAllowsRegister();
107+
// SystemZ has 2-bits CC, and hence Interval [0, 4).
108+
Info.setOutputOperandBounds(0, 4);
109+
return true;
110+
}
111+
return false;
102112
}
103113
}
104114

@@ -161,6 +171,9 @@ unsigned SystemZTargetInfo::getMinGlobalAlign(uint64_t Size,
161171

162172
void SystemZTargetInfo::getTargetDefines(const LangOptions &Opts,
163173
MacroBuilder &Builder) const {
174+
// Inline assembly supports SystemZ flag outputs.
175+
Builder.defineMacro("__GCC_ASM_FLAG_OUTPUTS__");
176+
164177
Builder.defineMacro("__s390__");
165178
Builder.defineMacro("__s390x__");
166179
Builder.defineMacro("__zarch__");

clang/lib/Basic/Targets/SystemZ.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
136136

137137
std::string convertConstraint(const char *&Constraint) const override {
138138
switch (Constraint[0]) {
139+
case '@': // Flag output operand.
140+
if (llvm::StringRef(Constraint) == "@cc") {
141+
Constraint += 2;
142+
return std::string("{@cc}");
143+
}
144+
break;
139145
case 'p': // Keep 'p' constraint.
140146
return std::string("p");
141147
case 'Z':

clang/lib/Basic/Targets/X86.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,7 @@ bool X86TargetInfo::validateAsmConstraint(
15161516
if (auto Len = matchAsmCCConstraint(Name)) {
15171517
Name += Len - 1;
15181518
Info.setAllowsRegister();
1519+
Info.setOutputOperandBounds(0, 2);
15191520
return true;
15201521
}
15211522
return false;

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,7 +2674,8 @@ EmitAsmStores(CodeGenFunction &CGF, const AsmStmt &S,
26742674
const llvm::ArrayRef<LValue> ResultRegDests,
26752675
const llvm::ArrayRef<QualType> ResultRegQualTys,
26762676
const llvm::BitVector &ResultTypeRequiresCast,
2677-
const llvm::BitVector &ResultRegIsFlagReg) {
2677+
const std::vector<std::optional<std::pair<unsigned, unsigned>>>
2678+
&ResultBounds) {
26782679
CGBuilderTy &Builder = CGF.Builder;
26792680
CodeGenModule &CGM = CGF.CGM;
26802681
llvm::LLVMContext &CTX = CGF.getLLVMContext();
@@ -2685,18 +2686,20 @@ EmitAsmStores(CodeGenFunction &CGF, const AsmStmt &S,
26852686
// ResultRegDests can be also populated by addReturnRegisterOutputs() above,
26862687
// in which case its size may grow.
26872688
assert(ResultTypeRequiresCast.size() <= ResultRegDests.size());
2688-
assert(ResultRegIsFlagReg.size() <= ResultRegDests.size());
2689+
assert(ResultBounds.size() <= ResultRegDests.size());
26892690

26902691
for (unsigned i = 0, e = RegResults.size(); i != e; ++i) {
26912692
llvm::Value *Tmp = RegResults[i];
26922693
llvm::Type *TruncTy = ResultTruncRegTypes[i];
26932694

2694-
if ((i < ResultRegIsFlagReg.size()) && ResultRegIsFlagReg[i]) {
2695-
// Target must guarantee the Value `Tmp` here is lowered to a boolean
2696-
// value.
2697-
llvm::Constant *Two = llvm::ConstantInt::get(Tmp->getType(), 2);
2695+
if ((i < ResultBounds.size()) && ResultBounds[i].has_value()) {
2696+
const auto [LowerBound, UpperBound] = ResultBounds[i].value();
2697+
// FIXME: Support for nonzero lower bounds not yet implemented.
2698+
assert(LowerBound == 0 && "Output operand lower bound is not zero.");
2699+
llvm::Constant *UpperBoundConst =
2700+
llvm::ConstantInt::get(Tmp->getType(), UpperBound);
26982701
llvm::Value *IsBooleanValue =
2699-
Builder.CreateCmp(llvm::CmpInst::ICMP_ULT, Tmp, Two);
2702+
Builder.CreateCmp(llvm::CmpInst::ICMP_ULT, Tmp, UpperBoundConst);
27002703
llvm::Function *FnAssume = CGM.getIntrinsic(llvm::Intrinsic::assume);
27012704
Builder.CreateCall(FnAssume, IsBooleanValue);
27022705
}
@@ -2825,7 +2828,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
28252828
std::vector<llvm::Type *> ArgElemTypes;
28262829
std::vector<llvm::Value*> Args;
28272830
llvm::BitVector ResultTypeRequiresCast;
2828-
llvm::BitVector ResultRegIsFlagReg;
2831+
std::vector<std::optional<std::pair<unsigned, unsigned>>> ResultBounds;
28292832

28302833
// Keep track of inout constraints.
28312834
std::string InOutConstraints;
@@ -2883,8 +2886,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
28832886
ResultRegQualTys.push_back(QTy);
28842887
ResultRegDests.push_back(Dest);
28852888

2886-
bool IsFlagReg = llvm::StringRef(OutputConstraint).starts_with("{@cc");
2887-
ResultRegIsFlagReg.push_back(IsFlagReg);
2889+
ResultBounds.emplace_back(Info.getOutputOperandBounds());
28882890

28892891
llvm::Type *Ty = ConvertTypeForMem(QTy);
28902892
const bool RequiresCast = Info.allowsRegister() &&
@@ -3231,7 +3233,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
32313233

32323234
EmitAsmStores(*this, S, RegResults, ResultRegTypes, ResultTruncRegTypes,
32333235
ResultRegDests, ResultRegQualTys, ResultTypeRequiresCast,
3234-
ResultRegIsFlagReg);
3236+
ResultBounds);
32353237

32363238
// If this is an asm goto with outputs, repeat EmitAsmStores, but with a
32373239
// different insertion point; one for each indirect destination and with
@@ -3242,7 +3244,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
32423244
Builder.SetInsertPoint(Succ, --(Succ->end()));
32433245
EmitAsmStores(*this, S, CBRRegResults[Succ], ResultRegTypes,
32443246
ResultTruncRegTypes, ResultRegDests, ResultRegQualTys,
3245-
ResultTypeRequiresCast, ResultRegIsFlagReg);
3247+
ResultTypeRequiresCast, ResultBounds);
32463248
}
32473249
}
32483250
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
2+
// RUN: %clang_cc1 -O2 -triple s390x-linux -emit-llvm -o - %s | FileCheck %s
3+
4+
// CHECK-LABEL: define dso_local signext range(i32 0, 4) i32 @test(
5+
// CHECK-SAME: i32 noundef signext [[X:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
6+
// CHECK-NEXT: [[ENTRY:.*:]]
7+
// CHECK-NEXT: [[TMP0:%.*]] = tail call { i32, i32 } asm "ahi $0,42\0A", "=d,={@cc},0"(i32 [[X]]) #[[ATTR2:[0-9]+]], !srcloc [[META2:![0-9]+]]
8+
// CHECK-NEXT: [[ASMRESULT1:%.*]] = extractvalue { i32, i32 } [[TMP0]], 1
9+
// CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[ASMRESULT1]], 4
10+
// CHECK-NEXT: tail call void @llvm.assume(i1 [[TMP1]])
11+
// CHECK-NEXT: ret i32 [[ASMRESULT1]]
12+
//
13+
int test(int x) {
14+
int cc;
15+
asm ("ahi %[x],42\n" : [x] "+d"(x), "=@cc" (cc));
16+
return cc;
17+
}
18+
19+
// CHECK-LABEL: define dso_local signext range(i32 0, 2) i32 @test_low_high_transformation(
20+
// CHECK-SAME: i32 noundef signext [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
21+
// CHECK-NEXT: [[ENTRY:.*:]]
22+
// CHECK-NEXT: [[TMP0:%.*]] = tail call { i32, i32 } asm "ahi $0,42\0A", "=d,={@cc},0"(i32 [[X]]) #[[ATTR2]], !srcloc [[META3:![0-9]+]]
23+
// CHECK-NEXT: [[ASMRESULT1:%.*]] = extractvalue { i32, i32 } [[TMP0]], 1
24+
// CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[ASMRESULT1]], 4
25+
// CHECK-NEXT: tail call void @llvm.assume(i1 [[TMP1]])
26+
// CHECK-NEXT: [[TMP2:%.*]] = add nsw i32 [[ASMRESULT1]], -1
27+
// CHECK-NEXT: [[TMP3:%.*]] = icmp ult i32 [[TMP2]], 2
28+
// CHECK-NEXT: [[LOR_EXT:%.*]] = zext i1 [[TMP3]] to i32
29+
// CHECK-NEXT: ret i32 [[LOR_EXT]]
30+
//
31+
int test_low_high_transformation(int x) {
32+
int cc;
33+
asm ("ahi %[x],42\n" : [x] "+d"(x), "=@cc" (cc));
34+
return cc == 1 || cc == 2;
35+
}
36+
37+
// CHECK-LABEL: define dso_local signext range(i32 0, 2) i32 @test_equal_high_transformation(
38+
// CHECK-SAME: i32 noundef signext [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
39+
// CHECK-NEXT: [[ENTRY:.*:]]
40+
// CHECK-NEXT: [[TMP0:%.*]] = tail call { i32, i32 } asm "ahi $0,42\0A", "=d,={@cc},0"(i32 [[X]]) #[[ATTR2]], !srcloc [[META4:![0-9]+]]
41+
// CHECK-NEXT: [[ASMRESULT1:%.*]] = extractvalue { i32, i32 } [[TMP0]], 1
42+
// CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[ASMRESULT1]], 4
43+
// CHECK-NEXT: tail call void @llvm.assume(i1 [[TMP1]])
44+
// CHECK-NEXT: [[TMP2:%.*]] = and i32 [[ASMRESULT1]], 1
45+
// CHECK-NEXT: [[LOR_EXT:%.*]] = xor i32 [[TMP2]], 1
46+
// CHECK-NEXT: ret i32 [[LOR_EXT]]
47+
//
48+
int test_equal_high_transformation(int x) {
49+
int cc;
50+
asm ("ahi %[x],42\n" : [x] "+d"(x), "=@cc" (cc));
51+
return cc == 0 || cc == 2;
52+
}
53+
//.
54+
// CHECK: [[META2]] = !{i64 788}
55+
// CHECK: [[META3]] = !{i64 1670}
56+
// CHECK: [[META4]] = !{i64 2505}
57+
//.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// RUN: %clang -target systemz-unknown-unknown -x c -E -dM -o - %s | FileCheck -match-full-lines %s
2+
// RUN: %clang -target s390x-unknown-unknown -x c -E -dM -o - %s | FileCheck -match-full-lines %s
3+
4+
// CHECK: #define __GCC_ASM_FLAG_OUTPUTS__ 1

llvm/lib/Target/SystemZ/SystemZ.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class SystemZTargetMachine;
2424

2525
namespace SystemZ {
2626
// Condition-code mask values.
27+
const unsigned CCMASK_NONE = 0;
2728
const unsigned CCMASK_0 = 1 << 3;
2829
const unsigned CCMASK_1 = 1 << 2;
2930
const unsigned CCMASK_2 = 1 << 1;

0 commit comments

Comments
 (0)