Skip to content

Commit

Permalink
[Clang][XTHeadVector] Define vector unit-stride load/store intrinsics…
Browse files Browse the repository at this point in the history
… for clang (ruyisdk#33)

* [Clang][XTHeadVector] Define `vle`, `vse` intrinsics for clang

* [Clang][XTHeadVector] Fix `IRName`

* [Clang][XTHeadVector] Add tests
  • Loading branch information
imkiva committed Apr 1, 2024
1 parent e8778dc commit ecbf7bc
Show file tree
Hide file tree
Showing 12 changed files with 1,074 additions and 10 deletions.
4 changes: 0 additions & 4 deletions clang/include/clang/Basic/riscv_vector.td
Original file line number Diff line number Diff line change
Expand Up @@ -467,10 +467,6 @@ defvar EEWList = [["8", "(Log2EEW:3)"],
["32", "(Log2EEW:5)"],
["64", "(Log2EEW:6)"]];

class IsFloat<string type> {
bit val = !or(!eq(type, "x"), !eq(type, "f"), !eq(type, "d"));
}

let SupportOverloading = false,
MaskedPolicyScheme = NonePolicy in {
class RVVVLEMaskBuiltin : RVVOutBuiltin<"m", "mPCUe", "c"> {
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/riscv_vector_common.td
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,7 @@ class RVVBuiltin<string suffix, string prototype, string type_range,
class RVVHeader {
code HeaderCode;
}

class IsFloat<string type> {
bit val = !or(!eq(type, "x"), !eq(type, "f"), !eq(type, "d"));
}
78 changes: 72 additions & 6 deletions clang/include/clang/Basic/riscv_vector_xtheadv.td
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

include "riscv_vector_common.td"

class XRVVBuiltin<string suffix, string prototype, string type_range,
string overloaded_suffix = "">
: RVVBuiltin<suffix, prototype, type_range, overloaded_suffix> {
class RVVOutBuiltin<string suffix, string prototype, string type_range>
: RVVBuiltin<suffix, prototype, type_range> {
let IntrinsicTypes = [-1];
}

multiclass RVVBuiltinSet<string intrinsic_name, string type_range,
Expand All @@ -28,7 +28,7 @@ multiclass RVVBuiltinSet<string intrinsic_name, string type_range,
let Name = NAME # "_" # s_p[0] in {
defvar suffix = s_p[1];
defvar prototype = s_p[2];
def : XRVVBuiltin<suffix, prototype, type_range>;
def : RVVBuiltin<suffix, prototype, type_range>;
}
}
}
Expand All @@ -55,7 +55,7 @@ multiclass RVVIntBinBuiltinSet


//===----------------------------------------------------------------------===//
// 6. Configuration-Setting Instructions
// 6. Configuration-Setting and Utility
//===----------------------------------------------------------------------===//

// Define vread_csr&vwrite_csr described in RVV intrinsics doc.
Expand Down Expand Up @@ -170,7 +170,73 @@ let HasBuiltinAlias = false,
}

//===----------------------------------------------------------------------===//
// 12. Vector Integer Arithmetic Instructions
// 7. Vector Loads and Stores
//===----------------------------------------------------------------------===//

let SupportOverloading = false,
UnMaskedPolicyScheme = HasPassthruOperand in {
multiclass RVVVLEBuiltin<list<string> types> {
let Name = NAME # "_v",
IRName = "th_vle",
MaskedIRName ="th_vle_mask" in {
foreach type = types in {
// `vPCe` is type `const T * -> VectorType`
def : RVVOutBuiltin<"v", "vPCe", type>;
if !not(IsFloat<type>.val) then {
// `UvPCUe` is type `const unsigned T * -> unsigned VectorType`
def : RVVOutBuiltin<"Uv", "UvPCUe", type>;
}
}
}
}
}

