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