因為 onnx 套件在 risc-v 上面沒法安裝,所以要在 x86 上面 cross-compile 模型

以下是編譯用的 python 檔,跟之前 x86 的差別為

  1. 增加 riscv_fcompile 函數並增加到 ex.export_library(output_path, fcompile=riscv_fcompile)
  2. 最後一行原本是 target="llvm" ,改成 target="c" ,原因是 llvm可以編譯的過,但到 inference 時會跳 TVMError: Binary was created using {relax.Executable} but a loader of that name is not registered. Available loaders are static_library, const_loader, c, relax.VMExecutable, bananapi. Perhaps you need to recompile with this runtime enabled.

然後這份檔案會跳

# Metadata omitted. Use show_meta=True in script() method to show it.

Traceback (most recent call last):
  File "/home/fre930727/whisper-tiny/onnx/compile_onnx_models_lack_of_parameters.py", line 80, in <module>
    decoder_so = compile_model("decoder_model.onnx", target = tvm.target.Target("c"))
  File "/home/fre930727/whisper-tiny/onnx/compile_onnx_models_lack_of_parameters.py", line 75, in compile_model
    ex.export_library(output_path, fcompile=riscv_fcompile)
  File "/home/fre930727/tvm/python/tvm/relax/vm_build.py", line 146, in export_library
    return self.mod.export_library(
  File "/home/fre930727/tvm/python/tvm/runtime/module.py", line 628, in export_library
    return fcompile(file_name, files, **kwargs)
  File "/home/fre930727/whisper-tiny/onnx/compile_onnx_models_lack_of_parameters.py", line 15, in riscv_fcompile
    return cc.create_shared(
  File "/home/fre930727/tvm/python/tvm/contrib/cc.py", line 94, in create_shared
    _linux_compile(output, objects, options, cc, cwd, ccache_env, compile_shared=True)
  File "/home/fre930727/tvm/python/tvm/contrib/cc.py", line 371, in _linux_compile
    raise RuntimeError(msg)
RuntimeError: Compilation error:
/tmp/tmptt6npw2p/lib0.c:54:17: error: conflicting declaration of C function 'int32_t erf(void*, int32_t*, int32_t, void*, int32_t*, void*)'
   54 | TVM_DLL int32_t erf(void* args, int32_t* arg_type_ids, int32_t num_args, void* out_ret_value, int32_t* out_ret_tcode, void* resource_handle);
      |                 ^~~
In file included from /opt/riscv/sysroot/usr/include/features.h:524,
                 from /opt/riscv/sysroot/usr/include/bits/libc-header-start.h:33,
                 from /opt/riscv/sysroot/usr/include/stdint.h:26,
                 from /opt/riscv/lib/gcc/riscv64-unknown-linux-gnu/15.1.0/include/stdint.h:11,
                 from /home/fre930727/tvm/3rdparty/dlpack/include/dlpack/dlpack.h:35,
                 from /home/fre930727/tvm/include/tvm/runtime/c_runtime_api.h:79,
                 from /tmp/tmptt6npw2p/lib0.c:3:
/opt/riscv/sysroot/usr/include/bits/mathcalls.h:285:1: note: previous declaration 'double erf(double)'
  285 | __MATHCALL_VEC (erf,, (_Mdouble_));
      | ^~~~~~~~~~~~~~
/tmp/tmptt6npw2p/lib0.c:510:17: error: conflicting declaration of C function 'int32_t erf(void*, int32_t*, int32_t, void*, int32_t*, void*)'
  510 | TVM_DLL int32_t erf(void* args, int32_t* arg_type_ids, int32_t num_args, void* out_ret_value, int32_t* out_ret_tcode, void* resource_handle) {
      |                 ^~~
/opt/riscv/sysroot/usr/include/bits/mathcalls.h:285:1: note: previous declaration 'double erf(double)'
  285 | __MATHCALL_VEC (erf,, (_Mdouble_));
      | ^~~~~~~~~~~~~~

Command line: riscv64-unknown-linux-gnu-g++ -shared -fPIC -o decoder_model.so /tmp/tmptt6npw2p/lib0.c /tmp/tmptt6npw2p/devc.c -I/home/fre930727/tvm/include -I/home/fre930727/tvm/3rdparty/dlpack/include -I/home/fre930727/tvm/3rdparty/dmlc-core/include -march=rv64imafdcv -mabi=lp64d

重點是/tmp/tmptt6npw2p/lib0.c:54:17: error: conflicting declaration of C function 'int32_t erf(void*, int32_t*, int32_t, void*, int32_t*, void*)',好像撞名字了

再補充到,我剛剛用以下的 script 去改 node 名稱,但仍報一樣的錯

import onnx

# Load ONNX model
onnx_model = onnx.load("decoder_model.onnx")

# Add prefix to every node name
for node in onnx_model.graph.node:
    node.name = "ccucsie_" + node.name if node.name else "ccucsie_unnamed"
    print("hi")

# Save modified model
onnx.save(onnx_model, "decoder_model_renamed.onnx")

完整的編譯檔案

import onnx
import tvm
from tvm import relax
from tvm.relax.frontend.onnx import from_onnx  # Correct import path
from tvm.relax.dpl import is_op, wildcard
from tvm.contrib import cc

def riscv_fcompile(file_name, files, options=None, **kwargs):
    if options is None:
        options = []
    # 添加任何必要的 RISC-V 編譯選項,例如浮點 ABI
    options.append("-march=rv64imafdcv") # 範例,指定 ISA 擴展
    options.append("-mabi=lp64d")   # 範例,指定 ABI

    return cc.create_shared(
        file_name,
        files,
        options=options,
        cc="riscv64-unknown-linux-gnu-g++", # 指定你的 RISC-V 編譯器
        **kwargs

    )
def compile_model(onnx_path, target="llvm"):
	# 1. Load ONNX model
	onnx_model = onnx.load(onnx_path) 
	# 2. Convert to Relax IR (updated API)
	#mod = from_onnx(onnx_model, {"input_features": (1, 80, 3000)})# give input shape of both encoder and decoder, make them static. Somer op does not support dynamic shape

	mod = from_onnx(onnx_model, {"input_ids": (1, 1), "encoder_hidden_states": (1, 1500, 384)})# give input shape of both encoder and decoder, make them static. Somer op does not support dynamic shape
	
	#mod = from_onnx(onnx_model)
	#mod=tvm.relax.transform.BindSymbolicVars({"batch_size":1, "encoder_sequence_length_out": 1500})(mod)

	patterns = [("bananapi.matmul", is_op("relax.matmul")(wildcard(), wildcard()))]
	#patterns = [("tensorrt.add", is_op("relax.add")(wildcard(), wildcard()))]

	'''
	annotate_codegen: 不要 Merge 相鄰的 OP,一個 OP 一個 Relax function
	bind_constants: 綁定常數,如果前面 from_onnx 的 keep_params_in_input=False(預設) 這裡要設成 bind_constants=False
						 如果前面 from_onnx 的 keep_params_in_input=True		這裡要設成 bind_constants=True(預設)
	'''
	mod = relax.transform.FuseOpsByPattern(patterns, bind_constants=False, annotate_codegen=True)(mod)
	#mod = relax.transform.FuseOpsByPattern(patterns, bind_constants=False)(mod)
	#mod = relax.transform.FuseOpsByPattern(patterns)(mod)
	#mod.show()

	#mod = relax.transform.MergeCompositeFunctions()(mod)
	#mod.show()

	mod = relax.transform.RunCodegen()(mod)
	mod.show()

	# 3. Apply mandatory passes
	seq = tvm.ir.transform.Sequential([
		relax.transform.LegalizeOps(),
		relax.transform.FoldConstant(),
		relax.transform.DeadCodeElimination()
	])
	mod = seq(mod)

	# Check if output IRModule is well-formed. 
	#assert relax.analysis.well_formed(mod)
	# 4. Build
	ex = relax.build(mod, target)
	
	# 5. Save
	output_path = onnx_path.replace(".onnx", ".so")
	ex.export_library(output_path, fcompile=riscv_fcompile)
	return output_path

# Compile both encoder and decoder
#encoder_so = compile_model("encoder_model.onnx", target="llvm -mtriple=riscv64-unknown-linux-gnu -mattr=+m,+a,+f,+d,+cllvm")
decoder_so = compile_model("decoder_model.onnx", target = tvm.target.Target("c"))

riscv toolchain cross-check

zin’s simple.so cant work here