1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "berberis/intrinsics/vector_intrinsics.h"
18
19 #include <cstdint>
20 #include <tuple>
21
22 namespace berberis::intrinsics {
23
24 namespace {
25
26 constexpr uint64_t kVill = uint64_t{1} << 63;
27 constexpr uint64_t kVtypeNoVill = 0b1'1'111'111;
28
29 // Note: only 3bit vsew and 3bit vlmul are verified here.
30 // Vill is verified elsewhere, vma/vta are always valid and other bits are reserved and thus
31 // should be ignored for forward compatibility.
VtypeToVlMax(uint8_t vtype)32 inline uint64_t VtypeToVlMax(uint8_t vtype) {
33 constexpr uint8_t kVtypeToVlMax[64] = {
34 16, 32, 64, 128, 0, 2, 4, 8,
35 8, 16, 32, 64, 0, 1, 2, 4,
36 4, 8, 16, 32, 0, 0, 1, 2,
37 2, 4, 8, 16, 0, 0, 0, 1,
38 0, 0, 0, 0, 0, 0, 0, 0,
39 0, 0, 0, 0, 0, 0, 0, 0,
40 0, 0, 0, 0, 0, 0, 0, 0,
41 0, 0, 0, 0, 0, 0, 0, 0};
42 return kVtypeToVlMax[vtype & 0b111'111];
43 }
44
AvlToVl(uint64_t avl,uint64_t vlmax)45 inline uint64_t AvlToVl(uint64_t avl, uint64_t vlmax) {
46 if (avl <= vlmax) {
47 return avl;
48 } else if (avl >= 2 * vlmax) {
49 return vlmax;
50 } else {
51 return (avl + 1) / 2;
52 }
53 }
54
55 } // namespace
56
Vsetivli(uint8_t avl,uint16_t vtype)57 std::tuple<uint64_t, uint64_t> Vsetivli(uint8_t avl, uint16_t vtype) {
58 return Vsetvli(avl, vtype);
59 }
60
Vsetvl(uint64_t avl,uint64_t vtype)61 std::tuple<uint64_t, uint64_t> Vsetvl(uint64_t avl, uint64_t vtype) {
62 uint64_t vlmax = VtypeToVlMax(vtype);
63 if (vlmax == 0) {
64 return {0, kVill};
65 }
66 // Documentation is unclear about what should we do if someone attempts to set vill flag.
67 // Clear it out for now.
68 return {AvlToVl(avl, vlmax), vtype & kVtypeNoVill};
69 }
70
Vsetvli(uint64_t avl,uint16_t vtype)71 std::tuple<uint64_t, uint64_t> Vsetvli(uint64_t avl, uint16_t vtype) {
72 uint64_t vlmax = VtypeToVlMax(vtype);
73 if (vlmax == 0) {
74 return {0, kVill};
75 }
76 // We wouldn't set vill bit with vsetivli instruction because range of immediates is too small.
77 // Thus unlike Vsetvlmax we don't have anything to filter out here.
78 return {AvlToVl(avl, vlmax), vtype};
79 }
80
Vsetvlmax(uint64_t vtype)81 std::tuple<uint64_t, uint64_t> Vsetvlmax(uint64_t vtype) {
82 return Vsetvl(~0ULL, vtype);
83 }
84
Vsetvlimax(uint16_t vtype)85 std::tuple<uint64_t, uint64_t> Vsetvlimax(uint16_t vtype) {
86 return Vsetvli(~0ULL, vtype);
87 }
88
Vtestvl(uint8_t vl_orig,uint64_t vtype_orig,uint64_t vtype_new)89 std::tuple<uint64_t, uint64_t> Vtestvl(uint8_t vl_orig, uint64_t vtype_orig, uint64_t vtype_new) {
90 if (vtype_orig & kVill) {
91 return {0, kVill};
92 }
93 uint64_t vlmax_orig = VtypeToVlMax(vtype_orig);
94 uint64_t vlmax_new = VtypeToVlMax(vtype_new);
95 if (vlmax_orig != vlmax_new) {
96 return {0, kVill};
97 }
98 return {vl_orig, vtype_new & kVtypeNoVill};
99 }
100
Vtestvli(uint8_t vl_orig,uint64_t vtype_orig,uint16_t vtype_new)101 std::tuple<uint64_t, uint64_t> Vtestvli(uint8_t vl_orig, uint64_t vtype_orig, uint16_t vtype_new) {
102 return Vtestvl(vl_orig, vtype_orig, vtype_new);
103 }
104
105 } // namespace berberis::intrinsics
106