let HasMaskedOffOperand = false,
MaskedPolicyScheme = NonePolicy,
ManualCodegen = [{
if (IsMasked) {
// Builtin: (mask, ptr, value, vl). Intrinsic: (value, ptr, mask, vl)
std::swap(Ops[0], Ops[2]);
} else {
// Builtin: (ptr, value, vl). Intrinsic: (value, ptr, vl)
std::swap(Ops[0], Ops[1]);
}
Ops[1] = Builder.CreateBitCast(Ops[1], Ops[0]->getType()->getPointerTo());
if (IsMasked)
IntrinsicTypes = {Ops[0]->getType(), Ops[3]->getType()};
else
IntrinsicTypes = {Ops[0]->getType(), Ops[2]->getType()};
}] in {
multiclass RVVVSEBuiltin<list<string> types> {
let Name = NAME # "_v",
IRName = "th_vse",
MaskedIRName = "th_vse_mask" in {
foreach type = types in {
// `0Pev` is type `T * -> VectorType -> void`
def : RVVBuiltin<"v", "0Pev", type>;
if !not(IsFloat<type>.val) then {
// `0PUeUv` is type `unsigned T * -> unsigned VectorType -> void`
def : RVVBuiltin<"Uv", "0PUeUv", type>;
}
}
}
}
}

// 7.1. Vector Unit-Stride Operations
// TODO: vlb, vlh, vlw
defm th_vle8: RVVVLEBuiltin<["c"]>;
defm th_vle16: RVVVLEBuiltin<["s","x"]>;
defm th_vle32: RVVVLEBuiltin<["i","f"]>;
defm th_vle64: RVVVLEBuiltin<["l","d"]>;
// TODO: vsb, vsh, vsw
defm th_vse8 : RVVVSEBuiltin<["c"]>;
defm th_vse16: RVVVSEBuiltin<["s","x"]>;
defm th_vse32: RVVVSEBuiltin<["i","f"]>;
defm th_vse64: RVVVSEBuiltin<["l","d"]>;

//===----------------------------------------------------------------------===//
// 12. Vector Integer Arithmetic Operations
//===----------------------------------------------------------------------===//

