diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1783,6 +1783,15 @@ let Documentation = [RISCVInterruptDocs]; } +def RISCVVPolicy : InheritableAttr, TargetSpecificAttr { + let Spellings = [Clang<"rvv_policy">]; + let Subjects = SubjectList<[Function]>; + let Args = [EnumArgument<"Policy", "PolicyType", + ["tumu", "tamu", "tuma", "tama"], + ["TUMU", "TAMU", "TUMA", "TAMA"]>]; + let Documentation = [RISCVVPolicyDocs]; +} + // This is not a TargetSpecificAttr so that is silently accepted and // ignored on other targets as encouraged by the OpenCL spec. // diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2143,6 +2143,19 @@ }]; } +def RISCVVPolicyDocs : Documentation { + let Category = DocCatFunction; + let Heading = "RISC-V vector tail/mask policy"; + let Content = [{ +Users could use the attribute to specify the policy of destination tail and +destination inactive masked-off elements in the vector operations. There are +two kinds of policies described in the vector specification. One is undisturbed. +It will retain the value they previously held. Another is agnostic. It will +retain the value they previously held or are overwritten with 1s. It is intended +for use only inside ``riscv_*.h``. + }]; +} + def AVRInterruptDocs : Documentation { let Category = DocCatFunction; let Heading = "interrupt (AVR)"; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -18608,6 +18608,10 @@ Intrinsic::ID ID = Intrinsic::not_intrinsic; unsigned NF = 1; constexpr unsigned TAIL_UNDISTURBED = 0; + constexpr unsigned TAIL_AGNOSTIC = 0b01; + constexpr unsigned MASK_AGNOSTIC = 0b10; + auto *PolicyAttr = E->getCalleeDecl()->getAttr(); + size_t PolicyValue; // Required for overloaded intrinsics. llvm::SmallVector IntrinsicTypes; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5369,6 +5369,23 @@ D->addAttr(::new (S.Context) BuiltinAliasAttr(S.Context, AL, Ident)); } +static void handleRISCVVPolicyAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (!AL.isArgIdent(0)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type) + << AL << 0 << AANT_ArgumentIdentifier; + return; + } + + RISCVVPolicyAttr::PolicyType Policy; + IdentifierInfo *II = AL.getArgAsIdent(0)->Ident; + if (!RISCVVPolicyAttr::ConvertStrToPolicyType(II->getName(), Policy)) { + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) << AL << II; + return; + } + + D->addAttr(::new (S.Context) RISCVVPolicyAttr(S.Context, AL, Policy)); +} + //===----------------------------------------------------------------------===// // Checker-specific attribute handlers. //===----------------------------------------------------------------------===// @@ -8520,6 +8537,10 @@ case ParsedAttr::AT_UsingIfExists: handleSimpleAttribute(S, D, AL); break; + + case ParsedAttr::AT_RISCVVPolicy: + handleRISCVVPolicyAttr(S, D, AL); + break; } } diff --git a/clang/test/CodeGen/RISCV/rvv-intrinsics/vadd-policy.c b/clang/test/CodeGen/RISCV/rvv-intrinsics/vadd-policy.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/RISCV/rvv-intrinsics/vadd-policy.c @@ -0,0 +1,42 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// REQUIRES: riscv-registered-target +// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-feature +d -target-feature +experimental-v \ +// RUN: -target-feature +experimental-zfh -disable-O0-optnone -emit-llvm %s -o - | opt -S -mem2reg | FileCheck --check-prefix=CHECK-RV64 %s + +#include + +// CHECK-RV64-LABEL: @test_vadd_vv_i8m1_tama( +// CHECK-RV64-NEXT: entry: +// CHECK-RV64-NEXT: [[TMP0:%.*]] = call @llvm.riscv.vadd.mask.nxv8i8.nxv8i8.i64( [[MASKEDOFF:%.*]], [[OP1:%.*]], [[OP2:%.*]], [[MASK:%.*]], i64 [[VL:%.*]], i64 3) +// CHECK-RV64-NEXT: ret [[TMP0]] +// +vint8m1_t test_vadd_vv_i8m1_tama(vbool8_t mask, vint8m1_t maskedoff, vint8m1_t op1, vint8m1_t op2, size_t vl) { + return vadd_vv_i8m1_tama(mask, maskedoff, op1, op2, vl); +} + +// CHECK-RV64-LABEL: @test_vadd_vv_i8m1_tamu( +// CHECK-RV64-NEXT: entry: +// CHECK-RV64-NEXT: [[TMP0:%.*]] = call @llvm.riscv.vadd.mask.nxv8i8.nxv8i8.i64( [[MASKEDOFF:%.*]], [[OP1:%.*]], [[OP2:%.*]], [[MASK:%.*]], i64 [[VL:%.*]], i64 1) +// CHECK-RV64-NEXT: ret [[TMP0]] +// +vint8m1_t test_vadd_vv_i8m1_tamu(vbool8_t mask, vint8m1_t maskedoff, vint8m1_t op1, vint8m1_t op2, size_t vl) { + return vadd_vv_i8m1_tamu(mask, maskedoff, op1, op2, vl); +} + +// CHECK-RV64-LABEL: @test_vadd_vv_i8m1_tuma( +// CHECK-RV64-NEXT: entry: +// CHECK-RV64-NEXT: [[TMP0:%.*]] = call @llvm.riscv.vadd.mask.nxv8i8.nxv8i8.i64( [[MASKEDOFF:%.*]], [[OP1:%.*]], [[OP2:%.*]], [[MASK:%.*]], i64 [[VL:%.*]], i64 2) +// CHECK-RV64-NEXT: ret [[TMP0]] +// +vint8m1_t test_vadd_vv_i8m1_tuma(vbool8_t mask, vint8m1_t maskedoff, vint8m1_t op1, vint8m1_t op2, size_t vl) { + return vadd_vv_i8m1_tuma(mask, maskedoff, op1, op2, vl); +} + +// CHECK-RV64-LABEL: @test_vadd_vv_i8m1_tumu( +// CHECK-RV64-NEXT: entry: +// CHECK-RV64-NEXT: [[TMP0:%.*]] = call @llvm.riscv.vadd.mask.nxv8i8.nxv8i8.i64( [[MASKEDOFF:%.*]], [[OP1:%.*]], [[OP2:%.*]], [[MASK:%.*]], i64 [[VL:%.*]], i64 0) +// CHECK-RV64-NEXT: ret [[TMP0]] +// +vint8m1_t test_vadd_vv_i8m1_tumu(vbool8_t mask, vint8m1_t maskedoff, vint8m1_t op1, vint8m1_t op2, size_t vl) { + return vadd_vv_i8m1_tumu(mask, maskedoff, op1, op2, vl); +} diff --git a/clang/test/CodeGen/RISCV/rvv-intrinsics/vadd-tu.c b/clang/test/CodeGen/RISCV/rvv-intrinsics/vadd-tu.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/RISCV/rvv-intrinsics/vadd-tu.c @@ -0,0 +1,24 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// REQUIRES: riscv-registered-target +// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-feature +d -target-feature +experimental-v \ +// RUN: -target-feature +experimental-zfh -disable-O0-optnone -emit-llvm %s -o - | opt -S -mem2reg | FileCheck --check-prefix=CHECK-RV64 %s + +#include + +// CHECK-RV64-LABEL: @test_vadd_vv_i8m1_tu( +// CHECK-RV64-NEXT: entry: +// CHECK-RV64-NEXT: [[TMP0:%.*]] = call @llvm.riscv.vadd.tu.nxv8i8.nxv8i8.i64( [[DEST:%.*]], [[OP1:%.*]], [[OP2:%.*]], i64 [[VL:%.*]]) +// CHECK-RV64-NEXT: ret [[TMP0]] +// +vint8m1_t test_vadd_vv_i8m1_tu(vint8m1_t dest, vint8m1_t op1, vint8m1_t op2, size_t vl) { + return vadd_vv_i8m1_tu(dest, op1, op2, vl); +} + +// CHECK-RV64-LABEL: @test_vadd_vx_i8m1_tu( +// CHECK-RV64-NEXT: entry: +// CHECK-RV64-NEXT: [[TMP0:%.*]] = call @llvm.riscv.vadd.tu.nxv8i8.i8.i64( [[DEST:%.*]], [[OP1:%.*]], i8 [[OP2:%.*]], i64 [[VL:%.*]]) +// CHECK-RV64-NEXT: ret [[TMP0]] +// +vint8m1_t test_vadd_vx_i8m1_tu(vint8m1_t dest, vint8m1_t op1, int8_t op2, size_t vl) { + return vadd_vx_i8m1_tu(dest, op1, op2, vl); +} diff --git a/clang/utils/TableGen/RISCVVEmitter.cpp b/clang/utils/TableGen/RISCVVEmitter.cpp --- a/clang/utils/TableGen/RISCVVEmitter.cpp +++ b/clang/utils/TableGen/RISCVVEmitter.cpp @@ -204,6 +204,10 @@ // Emit the macros for mapping C/C++ intrinsic function to builtin functions. void emitIntrinsicFuncDef(raw_ostream &o) const; + // Emit the declarations for mapping C/C++ intrinsic function to builtin + // functions. + void emitIntrinsicWithPolicyFuncDef(raw_ostream &o) const; + // Emit the mangled function definition. void emitMangledFuncDef(raw_ostream &o) const; }; @@ -835,9 +839,28 @@ if (isMask()) { if (hasVL()) { OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);\n"; - if (hasPolicy()) - OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType()," - " TAIL_UNDISTURBED));\n"; + if (hasPolicy()) { + OS << " if (PolicyAttr) {\n"; + OS << " switch (PolicyAttr->getPolicy()) {\n"; + OS << " default:\n"; + OS << " PolicyValue = 0;\n"; + OS << " break;\n"; + OS << " case RISCVVPolicyAttr::TAMU:\n"; + OS << " PolicyValue = TAIL_AGNOSTIC;\n"; + OS << " break;\n"; + OS << " case RISCVVPolicyAttr::TUMA:\n"; + OS << " PolicyValue = MASK_AGNOSTIC;\n"; + OS << " break;\n"; + OS << " case RISCVVPolicyAttr::TAMA:\n"; + OS << " PolicyValue = MASK_AGNOSTIC | TAIL_AGNOSTIC;\n"; + OS << " break;\n"; + OS << " }\n"; + OS << " } else {\n"; + OS << " PolicyValue = 0;\n"; + OS << " }\n"; + OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(), " + "PolicyValue));\n"; + } } else { OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end());\n"; } @@ -873,6 +896,30 @@ OS << ");\n"; } +void RVVIntrinsic::emitIntrinsicWithPolicyFuncDef(raw_ostream &OS) const { + if (!isMask()) + return; + + const char *policySuffix[] = {"tumu", "tamu", "tuma", "tama"}; + + for (unsigned i = 0; i < 4; ++i) { + OS << "__rvv_ai "; + OS << "__attribute__((__clang_builtin_alias__("; + OS << "__builtin_rvv_" << getBuiltinName() << ")))\n"; + OS << "__attribute__((rvv_policy(" << policySuffix[i] << ")))\n"; + StringRef IntrinsicName = getName().substr(0, getName().size() - 2); + OS << OutputType->getTypeStr() << " " << IntrinsicName << "_" + << policySuffix[i] << "("; + // Emit function arguments + if (!InputTypes.empty()) { + ListSeparator LS; + for (unsigned i = 0; i < InputTypes.size(); ++i) + OS << LS << InputTypes[i]->getTypeStr(); + } + OS << ");\n"; + } +} + void RVVIntrinsic::emitMangledFuncDef(raw_ostream &OS) const { OS << "__attribute__((__clang_builtin_alias__("; OS << "__builtin_rvv_" << getBuiltinName() << ")))\n"; @@ -989,6 +1036,10 @@ Inst.emitIntrinsicFuncDef(OS); }); + emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { + Inst.emitIntrinsicWithPolicyFuncDef(OS); + }); + OS << "#undef __rvv_ai\n\n"; OS << "#define __riscv_v_intrinsic_overloading 1\n";