From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8C7A131352D for ; Wed, 8 Jul 2026 03:36:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.51 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783481794; cv=none; b=V5dwIzC6T20/ExjdLsgg8e3w/8uTgRW9ElRPwTfGp7ZBxJpkaIap52eCAcrb+imFKHT6DaQy4BjPLog9jRCrcbWOPxTBsdK8CcpVEteuy4xfpp0+5KqVDF9T7G6AWYYMAeXmaYLaOUlL6QSzGcU57yAuCAWL6OW+tCIkoQCD/9E= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783481794; c=relaxed/simple; bh=HdYvgOObcCco2wGxxKODFPVHUKHN14IgKdwifmPma+4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PQ4tqrHZDDi3D8pzKgbA1PWIHNyJH0v6cHf8achnOqUKyiHceWwlZbb/R8wt11DSfozQ/Y5o7a2RIkglQJLurZAKMf42Ktbwx7Ry4DcSlEqlJh9jggr7qqNXtVjg1H6SSjszPLgAZJpN9qMv5WumLgIOjPw+hM0VeE8luW3lSM4= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=iKfM2cNz; arc=none smtp.client-ip=209.85.216.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="iKfM2cNz" Received: by mail-pj1-f51.google.com with SMTP id 98e67ed59e1d1-3811074a8c5so181140a91.1 for ; Tue, 07 Jul 2026 20:36:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783481781; x=1784086581; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=COoyB1QUx/kn7jDFOhvpHTBTplgrDIeMEVoVVb1nOo8=; b=iKfM2cNzCnM3TEITHYFEXCyRnqQGjQhqMOdZTdspxpDicoo3EPjyRuZ2m23NP+y5bO zv/wDQxASh//4vCIT1gZuGhss0wsnpERhksKQPNL4WYAxH9EjxNvIcfSJ8ZFbwG9HC45 qmNFDS4Fa/XjAniE8vuac8zp4TDBZANiahg+IafJ1j9qBbiqL6+I5j0/FaOb2wpHrtRn 6Ex7+JpmfwfH2fJrqtTNwUtzXv4xz38AtvGpixvBQiz1K2KqfM+j4TetoVkxYN4Cnme6 +/wN1y+SV7McvUX8zQB0Ewos8pDKNQZg8jPaxC3IpQVu9DYlcVu/eSM7lx7kHJv5TMV5 fORQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783481781; x=1784086581; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=COoyB1QUx/kn7jDFOhvpHTBTplgrDIeMEVoVVb1nOo8=; b=ZI7dx+/MnmDs2TawBvnC5vPwK4aXsiASfe3GABc5GBrxN6z467AwgkU3BRVPaMe2ot cCaQXysZs8ETEcxWxT/EP1dvWcJKYiVXpRMr/WfBi+kVtFLrHkLjrkhqTp9putr4KGCK 5+YTfNy25IZgstpVPi3vaXahhadmgzjQUQC3HnL9goQynO9xIER9EL2QDOzjAhM1gjvD l+/q4w5V+ICSI3J7R7u+OAktsQVia7dST810U+ICV/fzGoGNwBE+9NMzPqGufPPE/eIA YzE/9xq/AEgzXTrBq9K872hVNABinpfEupUMASDc49xJUqpBhlPcXaFLl4bE4ox1Arl5 jXGw== X-Forwarded-Encrypted: i=1; AHgh+RofNkZCL8dEbmd1sOZ5+c6VTflsmQSp/wCy8lRD3e3lXNmfzZfyZNnbPn9aAP3FaMJohQq5AC3V9Ix7jC8=@vger.kernel.org X-Gm-Message-State: AOJu0YwHUxtN4YTBVxHx+wX1E1EM2PdHU3RZ3UDIJGr2lgSXF1S5qQOc tkZONzbUU66K3Do5yWCd81EohC92RywAuezFT33KGsIxpKflNsJf4jSx X-Gm-Gg: AfdE7cmimRwHFFHZqa747tzlOQzsvvCDzEs3EZEEyUWwgpVvlI88wqjqNAToEztuI5x FTjw2uVevpWY7Fuki6U1AYnXv+ubDVgvAVhTcBugFAvfq5WMCGkLqz3JQSa1OmqzqRwCI5LUZZa u5MSbBnm5eelBYzI/6U4U8eFu9wGBDrYcncChmcFnijLltHFQZeKYsp4gh4SE86FXkeL9ouBZAF csAQFlqN/GmetUaPsNNjIP/RLecARr2TzFPAQiD88AmpR1MOwyUkUybEi0YWMOGRTQ1hCvsIEik cTfaFbpi7N9wTagv+ezku4s7v54ux5A/cZliezVCC8L4nyMimfi1vOV4RxkJEK1g56AB4RcQFZS Q5KN1FG8QrqNDfA1Z14zITHH+U3kIatt62v8PDn3gQm7kSsrnVeOcOEBZhNktgmViOTALOeqEwf qiCuoinA/4VUtKt33LIZ1Dvtrc2BpMYxZc83qvAOWC+Ekit6iXGA== X-Received: by 2002:a17:90b:2685:b0:380:f85c:94b4 with SMTP id 98e67ed59e1d1-3893fb711c5mr736121a91.7.1783481781087; Tue, 07 Jul 2026 20:36:21 -0700 (PDT) Received: from [10.0.0.65] ([2601:647:6700:64d0::94ac]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-31174accae5sm21092850eec.29.2026.07.07.20.36.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 07 Jul 2026 20:36:20 -0700 (PDT) From: Charlie Jenkins Date: Tue, 07 Jul 2026 20:34:24 -0700 Subject: [PATCH v3 01/17] riscv: Introduce instruction table generation Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260707-riscv_insn_table-v3-1-3f8e5f75ba97@gmail.com> References: <20260707-riscv_insn_table-v3-0-3f8e5f75ba97@gmail.com> In-Reply-To: <20260707-riscv_insn_table-v3-0-3f8e5f75ba97@gmail.com> To: Paul Walmsley , Palmer Dabbelt , Nam Cao , Alexandre Ghiti , Anup Patel , Atish Patra , Conor Dooley , Paolo Bonzini , Andrew Morton , Shuah Khan , =?utf-8?q?Radim_Kr=C4=8Dm=C3=A1=C5=99?= , Jesse Taube Cc: linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org, kvm@vger.kernel.org, kvm-riscv@lists.infradead.org, linux-kselftest@vger.kernel.org, Charlie Jenkins X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1783481777; l=123010; i=thecharlesjenkins@gmail.com; s=20260605; h=from:subject:message-id; bh=HdYvgOObcCco2wGxxKODFPVHUKHN14IgKdwifmPma+4=; b=ghDAO2KAQfPKTKWUzO9BrLLX99Gd6FulhfmUDBCdO5QK4AuvnLdPcXv5MfkLE0qMsQUrByGOS Ls2R5iKVrkLCZ+mYEeZq0o3iwydPIolWkM/2Sa5LRBulaRKX8yxyfC0 X-Developer-Key: i=thecharlesjenkins@gmail.com; a=ed25519; pk=ajnnRQ98PIdwKp4HeMkq9U32okYbnh6Zb4G3o5XXvkg= Eliminate the need to hand-write riscv instructions by using a shell script to autogenerate a header from an instruction table. This is modeled after the syscall table infrastructure. The table is generated externally by riscv-unified-db [1], but is in a simple format to make it possible to use other tools or modify manually. [1] https://github.com/riscv-software-src/riscv-unified-db Signed-off-by: Charlie Jenkins --- This change immediately modifies all of the riscv_insn_* macros. I don't have a clean way of sharing my test cases but what I did was compare the old and new versions of the functions using the following test case: for (unsigned int i = 0; i < ((1ULL << 32) - 1); i++) {\ bool old = riscv_insn_is_##name(i);\ bool new = new_riscv_insn_is_##name(i);\ if (old != new) {\ printf(#name " %u\n", i);\ }\ } for (unsigned int i = 0; i < ((1ULL << 32) - 1); i++) {\ bool old = riscv_insn_is_##name(i);\ bool new = new_riscv_insn_is_##name(i);\ \ if (old != new) {\ printf(#name " %u\n", i);\ return;\ }\ } void check() { check_64(auipc) check_64(jalr) check_64(jal) check_64(beq) check_64(bne) check_64(blt) check_64(bge) check_64(bltu) check_64(bgeu) check_64(ebreak) check_64(sret) check_64(fence) check_32(c_jr) check_32(c_jalr) check_32(c_j) check_32(c_beqz) check_32(c_bnez) check_32(c_ebreak) } This does a simple brute force to check all possible numbers. The only difference with the new version is that the fence instruction refers to the literal "fence" instruction whereas the previous version could have matched on to fence, fence.i, or fence.tso. This does not make a material difference because riscv_insn_is_fence() isn't currently in use. --- arch/riscv/Makefile | 3 + arch/riscv/include/asm/Kbuild | 1 + arch/riscv/include/asm/insn.h | 72 +-- arch/riscv/tools/Makefile | 22 + arch/riscv/tools/insn.tbl | 1392 +++++++++++++++++++++++++++++++++++++++++ arch/riscv/tools/insn_tbl.sh | 258 ++++++++ 6 files changed, 1706 insertions(+), 42 deletions(-) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index ce0cc737f870..d14caa8720f8 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -176,6 +176,9 @@ BOOT_TARGETS := Image Image.gz Image.bz2 Image.lz4 Image.lzma Image.lzo Image.zs all: $(notdir $(KBUILD_IMAGE)) +archprepare: + $(Q)$(MAKE) $(build)=arch/riscv/tools insn + loader.bin: loader Image.gz Image.bz2 Image.lz4 Image.lzma Image.lzo Image.zst Image.xz loader xipImage vmlinuz.efi: Image diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild index 7721b63642f4..16be4de3d0b1 100644 --- a/arch/riscv/include/asm/Kbuild +++ b/arch/riscv/include/asm/Kbuild @@ -2,6 +2,7 @@ syscall-y += syscall_table_32.h syscall-y += syscall_table_64.h +generated-y += insn_gen.h generic-y += early_ioremap.h generic-y += flat.h generic-y += fprobe.h diff --git a/arch/riscv/include/asm/insn.h b/arch/riscv/include/asm/insn.h index c3005573e8c9..d562b2b40ba1 100644 --- a/arch/riscv/include/asm/insn.h +++ b/arch/riscv/include/asm/insn.h @@ -8,6 +8,36 @@ #include +/* + * Generate a function to check if a sequence of bits matches an instruction + */ +#define __RISCV_INSN_FUNCS(name) \ +static __always_inline bool riscv_insn_is_##name(u32 _insn) \ +{ \ + BUILD_BUG_ON(~(riscv_insn_##name##_MASK) & (riscv_insn_##name##_MATCH)); \ + return (_insn & (riscv_insn_##name##_MASK)) == (riscv_insn_##name##_MATCH); \ +} + +/* + * Generate a function to check if a sequence of bits matches an instruction + * with constraints. Some instructions require inputs to be specific values. + */ +#define __RISCV_INSN_FUNCS_CONSTRAINED(name, constraints) \ +static __always_inline bool riscv_insn_is_##name(u32 _insn) \ +{ \ + BUILD_BUG_ON(~(riscv_insn_##name##_MASK) & (riscv_insn_##name##_MATCH)); \ + return ((_insn & (riscv_insn_##name##_MASK)) == (riscv_insn_##name##_MATCH)) && \ + (constraints); \ +} + +#define __RISCV_INSN_FUNCS_UNSUPPORTED(name) \ +static __always_inline bool riscv_insn_is_##name(u32 _insn) \ +{ \ + return 0; \ +} + +#include + #define RV_INSN_FUNCT3_MASK GENMASK(14, 12) #define RV_INSN_FUNCT3_OPOFF 12 #define RV_INSN_OPCODE_MASK GENMASK(6, 0) @@ -233,36 +263,6 @@ #define __INSN_OPCODE_MASK _UL(0x7F) #define __INSN_BRANCH_OPCODE _UL(RVG_OPCODE_BRANCH) -#define __RISCV_INSN_FUNCS(name, mask, val) \ -static __always_inline bool riscv_insn_is_##name(u32 code) \ -{ \ - BUILD_BUG_ON(~(mask) & (val)); \ - return (code & (mask)) == (val); \ -} \ - -#if __riscv_xlen == 32 -/* C.JAL is an RV32C-only instruction */ -__RISCV_INSN_FUNCS(c_jal, RVC_MASK_C_JAL, RVC_MATCH_C_JAL) -#else -#define riscv_insn_is_c_jal(opcode) 0 -#endif -__RISCV_INSN_FUNCS(auipc, RVG_MASK_AUIPC, RVG_MATCH_AUIPC) -__RISCV_INSN_FUNCS(jalr, RVG_MASK_JALR, RVG_MATCH_JALR) -__RISCV_INSN_FUNCS(jal, RVG_MASK_JAL, RVG_MATCH_JAL) -__RISCV_INSN_FUNCS(c_j, RVC_MASK_C_J, RVC_MATCH_C_J) -__RISCV_INSN_FUNCS(beq, RVG_MASK_BEQ, RVG_MATCH_BEQ) -__RISCV_INSN_FUNCS(bne, RVG_MASK_BNE, RVG_MATCH_BNE) -__RISCV_INSN_FUNCS(blt, RVG_MASK_BLT, RVG_MATCH_BLT) -__RISCV_INSN_FUNCS(bge, RVG_MASK_BGE, RVG_MATCH_BGE) -__RISCV_INSN_FUNCS(bltu, RVG_MASK_BLTU, RVG_MATCH_BLTU) -__RISCV_INSN_FUNCS(bgeu, RVG_MASK_BGEU, RVG_MATCH_BGEU) -__RISCV_INSN_FUNCS(c_beqz, RVC_MASK_C_BEQZ, RVC_MATCH_C_BEQZ) -__RISCV_INSN_FUNCS(c_bnez, RVC_MASK_C_BNEZ, RVC_MATCH_C_BNEZ) -__RISCV_INSN_FUNCS(c_ebreak, RVC_MASK_C_EBREAK, RVC_MATCH_C_EBREAK) -__RISCV_INSN_FUNCS(ebreak, RVG_MASK_EBREAK, RVG_MATCH_EBREAK) -__RISCV_INSN_FUNCS(sret, RVG_MASK_SRET, RVG_MATCH_SRET) -__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE); - /* special case to catch _any_ system instruction */ static __always_inline bool riscv_insn_is_system(u32 code) { @@ -275,18 +275,6 @@ static __always_inline bool riscv_insn_is_branch(u32 code) return (code & RV_INSN_OPCODE_MASK) == RVG_OPCODE_BRANCH; } -static __always_inline bool riscv_insn_is_c_jr(u32 code) -{ - return (code & RVC_MASK_C_JR) == RVC_MATCH_C_JR && - (code & RVC_INSN_J_RS1_MASK) != 0; -} - -static __always_inline bool riscv_insn_is_c_jalr(u32 code) -{ - return (code & RVC_MASK_C_JALR) == RVC_MATCH_C_JALR && - (code & RVC_INSN_J_RS1_MASK) != 0; -} - #define INSN_MATCH_LB 0x3 #define INSN_MASK_LB 0x707f #define INSN_MATCH_LH 0x1003 diff --git a/arch/riscv/tools/Makefile b/arch/riscv/tools/Makefile new file mode 100644 index 000000000000..5f40439c12e9 --- /dev/null +++ b/arch/riscv/tools/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 + +gen := arch/$(ARCH)/include/generated/asm +insn_tbl := $(src)/insn_tbl.sh +insn := $(src)/insn.tbl + +gen-y := $(gen)/insn_gen.h + +targets += $(addprefix ../../../,$(gen-y)) + +PHONY += insn + +insn: $(gen-y) + +# Create output directory if not already present +$(shell mkdir -p $(gen)) + +quiet_cmd_insn_tbl = INST_TBL $@ + cmd_insn_tbl = $(CONFIG_SHELL) $(insn_tbl) $< $@ + +$(gen)/insn_gen.h: $(insn) $(insn_tbl) FORCE + $(call if_changed,insn_tbl) diff --git a/arch/riscv/tools/insn.tbl b/arch/riscv/tools/insn.tbl new file mode 100644 index 000000000000..5dc3392a5543 --- /dev/null +++ b/arch/riscv/tools/insn.tbl @@ -0,0 +1,1392 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +# +# GENERATED WITH https://github.com/riscv-software-src/riscv-unified-db +# "./bin/generate inst-table -o test_table.txt" +# +# Each line of the instruction table should have the following format: +# NAME BASE FIXED_BITS [VARIABLE_LIST] +# NAME instruction name +# BASE instruction base size (common[,(32|64)]) +# "common" means the instruction is valid on both architecture sizes +# "32" or "64" means the instruction is valid on that size +# if the instruction is valid on both architectures but has unique +# encodings, use a 32-bit entry "common,32" and 64-bit entry +# FIXED_BITS bitfields of the fixed bits of an instruction concatenated with '|' +# continuous grouping of fixed bits are in the form of 'bits_MASK useful to help check if arbitrary binary is +# - riscv_insn__MATCH useful to help check if arbitrary binary is +# - riscv_insn_ useful to construct +# - riscv_insn__ useful to extract from +# +# Each line of the instruction table should have the following format: +# NAME BASE FIXED_BITS [VARIABLE_LIST] +# NAME instruction name +# BASE instruction base size (common[,(32|64)]) +# "common" means the instruction is valid on both architecture sizes +# "32" or "64" means the instruction is valid on that size +# if the instruction is valid on both architectures but has unique +# encodings, use a 32-bit entry "common,32" and 64-bit entry +# FIXED_BITS bitfields of the fixed bits of an instruction concatenated with '|' +# continuous grouping of fixed bits are in the form of 'bits&2 "usage: $0 BASE INFILE OUTFILE" >&2 + echo >&2 + echo >&2 " INFILE input instruction table" + echo >&2 " OUTFILE output header file" + exit 1 +} + +if [ $# -ne 2 ]; then + usage +fi + +infile="$1" +outfile="$2" + +file=$(readlink -f $0) + +echo "/* Auto-generated rv${base} header from script arch/${file#*arch/} */" > $outfile + +echo "#ifndef RISCV_INSN_GEN_H" >> $outfile +echo "#define RISCV_INSN_GEN_H" >> $outfile +echo >> $outfile + +printf "#include " >> $outfile +echo >> $outfile +printf "#include " >> $outfile +echo >> $outfile + +grep -E "^[a-z\.0-9]+[[:space:]]+" "$infile" | { + while read name base fixed variables; do + echo "/* $name */" + + non_compressed_insn=${name##c.*} + invalid_inst_functions="" + variable_params="" + constraints="" + match="" + mask="" + make="" + + # All compressed instructions start with "c." + size=${non_compressed_insn:+32}; + size=${size:-16}; + + # Replace all . with _ + formatted_inst_name=$name + while [ ! ${formatted_inst_name##*.*} ]; do + prefix=${formatted_inst_name%.*} + suffix=${formatted_inst_name##*.} + contains_dot=${formatted_inst_name##*.*} + formatted_inst_name=${contains_dot:-${prefix}_${suffix}} + done + + # Collect all fixed bits of an instruction + OLD_IFS=$IFS + IFS='|' + for segment in $fixed; do + bits=${segment%<*} + offset=${segment#*<} + + len=${#bits} + + mask="${mask} | 0b" + + while [ $len -gt 0 ]; do + len=$((len - 1)) + mask=${mask}1 + done + + if [ ${offset} -gt 0 ]; then + s=" << ${offset}" + else + s="" + fi + + mask="${mask}${s}" + + match="${match} | 0b${bits}U${s}" + done + IFS=$OLD_IFS + + # Instruction only appears in one base + only_base= + if [ "${base}" != "${base%32}" ]; then + echo "#if IS_ENABLED(CONFIG_32BIT)" + only_base=32 + elif [ "${base}" != "${base%64}" ]; then + echo "#if IS_ENABLED(CONFIG_64BIT)" + only_base=64 + fi + + # Standard name for the instruction parameter in generated functions + insn="_insn" + + for variable in ${variables}; do + variable_name="${variable%%[<~=!]*}" + parts="${variable#*=}" + insert_mask="" + sign_extend="" + left_shift="" + extract="" + insert="" + + # Standard name for the variable parameter in generated functions + var="_${variable_name}" + variable_params="${variable_params}u32 ${var}, " + + if [ "${variable}" != "${variable#*~}" ]; then + sign_extend="1" + fi + + if [ "${variable}" != "${variable#*<}" ]; then + left_shift="${variable#*<}" + left_shift="${left_shift%%[=<~!]*}" + else + left_shift="0" + fi + + if [ "${variable}" != "${variable#*!}" ]; then + raw_constraints="${variable#*!}" + raw_constraints="${raw_constraints%%[=<~]**}" + + OLD_IFS=$IFS + IFS='!' + for constraint in $raw_constraints; do + constraints="${constraints}(riscv_insn_${formatted_inst_name}_extract_${variable_name}(${insn}) != ${constraint}) && " + done + IFS=$OLD_IFS + fi + + offset=0 + while true; do + part=${parts##*|} + + if [ "${part#*-}" = "${part}" ]; then + high="${part}" + low="${part}" + len=1 + else + high="${part%-*}" + low="${part#*-}" + len=$((high - low + 1)) + fi + + # Don't emit shift if 0 + first_shift=${low} + if [ "${first_shift}" = "0" ]; then + first_shift= + fi + + second_shift=$((offset + left_shift)) + if [ "${second_shift}" = "0" ]; then + second_shift= + fi + + extract="${extract} | ((${insn}${first_shift:+ >> }${first_shift} & GENMASK($((len - 1)), 0))${second_shift:+ << }${second_shift})" + insert_mask="${insert_mask} & ~GENMASK(${high}, ${low})" + insert="${insert} | (((${var}${second_shift:+ >> }${second_shift}) & GENMASK($((len - 1)), 0))${first_shift:+ << }${first_shift})" + offset=$((offset + len)) + + if [ "${parts}" = "${part}" ]; then + # Processed all parts of variable + break + fi + + parts=${parts%|*} + done + + extract="${extract# | }" + + if [ ${sign_extend} ]; then + extract="sign_extend32(${extract}, $((offset + left_shift - 1)))" + type="s" + else + type="u" + fi + + printf "static __always_inline ${type}32 riscv_insn_${formatted_inst_name}_extract_${variable_name}(u${size} ${insn})\n" + printf "{\n" + printf "\treturn ${extract};\n" + printf "}\n" + printf "static __always_inline void riscv_insn_${formatted_inst_name}_insert_${variable_name}(u${size} *${insn}, ${type}32 ${var})\n" + printf "{\n" + printf "\t*_insn &= ${insert_mask# & };\n" + printf "\t*_insn |= ${insert# | };\n" + printf "}\n" + + if [ "${only_base}" ]; then + invalid_inst_functions="${invalid_inst_functions}static __always_inline ${type}${size} riscv_insn_${formatted_inst_name}_extract_${variable_name}(u${size} ${insn}) {\n\tBUILD_BUG_ON_MSG(1, \"${name} is not supported on non ${only_base}-bit systems.\");\n}\n" + fi + + make="${make} riscv_insn_${formatted_inst_name}_insert_${variable_name}(&${insn}, ${var});\n" + done + + variable_params="${variable_params%, }" + variable_params="${variable_params:-void}" + + printf "#define riscv_insn_${formatted_inst_name}_MASK (${mask# | })\n" + printf "#define riscv_insn_${formatted_inst_name}_MATCH (${match# | })\n" + printf "static __always_inline u${size} riscv_insn_${formatted_inst_name}(${variable_params})\n" + printf "{\n" + printf "\tu${size} ${insn} = riscv_insn_${formatted_inst_name}_MATCH;\n" + printf "${make} return ${insn};\n" + printf "}\n" + + # Check against instructions that have a variable that may contain invalid values + if [ "$constraints" ]; then + printf "__RISCV_INSN_FUNCS_CONSTRAINED(${formatted_inst_name}, ${constraints% && });\n" + else + printf "__RISCV_INSN_FUNCS(${formatted_inst_name});\n" + fi + + # If common does not appear in the base, then this instruction only appears in one base + if [ "$base" = "${base#common}" ]; then + printf "#else\n" + printf "__RISCV_INSN_FUNCS_UNSUPPORTED(${formatted_inst_name});\n" + printf "${invalid_inst_functions%\\n}\n" + fi + + # Instruction has a base variant + if [ "$base" != "${base%[24]}" ]; then + echo "#endif" + fi + + echo + done + + echo "#endif /* RISCV_INST_GEN_H */" +} >> $outfile -- 2.54.0