let UnMaskedPolicyScheme = HasPassthruOperand in {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// RUN: %clang_cc1 -triple riscv64 -target-feature +xtheadvector \
// RUN: -O0 -emit-llvm %s -o - | FileCheck %s

#include <riscv_vector.h>

typedef unsigned char uint8_t;

// CHECK-LABEL: define dso_local void @memcpy_v(ptr noundef %dst, ptr noundef %src, i32 noundef signext %n) #0 {
// CHECK-NEXT: entry:
// CHECK-NEXT: %dst.addr = alloca ptr, align 8
// CHECK-NEXT: %src.addr = alloca ptr, align 8
// CHECK-NEXT: %n.addr = alloca i32, align 4
// CHECK-NEXT: %vl = alloca i32, align 4
// CHECK-NEXT: %vec_src = alloca <vscale x 32 x i8>, align 1
// CHECK-NEXT: store ptr %dst, ptr %dst.addr, align 8
// CHECK-NEXT: store ptr %src, ptr %src.addr, align 8
// CHECK-NEXT: store i32 %n, ptr %n.addr, align 4
// CHECK-NEXT: br label %for.cond
//
// CHECK-LABEL: for.cond: ; preds = %for.inc, %entry
// CHECK-NEXT: %0 = load i32, ptr %n.addr, align 4
// CHECK-NEXT: %cmp = icmp sgt i32 %0, 0
// CHECK-NEXT: br i1 %cmp, label %for.body, label %for.end
//
// CHECK-LABEL: for.body: ; preds = %for.cond
// CHECK-NEXT: %1 = load i32, ptr %n.addr, align 4
// CHECK-NEXT: %conv = sext i32 %1 to i64
// CHECK-NEXT: %2 = call i64 @llvm.riscv.th.vsetvl.i64(i64 %conv, i64 0, i64 2)
// CHECK-NEXT: %conv1 = trunc i64 %2 to i32
// CHECK-NEXT: store i32 %conv1, ptr %vl, align 4
// CHECK-NEXT: %3 = load ptr, ptr %src.addr, align 8
// CHECK-NEXT: %4 = load i32, ptr %vl, align 4
// CHECK-NEXT: %conv2 = sext i32 %4 to i64
// CHECK-NEXT: %5 = call <vscale x 32 x i8> @llvm.riscv.th.vle.nxv32i8.i64(<vscale x 32 x i8> poison, ptr %3, i64 %conv2)
// CHECK-NEXT: store <vscale x 32 x i8> %5, ptr %vec_src, align 1
// CHECK-NEXT: %6 = load ptr, ptr %dst.addr, align 8
// CHECK-NEXT: %7 = load <vscale x 32 x i8>, ptr %vec_src, align 1
// CHECK-NEXT: %8 = load i32, ptr %vl, align 4
// CHECK-NEXT: %conv3 = sext i32 %8 to i64
// CHECK-NEXT: call void @llvm.riscv.th.vse.nxv32i8.i64(<vscale x 32 x i8> %7, ptr %6, i64 %conv3)
// CHECK-NEXT: br label %for.inc
//
// CHECK-LABEL: for.inc: ; preds = %for.body
// CHECK-NEXT: %9 = load i32, ptr %vl, align 4
// CHECK-NEXT: %10 = load i32, ptr %n.addr, align 4
// CHECK-NEXT: %sub = sub nsw i32 %10, %9
// CHECK-NEXT: store i32 %sub, ptr %n.addr, align 4
// CHECK-NEXT: %11 = load i32, ptr %vl, align 4
// CHECK-NEXT: %12 = load ptr, ptr %src.addr, align 8
// CHECK-NEXT: %idx.ext = sext i32 %11 to i64
// CHECK-NEXT: %add.ptr = getelementptr inbounds i8, ptr %12, i64 %idx.ext
// CHECK-NEXT: store ptr %add.ptr, ptr %src.addr, align 8
// CHECK-NEXT: %13 = load i32, ptr %vl, align 4
// CHECK-NEXT: %14 = load ptr, ptr %dst.addr, align 8
// CHECK-NEXT: %idx.ext4 = sext i32 %13 to i64
// CHECK-NEXT: %add.ptr5 = getelementptr inbounds i8, ptr %14, i64 %idx.ext4
// CHECK-NEXT: store ptr %add.ptr5, ptr %dst.addr, align 8
// CHECK-NEXT: br label %for.cond, !llvm.loop !4
//
// CHECK-LABEL: for.end: ; preds = %for.cond
// CHECK-NEXT: ret void
// CHECK-NEXT: }

void memcpy_v(uint8_t *dst, const uint8_t *src, int n) {
for (int vl; n > 0; n -= vl, src += vl, dst += vl) {
vl = __riscv_vsetvl_e8m4(n);
vuint8m4_t vec_src = __riscv_th_vle8_v_u8m4(src, vl);
__riscv_th_vse8_v_u8m4(dst, vec_src, vl);
}
}
126 changes: 126 additions & 0 deletions clang/test/CodeGen/RISCV/rvv0p71-intrinsics-handcrafted/vle16.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// RUN: %clang_cc1 -triple riscv64 -target-feature +xtheadvector \
// RUN: -disable-O0-optnone -emit-llvm %s -o - | \
// RUN: opt -S -passes=mem2reg | \
// RUN: FileCheck --check-prefix=CHECK-RV64 %s

#include <riscv_vector.h>

// CHECK-RV64-LABEL: define dso_local <vscale x 4 x half> @test_th_vle16_v_f16m1
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 4 x half> @llvm.riscv.th.vle.nxv4f16.i64(<vscale x 4 x half> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 4 x half> [[TMP0]]
//
vfloat16m1_t test_th_vle16_v_f16m1(const _Float16 *base, size_t vl) {
return __riscv_th_vle16_v_f16m1(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 8 x half> @test_th_vle16_v_f16m2
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 8 x half> @llvm.riscv.th.vle.nxv8f16.i64(<vscale x 8 x half> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 8 x half> [[TMP0]]
//
vfloat16m2_t test_th_vle16_v_f16m2(const _Float16 *base, size_t vl) {
return __riscv_th_vle16_v_f16m2(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 16 x half> @test_th_vle16_v_f16m4
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 16 x half> @llvm.riscv.th.vle.nxv16f16.i64(<vscale x 16 x half> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 16 x half> [[TMP0]]
//
vfloat16m4_t test_th_vle16_v_f16m4(const _Float16 *base, size_t vl) {
return __riscv_th_vle16_v_f16m4(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 32 x half> @test_th_vle16_v_f16m8
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 32 x half> @llvm.riscv.th.vle.nxv32f16.i64(<vscale x 32 x half> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 32 x half> [[TMP0]]
//
vfloat16m8_t test_th_vle16_v_f16m8(const _Float16 *base, size_t vl) {
return __riscv_th_vle16_v_f16m8(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 4 x i16> @test_th_vle16_v_i16m1
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 4 x i16> @llvm.riscv.th.vle.nxv4i16.i64(<vscale x 4 x i16> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 4 x i16> [[TMP0]]
//
vint16m1_t test_th_vle16_v_i16m1(const int16_t *base, size_t vl) {
return __riscv_th_vle16_v_i16m1(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 8 x i16> @test_th_vle16_v_i16m2
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 8 x i16> @llvm.riscv.th.vle.nxv8i16.i64(<vscale x 8 x i16> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 8 x i16> [[TMP0]]
//
vint16m2_t test_th_vle16_v_i16m2(const int16_t *base, size_t vl) {
return __riscv_th_vle16_v_i16m2(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 16 x i16> @test_th_vle16_v_i16m4
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 16 x i16> @llvm.riscv.th.vle.nxv16i16.i64(<vscale x 16 x i16> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 16 x i16> [[TMP0]]
//
vint16m4_t test_th_vle16_v_i16m4(const int16_t *base, size_t vl) {
return __riscv_th_vle16_v_i16m4(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 32 x i16> @test_th_vle16_v_i16m8
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 32 x i16> @llvm.riscv.th.vle.nxv32i16.i64(<vscale x 32 x i16> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 32 x i16> [[TMP0]]
//
vint16m8_t test_th_vle16_v_i16m8(const int16_t *base, size_t vl) {
return __riscv_th_vle16_v_i16m8(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 4 x i16> @test_th_vle16_v_u16m1
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 4 x i16> @llvm.riscv.th.vle.nxv4i16.i64(<vscale x 4 x i16> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 4 x i16> [[TMP0]]
//
vuint16m1_t test_th_vle16_v_u16m1(const uint16_t *base, size_t vl) {
return __riscv_th_vle16_v_u16m1(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 8 x i16> @test_th_vle16_v_u16m2
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 8 x i16> @llvm.riscv.th.vle.nxv8i16.i64(<vscale x 8 x i16> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 8 x i16> [[TMP0]]
//
vuint16m2_t test_th_vle16_v_u16m2(const uint16_t *base, size_t vl) {
return __riscv_th_vle16_v_u16m2(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 16 x i16> @test_th_vle16_v_u16m4
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 16 x i16> @llvm.riscv.th.vle.nxv16i16.i64(<vscale x 16 x i16> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 16 x i16> [[TMP0]]
//
vuint16m4_t test_th_vle16_v_u16m4(const uint16_t *base, size_t vl) {
return __riscv_th_vle16_v_u16m4(base, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 32 x i16> @test_th_vle16_v_u16m8
// CHECK-RV64-SAME: (ptr noundef [[BASE:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 32 x i16> @llvm.riscv.th.vle.nxv32i16.i64(<vscale x 32 x i16> poison, ptr [[BASE]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 32 x i16> [[TMP0]]
//
vuint16m8_t test_th_vle16_v_u16m8(const uint16_t *base, size_t vl) {
return __riscv_th_vle16_v_u16m8(base, vl);
}
Loading

0 comments on commit ecbf7bc

Please sign in to comment.