| // Copyright 2023 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| // Host tool to generate the intrinsic header and toolchain op files |
| |
| // Encoding generator "kelvin-opc.[c,h]" and kelvin_intrinsics.h |
| |
| #include <assert.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| FILE* fc_ = nullptr; |
| FILE* fh_ = nullptr; |
| FILE* fi_ = nullptr; |
| |
| enum OpCode { |
| kOpNone, |
| kOpS, |
| kOpST, |
| kOpD, |
| kOpDS, |
| kOpDST, |
| kOpVd, |
| kOpVdS, |
| kOpVdT, |
| kOpVdST, |
| kOpVdVs, |
| kOpVdVsT, |
| kOpVdVsVt, |
| kOpVdVsTVr, |
| kOpVdVsVtVr, |
| }; |
| |
| constexpr int kPad1 = 24; |
| constexpr int kPad2 = 56; |
| |
| constexpr uint32_t kVxMask = 1; |
| constexpr uint32_t kVxShift = 1; |
| constexpr uint32_t kVxvMask = 1; |
| constexpr uint32_t kVxvShift = 2; |
| |
| constexpr uint32_t kFunc1Shift = 2; |
| constexpr uint32_t kFunc1Mask = 0x7; |
| constexpr uint32_t kFunc2Shift = 26; |
| constexpr uint32_t kFunc2Mask = 0x3f; |
| constexpr uint32_t kFunc3Shift = 26; |
| constexpr uint32_t kFunc3Mask = 0x3f; |
| constexpr uint32_t kFunc4LoShift = 3; |
| constexpr uint32_t kFunc4LoMask = 0x3; |
| constexpr uint32_t kFunc4HiShift = (12 - 2); |
| constexpr uint32_t kFunc4HiMask = 0xc; |
| |
| constexpr uint32_t kMMask = 0x1; |
| constexpr uint32_t kMShift = 5; |
| constexpr uint32_t kSizeMask = 0x3; |
| constexpr uint32_t kSizeShift = 12; |
| constexpr uint32_t kVs1Mask = 0x3f; |
| constexpr uint32_t kVs1Shift = 14; |
| constexpr uint32_t kVs2Mask = 0x3f; |
| constexpr uint32_t kVs2Shift = 20; |
| |
| void Space() { |
| for (auto fp : {fc_, fh_, fi_}) { |
| fprintf(fp, "\n"); |
| } |
| } |
| |
| // Add same comment to all three output. |
| void Comment(std::string s) { |
| for (auto fp : {fc_, fh_, fi_}) { |
| fprintf(fp, "// %s\n", s.c_str()); |
| } |
| } |
| |
| void Header() { |
| // OSS header. |
| for (auto fp : {fc_, fh_, fi_}) { |
| fprintf(fp, |
| "// Copyright 2023 Google LLC\n" |
| "// Licensed under the Apache License, Version 2.0, see LICENSE " |
| "for details.\n" |
| "// SPDX-License-Identifier: Apache-2.0\n" |
| "//\n" |
| "// clang-format off\n\n"); |
| } |
| fprintf(fh_, |
| "#ifndef PATCHES_KELVIN_KELVIN_OPC_H_\n" |
| "#define PATCHES_KELVIN_KELVIN_OPC_H_\n\n"); |
| |
| // intrinsic header file header |
| fprintf(fi_, |
| "// Kelvin instruction intrinsics\n\n" |
| "#ifndef CRT_KELVIN_INTRINSICS_H_\n" |
| "#define CRT_KELVIN_INTRINSICS_H_\n\n"); |
| } |
| |
| void Footer() { |
| fprintf(fh_, "#endif // PATCHES_KELVIN_KELVIN_OPC_H_\n"); |
| fprintf(fi_, "#endif // CRT_KELVIN_INTRINSICS_H_\n"); |
| |
| for (auto fp : {fc_, fh_, fi_}) { |
| // kelvin-ops.c need to set clang-format on differently. |
| std::string close_string = (fp == fc_) ? "\n " : ""; |
| fprintf(fp, "%s// clang-format on\n", close_string.c_str()); |
| } |
| } |
| |
| // Generate a 32-bit ISA mask/match bitmap based on the base string |
| uint32_t VMatchMask(const char* a, bool is_mask = true) { |
| const int n = strlen(a); |
| int i = 0; |
| int p = 0; |
| uint32_t v = 0; |
| |
| while (p < n && i < 32) { |
| const char c = a[p]; |
| p++; |
| if (c == '_') { |
| continue; |
| } |
| if (c == '1') { |
| v |= 1u << (31 - i); |
| } |
| if (is_mask && c == '0') { |
| v |= 1u << (31 - i); |
| } |
| i++; |
| } |
| |
| if (i != 32) { |
| fprintf(stderr, "ERROR: %s\n", a); |
| exit(-1); |
| } |
| |
| return v; |
| } |
| |
| uint32_t VMask(const char* a) { return VMatchMask(a, /*is_mask=*/true); } |
| |
| uint32_t VMatch(const char* a) { return VMatchMask(a, /*is_mask=*/false); } |
| |
| // Create match bitmap for bit[31...26] |
| void MmOpField(uint8_t bits, uint32_t& match, uint32_t& mask) { |
| match |= (bits & kFunc2Mask) << kFunc2Shift; |
| } |
| |
| // Create match bitmap for bit[13...12, 4...3] |
| void MmOpField2(uint8_t bits, uint32_t& match, uint32_t& mask) { |
| match |= (bits & kFunc4LoMask) << kFunc4LoShift; |
| match |= (bits & kFunc4HiMask) << kFunc4HiShift; |
| } |
| |
| // Create match bitmap for bit[13...12] |
| void MmSize(uint8_t sz, uint32_t& match, uint32_t& mask) { |
| match |= (sz & kSizeMask) << kSizeShift; |
| } |
| |
| // Create match bitmap for bit[5] |
| void MmStripmine(uint32_t& match, uint32_t& mask) { |
| match |= kMMask << kMShift; |
| } |
| |
| // Create match and mask bitmaps for .vx. bit[1] == 1, and bit[25...20] |
| // are used to record a scalar register |
| void MmVx(uint32_t& match, uint32_t& mask) { |
| match |= 1 << kVxShift; |
| mask |= 1 << (kVs2Shift + 5); // bit[25] == 0 |
| } |
| |
| // Create match and mask bitmaps for .vxv, bit[25...20] are used to record a |
| // scalar register, so bit[25] = 0 |
| void MmVxV(uint32_t& match, uint32_t& mask) { |
| match |= 1 << kVxvShift; |
| mask |= 1 << (kVs2Shift + 5); // bit[25] == 0 |
| } |
| |
| void MmVxVAlternate(uint32_t& match, uint32_t& mask) { |
| match |= 1 << (kVs2Shift + 5); // bit[25] == 1 |
| } |
| |
| // Create mask bitmap for .xx, both bit[25...20] and bit[19...14] |
| // are used by scalar registers. |
| void MmXx(uint32_t& match, uint32_t& mask) { |
| mask |= 1 << (kVs1Shift + 0); // bit[14] == 0 |
| mask |= 1 << (kVs2Shift + 5); // bit[25] == 0 |
| } |
| |
| // Create match and mask bitmaps for bit[19...14] == 0 |
| void MmXs1IsZero(uint32_t& match, uint32_t& mask) { |
| match &= ~(kVs1Mask << kVs1Shift); |
| mask |= kVs1Mask << kVs1Shift; |
| } |
| |
| // Create match and mask bitmaps for bit[25...20] == 0 |
| void MmXs2IsZero(uint32_t& match, uint32_t& mask) { |
| match &= ~(kVs2Mask << kVs2Shift); |
| mask |= kVs2Mask << kVs2Shift; |
| } |
| |
| void Pad(std::string& s, int padding) { |
| for (int i = s.length(); i < padding; ++i) { |
| s += " "; |
| } |
| } |
| |
| void TypeHelper(const int type, std::string& op) { |
| switch (type) { |
| case 0: |
| op += ".b"; |
| break; |
| case 1: |
| op += ".h"; |
| break; |
| case 2: |
| op += ".w"; |
| break; |
| } |
| } |
| |
| // Check if the op is within the op_group. |
| bool CheckVariant(const std::string& op_name, |
| const std::vector<std::string>& op_group) { |
| for (auto op : op_group) { |
| if (op_name == op) return true; |
| } |
| return false; |
| } |
| |
| // Encode opcode with match, mask, and intrinsic macro entry. |
| void EncodeCH(std::string name, uint32_t match, uint32_t mask, OpCode type) { |
| std::string hname = name; |
| std::string iname = name; |
| |
| // .h opcode mask/match entry. Capitalized. |
| for_each(hname.begin(), hname.end(), [](char& c) { |
| c = ::toupper(c); |
| if (c == '.') c = '_'; |
| }); |
| |
| for_each(iname.begin(), iname.end(), [](char& c) { |
| c = ::tolower(c); |
| if (c == '.') c = '_'; |
| }); |
| |
| // .c opcode entry. |
| std::string co; |
| co += "{\"" + name + "\","; |
| Pad(co, kPad1); |
| co += "0, INSN_CLASS_K, \""; |
| switch (type) { |
| case kOpNone: |
| break; |
| case kOpS: |
| co += "s"; |
| break; |
| case kOpST: |
| co += "s,t"; |
| break; |
| case kOpD: |
| co += "d"; |
| break; |
| case kOpDS: |
| co += "d,s"; |
| break; |
| case kOpDST: |
| co += "d,s,t"; |
| break; |
| case kOpVd: |
| co += "Vd"; |
| break; |
| case kOpVdS: |
| co += "Vd,s"; |
| break; |
| case kOpVdT: |
| co += "Vd,t"; |
| break; |
| case kOpVdST: |
| co += "Vd,s,t"; |
| break; |
| case kOpVdVs: |
| co += "Vd,Vs"; |
| break; |
| case kOpVdVsT: |
| co += "Vd,Vs,t"; |
| break; |
| case kOpVdVsVt: |
| co += "Vd,Vs,Vt"; |
| break; |
| case kOpVdVsTVr: |
| co += "Vd,Vs,t,Vr"; |
| break; |
| case kOpVdVsVtVr: |
| co += "Vd,Vs,Vt,Vr"; |
| break; |
| default: |
| assert(false && "EncodeCH::kOp"); |
| } |
| co += "\","; |
| Pad(co, kPad2); |
| co += "MATCH_" + hname + ", MASK_" + hname + ", match_opcode, 0 },"; |
| // intrinsics |
| std::string io; |
| io = "#define " + iname; |
| |
| // Append .xx to scalar opcodes intrinsic definitions. |
| // eg. avoid std::min/max collisions. |
| switch (type) { |
| case kOpDS: |
| if (iname.find("_x") == std::string::npos) { |
| io += "_x"; |
| } |
| break; |
| case kOpDST: |
| if (iname.find("_x") == std::string::npos) { |
| io += "_xx"; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| switch (type) { |
| case kOpNone: |
| io += "()"; |
| break; |
| case kOpS: |
| io += "(s)"; |
| break; |
| case kOpST: |
| io += "(s, t)"; |
| break; |
| case kOpD: |
| io += "(d)"; |
| break; |
| case kOpDS: |
| io += "(d, s)"; |
| break; |
| case kOpDST: |
| io += "(d, s, t)"; |
| break; |
| case kOpVd: |
| io += "(Vd)"; |
| break; |
| case kOpVdS: |
| io += "(Vd, s)"; |
| break; |
| case kOpVdT: |
| io += "(Vd, t)"; |
| break; |
| case kOpVdST: |
| io += "(Vd, s, t)"; |
| break; |
| case kOpVdVs: |
| io += "(Vd, Vs)"; |
| break; |
| case kOpVdVsT: |
| io += "(Vd, Vs, t)"; |
| break; |
| case kOpVdVsVt: |
| io += "(Vd, Vs, Vt)"; |
| break; |
| case kOpVdVsTVr: |
| io += "(Vd, Vs, t, Vr)"; |
| break; |
| case kOpVdVsVtVr: |
| io += "(Vd, Vs, Vt, Vr)"; |
| break; |
| default: |
| assert(false && "EncodeCH::kOp"); |
| } |
| |
| Pad(io, 36); |
| io += "__asm__ __volatile__"; |
| |
| switch (type) { |
| case kOpNone: |
| io += "(\"" + name + "\");"; |
| break; |
| case kOpS: |
| io += "(ARGS_F_A(\"" + name + "\", %0) : : \"r\"(s))"; |
| break; |
| case kOpST: |
| io += "(ARGS_F_A(\"" + name + "\", %0, %1) : : \"r\"(s), \"r\"(t))"; |
| break; |
| case kOpD: |
| io += "(ARGS_F_A(\"" + name + "\", %0) : \"=r\"(d) : )"; |
| break; |
| case kOpDS: |
| io += "(ARGS_F_A_A(\"" + name + "\", %0, %1) : \"=r\"(d) : \"r\"(s))"; |
| break; |
| case kOpDST: |
| io += "(ARGS_F_A_A_A(\"" + name + |
| "\", %0, %1, %2) : \"=r\"(d) : \"r\"(s), \"r\"(t))"; |
| break; |
| case kOpVd: |
| io += "(ARGS_F_A(\"" + name + "\", Vd) : : )"; |
| break; |
| case kOpVdS: |
| io += "(ARGS_F_A_A(\"" + name + "\", Vd, %0) : : \"r\"(s))"; |
| break; |
| case kOpVdT: |
| io += "(ARGS_F_A_A(\"" + name + "\", Vd, %0) : : \"r\"(t))"; |
| break; |
| case kOpVdST: |
| io += |
| "(ARGS_F_A_A_A(\"" + name + "\", Vd, %0, %1) : : \"r\"(s), \"r\"(t))"; |
| break; |
| case kOpVdVs: |
| io += "(ARGS_F_A_A(\"" + name + "\", Vd, Vs))"; |
| break; |
| case kOpVdVsT: |
| io += "(ARGS_F_A_A_A(\"" + name + "\", Vd, Vs, %0) : : \"r\"(t))"; |
| break; |
| case kOpVdVsVt: |
| io += "(ARGS_F_A_A_A(\"" + name + "\", Vd, Vs, Vt))"; |
| break; |
| case kOpVdVsTVr: |
| io += "(ARGS_F_A_A_A_A(\"" + name + "\", Vd, Vs, %0, Vr) : : \"r\"(t))"; |
| break; |
| case kOpVdVsVtVr: |
| io += "(ARGS_F_A_A_A_A(\"" + name + "\", Vd, Vs, Vt, Vr))"; |
| break; |
| default: |
| assert(false && "EncodeCH::kOp"); |
| } |
| |
| // Order these instructions. |
| std::vector<std::string> always{ |
| "ebreak", "ecall", "eexit", "eyield", "ectxsw", "mret", "mpause", |
| "flog", "slog", "klog", "clog", "vld", "vst", "vsq"}; |
| for (auto op : always) { |
| if (name.find(op) != std::string::npos) { |
| std::string v = "volatile"; |
| size_t pos = io.find(v) + v.length(); |
| io.insert(pos, "_always"); |
| } |
| } |
| |
| // Update load/store. |
| std::vector<std::string> ldst{"vld", "vst"}; |
| for (auto op : ldst) { |
| if (name.find(op) != std::string::npos) { |
| // Update base. |
| std::string p = "p_x"; |
| auto pos = io.find(p); |
| if (pos != std::string::npos) { |
| std::string r = ": \"r\"(s)"; |
| int pos = io.find(r); |
| io.insert(pos, "\"=r\"(s) "); |
| |
| pos = io.find(": \"r\"(s), \"r\"(t)"); |
| if (pos > 0) { |
| // : "r"(s), "r"(t) # from |
| // : "r"(t), "0"(s) # to |
| io[pos + 6] = 't'; |
| io[pos + 11] = '0'; |
| io[pos + 14] = 's'; |
| } else { |
| // : "r"(s) # from |
| // : "0"(s) # to |
| pos = io.find(": \"r\"(s)"); |
| io[pos + 3] = '0'; |
| } |
| } |
| // Memory side-effects. |
| pos = io.length() - 1; |
| io.insert(pos, " : \"memory\""); |
| } |
| } |
| |
| // file write |
| Pad(hname, 20); |
| |
| fprintf(fc_, "%s\n", co.c_str()); |
| |
| fprintf(fh_, "#define MATCH_%s 0x%08x\n", hname.c_str(), match); |
| fprintf(fh_, "#define MASK_%s 0x%08x\n", hname.c_str(), mask); |
| |
| fprintf(fi_, "%s\n", io.c_str()); |
| } |
| |
| void Encode(std::string name, std::string op) { |
| EncodeCH(name, VMatch(op.c_str()), VMask(op.c_str()), kOpNone); |
| } |
| |
| void EncodeS(std::string name, std::string op) { |
| EncodeCH(name, VMatch(op.c_str()), VMask(op.c_str()), kOpS); |
| } |
| |
| void EncodeGetVl() { |
| const char* base = "00010_00_xxxxx_xxxxx_000_xxxxx_11101_11"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| for (auto m : {false, true}) { |
| for (int srcs = 0; srcs < 3; ++srcs) { |
| for (int sz = 0; sz < 3; ++sz) { |
| std::string op = "get"; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| OpCode opcode; |
| if (srcs == 0) { |
| op += "maxvl"; |
| } else { |
| op += "vl"; |
| } |
| TypeHelper(sz, op); |
| switch (srcs) { |
| case 0: { |
| opcode = kOpD; |
| MmXs1IsZero(vmatch, vmask); |
| MmXs2IsZero(vmatch, vmask); |
| } break; |
| case 1: { |
| opcode = kOpDS; |
| MmXs2IsZero(vmatch, vmask); |
| op += ".x"; |
| } break; |
| case 2: { |
| opcode = kOpDST; |
| op += ".xx"; |
| } break; |
| default: |
| break; |
| } |
| MmXx(vmatch, vmask); |
| // Set size match bitmap. |
| constexpr uint32_t kSystemSzShift = 25; |
| vmatch |= (sz & kSizeMask) << kSystemSzShift; |
| if (m) { |
| op += ".m"; |
| constexpr uint32_t kSystemMShift = 27; |
| vmatch |= (kMMask << kSystemMShift); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, opcode); |
| } |
| } |
| // Encoded files have spaces between the normal mode and the stripmine mode. |
| if (!m) Space(); |
| } |
| } |
| |
| void EncodeVLdSt(std::string name, const int index) { |
| const char* base = "000000_xxxxxx_xxxxxx_00_xxxxxx_0_111_11"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| bool is_stq = (name == "vstq"); |
| |
| for (int sz = 0; sz < 3; ++sz) { |
| for (auto m : {false, true}) { |
| for (int mode = 0; mode < 8; ++mode) { |
| for (auto xs2_is_zero : {false, true}) { |
| if (mode != 0 && xs2_is_zero) continue; |
| // Can't set s and l without p (no .ls mode) |
| if (mode == 3) continue; |
| // stq only has .s and .sp mode |
| if (mode != 2 && mode != 6 && is_stq) continue; |
| |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| TypeHelper(sz, op); |
| MmSize(sz, vmatch, vmask); |
| |
| switch (mode) { |
| case 0: |
| if (xs2_is_zero) op += ".p"; |
| break; |
| case 1: |
| op += ".l"; |
| break; |
| case 2: |
| op += ".s"; |
| break; |
| case 4: |
| op += ".p"; |
| break; |
| case 5: |
| op += ".lp"; |
| break; |
| case 6: |
| op += ".sp"; |
| break; |
| case 7: |
| op += ".tp"; |
| break; |
| default: |
| break; |
| } |
| MmOpField(index | mode | (xs2_is_zero ? 0b100 : 0), vmatch, vmask); |
| |
| op += mode ? ".xx" : ".x"; |
| if (mode == 0) { |
| MmXs2IsZero(vmatch, vmask); |
| } |
| MmXx(vmatch, vmask); |
| |
| if (m) { |
| op += ".m"; |
| MmStripmine(vmatch, vmask); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, mode ? kOpVdST : kOpVdS); |
| } |
| } |
| } |
| } |
| } |
| |
| void EncodeVDup(std::string name, const int index) { |
| const char* base = "000000_xxxxxx_xxxxxx_00_xxxxxx_0_111_11"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| for (int sz = 0; sz < 3; ++sz) { |
| for (auto m : {false, true}) { |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| TypeHelper(sz, op); |
| MmSize(sz, vmatch, vmask); |
| |
| MmOpField(index, vmatch, vmask); |
| |
| op += ".x"; |
| MmXs1IsZero(vmatch, vmask); |
| MmXx(vmatch, vmask); |
| |
| if (m) { |
| op += ".m"; |
| MmStripmine(vmatch, vmask); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, kOpVdT); |
| } |
| } |
| } |
| |
| void EncodeVCget(std::string name, const int index) { |
| const char* base = "000000_xxxxxx_xxxxxx_00_xxxxxx_0_111_11"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| const int sz = 2; |
| |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| MmSize(sz, vmatch, vmask); |
| MmOpField(index, vmatch, vmask); |
| MmXs1IsZero(vmatch, vmask); |
| MmXs2IsZero(vmatch, vmask); |
| |
| EncodeCH(op.c_str(), vmatch, vmask, kOpVd); |
| } |
| |
| void Encode000(std::string name, const int index) { |
| const char* base = "000000_xxxxxx_xxxxxx_00_xxxxxx_0_000_00"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| bool has_u = |
| CheckVariant(name, {"vlt", "vle", "vgt", "vge", "vabsd", "vmax", "vmin"}); |
| |
| bool no_vv = (name == "vrsub"); |
| |
| for (int sz = 0; sz < 3; ++sz) { |
| for (auto m : {false, true}) { |
| for (auto u : {false, true}) { |
| if (u && !has_u) continue; |
| // The group has .{u}.{vv, vx}.{m} variations. |
| for (auto x : {false, true}) { |
| if (!x && no_vv) continue; |
| |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| TypeHelper(sz, op); |
| MmSize(sz, vmatch, vmask); |
| |
| if (u) { |
| op += ".u"; |
| } |
| MmOpField(index | u, vmatch, vmask); |
| |
| op += x ? ".vx" : ".vv"; |
| if (x) { |
| MmVx(vmatch, vmask); |
| } |
| |
| if (m) { |
| op += ".m"; |
| MmStripmine(vmatch, vmask); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, x ? kOpVdVsT : kOpVdVsVt); |
| } |
| } |
| } |
| } |
| } |
| |
| void Encode100(std::string name, const int index) { |
| const char* base = "000000_xxxxxx_xxxxxx_00_xxxxxx_0_100_00"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| bool has_v_only = CheckVariant(name, {"vpadd", "vpsub"}); |
| |
| bool no_sz_0 = |
| CheckVariant(name, {"vaddw", "vsubw", "vacc", "vpadd", "vpsub"}); |
| |
| bool has_r = CheckVariant(name, {"vhadd", "vhsub"}); |
| |
| for (int sz = 0; sz < 3; ++sz) { |
| if (sz == 0 && no_sz_0) continue; |
| // This group has [.r, .u, .ur].[v, vv, vx].{m} variations. |
| for (auto m : {false, true}) { |
| for (auto u : {false, true}) { |
| for (auto r : {false, true}) { |
| if (r && !has_r) continue; |
| for (auto v : {false, true}) { |
| // Skip this config for the match encoding. |
| if (v && !has_v_only) continue; |
| bool is_v_only = !v && has_v_only; |
| for (auto is_vx : {false, true}) { |
| // .v only code with !v && is_vx |
| if (!(!v && is_vx) && has_v_only) continue; |
| |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| TypeHelper(sz, op); |
| MmSize(sz, vmatch, vmask); |
| |
| if (u) { |
| op += ".u"; |
| } |
| |
| if (has_r) { |
| op += (r && !u) ? ".r" : (r ? "r" : ""); |
| } |
| |
| MmOpField(index | (u ? 0b1 : 0) | (has_r && r ? 0b10 : 0), vmatch, |
| vmask); |
| |
| op += is_v_only ? ".v" : is_vx ? ".vx" : ".vv"; |
| if (is_v_only) { |
| MmXs2IsZero(vmatch, vmask); |
| } |
| if (is_vx) { |
| MmVx(vmatch, vmask); |
| } |
| |
| if (m) { |
| op += ".m"; |
| MmStripmine(vmatch, vmask); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, |
| is_v_only ? kOpVdVs |
| : is_vx ? kOpVdVsT |
| : kOpVdVsVt); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void Encode001(std::string name, const int index) { |
| const char* base = "000000_xxxxxx_xxxxxx_00_xxxxxx_0_001_00"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| bool is_typeless = |
| CheckVariant(name, {"vnot", "vmv", "acset", "actr", "adwinit"}); |
| |
| bool is_typeless_vv = CheckVariant(name, {"vand", "vor", "vxor", "vmvp"}); |
| |
| bool is_v_only = CheckVariant(name, {"vnot", "vclb", "vclz", "vcpop", "vmv", |
| "acset", "actr", "adwinit"}); |
| |
| bool no_m = CheckVariant(name, {"acset", "actr", "adwinit"}); |
| |
| for (int sz = 0; sz < 3; ++sz) { |
| if (is_typeless && sz != 0) continue; |
| // The group has .{v, vv, vx}.{m} variations. |
| for (auto m : {false, true}) { |
| if (m && no_m) continue; |
| for (auto x : {false, true}) { |
| // Skip this for the match encoding. |
| if (!x && is_v_only) continue; |
| // Skip this for the match encoding. |
| if (!x && is_typeless_vv && sz != 0) continue; |
| |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| if (!is_typeless && !(is_typeless_vv && !x)) { |
| TypeHelper(sz, op); |
| } |
| MmSize(sz, vmatch, vmask); |
| |
| MmOpField(index, vmatch, vmask); |
| |
| op += is_v_only ? ".v" : x ? ".vx" : ".vv"; |
| if (is_v_only) { |
| MmXs2IsZero(vmatch, vmask); |
| } |
| if (x) { |
| MmVx(vmatch, vmask); |
| } |
| |
| if (m) { |
| op += ".m"; |
| MmStripmine(vmatch, vmask); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, |
| is_v_only ? kOpVdVs |
| : x ? kOpVdVsT |
| : kOpVdVsVt); |
| } |
| } |
| } |
| } |
| |
| void Encode010(std::string name, const int index) { |
| const char* base = "000000_xxxxxx_xxxxxx_00_xxxxxx_0_010_00"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| bool has_r = CheckVariant( |
| name, {"vsha", "vshl", "vsrans", "vsransu", "vsraqs", "vsraqsu"}); |
| |
| bool no_16bit = CheckVariant(name, {"vsraqs", "vsraqsu"}); |
| |
| bool no_32bit = |
| CheckVariant(name, {"vsrans", "vsransu", "vsraqs", "vsraqsu"}); |
| |
| for (int sz = 0; sz < 3; ++sz) { |
| if (no_16bit && sz == 1) continue; |
| if (no_32bit && sz == 2) continue; |
| // The group has .{r}.{vv, vx}.{m} variants |
| for (auto m : {false, true}) { |
| for (auto r : {false, true}) { |
| if (r && !has_r) continue; |
| for (auto x : {false, true}) { |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| TypeHelper(sz, op); |
| MmSize(sz, vmatch, vmask); |
| |
| if (r) { |
| op += ".r"; |
| } |
| MmOpField(index | (r ? 0b10 : 0), vmatch, vmask); |
| |
| op += x ? ".vx" : ".vv"; |
| if (x) { |
| MmVx(vmatch, vmask); |
| } |
| |
| if (m) { |
| op += ".m"; |
| MmStripmine(vmatch, vmask); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, x ? kOpVdVsT : kOpVdVsVt); |
| } |
| } |
| } |
| } |
| } |
| |
| void Encode011(std::string name, const int index) { |
| const char* base = "000000_xxxxxx_xxxxxx_00_xxxxxx_0_011_00"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| bool has_u = CheckVariant(name, {"vmuls", "vmulw"}); |
| bool has_r = CheckVariant(name, {"vmulh", "vmulhu", "vdmulh"}); |
| bool has_n = (name == "vdmulh"); |
| assert(!(has_n && !has_r)); |
| |
| for (int sz = 0; sz < 3; ++sz) { |
| // The group support .{u, r, rn}.{vx, vv},{m} variant |
| for (auto m : {false, true}) { |
| for (auto u : {false, true}) { |
| if (u && !has_u) continue; |
| for (auto r : {false, true}) { |
| if (r && !has_r) continue; |
| for (auto n : {false, true}) { |
| if (n && !(has_n && r)) continue; |
| for (auto x : {false, true}) { |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| TypeHelper(sz, op); |
| MmSize(sz, vmatch, vmask); |
| |
| if (u) { |
| op += ".u"; |
| } |
| if (r) { |
| op += ".r"; |
| if (n) { |
| op += "n"; |
| } |
| } |
| MmOpField(index | u | n | (r ? 0b10 : 0), vmatch, vmask); |
| |
| op += x ? ".vx" : ".vv"; |
| if (x) { |
| MmVx(vmatch, vmask); |
| } |
| |
| if (m) { |
| op += ".m"; |
| MmStripmine(vmatch, vmask); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, x ? kOpVdVsT : kOpVdVsVt); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void Encode110(std::string name, const int index) { |
| const char* base = "000000_xxxxxx_xxxxxx_00_xxxxxx_0_110_00"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| std::vector<std::string> slide_group = {"vsliden", "vslidehn", "vslidevn", |
| "vslidep", "vslidehp", "vslidevp"}; |
| |
| bool has_range = CheckVariant(name, slide_group); |
| int range = has_range ? 3 : 0; |
| bool is_m0 = CheckVariant(name, {"vsliden", "vslidep"}); |
| bool is_m1 = |
| CheckVariant(name, {"vslidehn", "vslidehp", "vslidevn", "vslidevp"}); |
| |
| for (int sz = 0; sz < 3; ++sz) { |
| for (auto m : {false, true}) { |
| if (!m && is_m1) continue; |
| if (m && is_m0) continue; |
| for (int n = 0; n <= range; ++n) { |
| for (auto x : {false, true}) { |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| TypeHelper(sz, op); |
| MmSize(sz, vmatch, vmask); |
| |
| MmOpField(index | n, vmatch, vmask); |
| |
| if (has_range) { |
| op += "." + std::to_string(n + 1); |
| } |
| |
| op += x ? ".vx" : ".vv"; |
| if (x) { |
| MmVx(vmatch, vmask); |
| } |
| |
| if (m) { |
| op += ".m"; |
| MmStripmine(vmatch, vmask); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, x ? kOpVdVsT : kOpVdVsVt); |
| } |
| } |
| } |
| } |
| } |
| |
| void EncodeVvv(std::string name, const int index, const bool alt = false) { |
| const char* base = "xxxxxx_xxxxxx_xxxxxx_00_xxxxxx_0_00_001"; |
| const uint32_t vmatch_base = VMatch(base); |
| const uint32_t vmask_base = VMask(base); |
| |
| std::string op = name; |
| uint32_t vmatch = vmatch_base; |
| uint32_t vmask = vmask_base; |
| |
| MmOpField2(index, vmatch, vmask); |
| |
| op += ".vxv"; |
| MmVxV(vmatch, vmask); |
| |
| if (alt) { |
| MmVxVAlternate(vmatch, vmask); |
| } |
| |
| EncodeCH(op.c_str(), vmatch, vmask, kOpVdVsTVr); |
| } |
| |
| int main() { |
| fc_ = fopen("kelvin-opc.c", "wt"); |
| fh_ = fopen("kelvin-opc.h", "wt"); |
| fi_ = fopen("kelvin_intrinsics.h", "wt"); |
| assert(fc_); |
| assert(fh_); |
| assert(fi_); |
| |
| Header(); |
| |
| Encode("eexit", "00000_01_00000_00000_000_00000_11100_11"); |
| Encode("eyield", "00000_10_00000_00000_000_00000_11100_11"); |
| Encode("ectxsw", "00000_11_00000_00000_000_00000_11100_11"); |
| Encode("mpause", "00001_00_00000_00000_000_00000_11100_11"); |
| Space(); |
| EncodeS("flog", "01111_00_00000_xxxxx_000_00000_11101_11"); |
| EncodeS("slog", "01111_00_00000_xxxxx_001_00000_11101_11"); |
| EncodeS("clog", "01111_00_00000_xxxxx_010_00000_11101_11"); |
| EncodeS("klog", "01111_00_00000_xxxxx_011_00000_11101_11"); |
| Space(); |
| Encode("flushall", "00100_11_00000_00000_000_00000_11101_11"); |
| EncodeS("flushat", "00100_11_00000_xxxxx_000_00000_11101_11"); |
| Space(); |
| |
| EncodeGetVl(); |
| Space(); |
| |
| Comment("111 Load/Store"); |
| EncodeVLdSt("vld", 0); |
| EncodeVLdSt("vst", 8); |
| EncodeVDup("vdup", 16); |
| EncodeVCget("vcget", 20); |
| EncodeVLdSt("vstq", 26); |
| |
| Space(); |
| Comment("000 Arithmetic"); |
| Encode000("vadd", 0); |
| Encode000("vsub", 1); |
| Encode000("vrsub", 2); |
| Encode000("veq", 6); |
| Encode000("vne", 7); |
| Encode000("vlt", 8); |
| Encode000("vle", 10); |
| Encode000("vgt", 12); |
| Encode000("vge", 14); |
| Encode000("vabsd", 16); |
| Encode000("vmax", 18); |
| Encode000("vmin", 20); |
| Encode000("vadd3", 24); |
| |
| Space(); |
| Comment("100 Arithmetic2"); |
| Encode100("vadds", 0); |
| Encode100("vsubs", 2); |
| Encode100("vaddw", 4); |
| Encode100("vsubw", 6); |
| Encode100("vacc", 10); |
| Encode100("vpadd", 12); |
| Encode100("vpsub", 14); |
| Encode100("vhadd", 16); |
| Encode100("vhsub", 20); |
| |
| Space(); |
| Comment("001 Logical"); |
| Encode001("vand", 0); |
| Encode001("vor", 1); |
| Encode001("vxor", 2); |
| Encode001("vnot", 3); |
| Encode001("vrev", 4); |
| Encode001("vror", 5); |
| Encode001("vclb", 8); |
| Encode001("vclz", 9); |
| Encode001("vcpop", 10); |
| Encode001("vmv", 12); |
| Encode001("vmvp", 13); |
| Encode001("acset", 16); |
| Encode001("actr", 17); |
| Encode001("adwinit", 18); |
| |
| Space(); |
| Comment("010 Shift"); |
| Encode010("vsll", 1); |
| Encode010("vsra", 2); |
| Encode010("vsrl", 3); |
| Encode010("vsha", 8); |
| Encode010("vshl", 9); |
| Encode010("vsrans", 16); |
| Encode010("vsransu", 17); |
| Encode010("vsraqs", 24); |
| Encode010("vsraqsu", 25); |
| |
| Space(); |
| Comment("011 Mul/Div"); |
| Encode011("vmul", 0); |
| Encode011("vmuls", 2); |
| Encode011("vmulw", 4); |
| Encode011("vmulh", 8); |
| Encode011("vmulhu", 9); |
| Encode011("vdmulh", 16); |
| Encode011("vmacc", 20); |
| Encode011("vmadd", 21); |
| |
| Space(); |
| Comment("110 Shuffle"); |
| Encode110("vsliden", 0); |
| Encode110("vslidevn", 0); |
| Encode110("vslidehn", 4); |
| Encode110("vslidep", 8); |
| Encode110("vslidevp", 8); |
| Encode110("vslidehp", 12); |
| Encode110("vsel", 16); |
| Encode110("vevn", 24); |
| Encode110("vodd", 25); |
| Encode110("vevnodd", 26); |
| Encode110("vzip", 28); |
| |
| Space(); |
| Comment("3arg"); |
| EncodeVvv("aconv", 8, true); |
| EncodeVvv("adwconv", 10, true); |
| EncodeVvv("vdwconv", 10); |
| |
| Footer(); |
| |
| fclose(fc_); |
| fclose(fh_); |
| fclose(fi_); |
| fc_ = nullptr; |
| fh_ = nullptr; |
| fi_ = nullptr; |
| |
| return 0; |
| } |