diff --git a/Cargo.lock b/Cargo.lock
index 43a7880362b4873fc2728445c2db1a8afa17a14b..7f9f3198e570e737052e77439c82974a5d3bb580 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -19907,6 +19907,19 @@ dependencies = [
  "polkavm-linux-raw 0.17.0",
 ]
 
+[[package]]
+name = "polkavm"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd044ab1d3b11567ab6b98ca71259a992b4034220d5972988a0e96518e5d343d"
+dependencies = [
+ "libc",
+ "log",
+ "polkavm-assembler 0.18.0",
+ "polkavm-common 0.18.0",
+ "polkavm-linux-raw 0.18.0",
+]
+
 [[package]]
 name = "polkavm-assembler"
 version = "0.9.0"
@@ -19934,6 +19947,15 @@ dependencies = [
  "log",
 ]
 
+[[package]]
+name = "polkavm-assembler"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaad38dc420bfed79e6f731471c973ce5ff5e47ab403e63cf40358fef8a6368f"
+dependencies = [
+ "log",
+]
+
 [[package]]
 name = "polkavm-common"
 version = "0.8.0"
@@ -19969,6 +19991,16 @@ dependencies = [
  "polkavm-assembler 0.17.0",
 ]
 
+[[package]]
+name = "polkavm-common"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31ff33982a807d8567645d4784b9b5d7ab87bcb494f534a57cadd9012688e102"
+dependencies = [
+ "log",
+ "polkavm-assembler 0.18.0",
+]
+
 [[package]]
 name = "polkavm-derive"
 version = "0.8.0"
@@ -20005,6 +20037,15 @@ dependencies = [
  "polkavm-derive-impl-macro 0.17.0",
 ]
 
+[[package]]
+name = "polkavm-derive"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2eb703f3b6404c13228402e98a5eae063fd16b8f58afe334073ec105ee4117e"
+dependencies = [
+ "polkavm-derive-impl-macro 0.18.0",
+]
+
 [[package]]
 name = "polkavm-derive-impl"
 version = "0.8.0"
@@ -20053,6 +20094,18 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "polkavm-derive-impl"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12d2840cc62a0550156b1676fed8392271ddf2fab4a00661db56231424674624"
+dependencies = [
+ "polkavm-common 0.18.0",
+ "proc-macro2 1.0.86",
+ "quote 1.0.37",
+ "syn 2.0.87",
+]
+
 [[package]]
 name = "polkavm-derive-impl-macro"
 version = "0.8.0"
@@ -20093,6 +20146,16 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "polkavm-derive-impl-macro"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c16669ddc7433e34c1007d31080b80901e3e8e523cb9d4b441c3910cf9294b"
+dependencies = [
+ "polkavm-derive-impl 0.18.0",
+ "syn 2.0.87",
+]
+
 [[package]]
 name = "polkavm-linker"
 version = "0.9.2"
@@ -20139,6 +20202,22 @@ dependencies = [
  "rustc-demangle",
 ]
 
+[[package]]
+name = "polkavm-linker"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9bfe793b094d9ea5c99b7c43ba46e277b0f8f48f4bbfdbabf8d3ebf701a4bd3"
+dependencies = [
+ "dirs",
+ "gimli 0.31.1",
+ "hashbrown 0.14.5",
+ "log",
+ "object 0.36.1",
+ "polkavm-common 0.18.0",
+ "regalloc2 0.9.3",
+ "rustc-demangle",
+]
+
 [[package]]
 name = "polkavm-linux-raw"
 version = "0.9.0"
@@ -20157,6 +20236,12 @@ version = "0.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e64c3d93a58ffbc3099d1227f0da9675a025a9ea6c917038f266920c1de1e568"
 
+[[package]]
+name = "polkavm-linux-raw"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23eff02c070c70f31878a3d915e88a914ecf3e153741e2fb572dde28cce20fde"
+
 [[package]]
 name = "polling"
 version = "2.8.0"
@@ -22882,7 +22967,7 @@ dependencies = [
 name = "sc-executor-common"
 version = "0.29.0"
 dependencies = [
- "polkavm 0.9.3",
+ "polkavm 0.18.0",
  "sc-allocator 23.0.0",
  "sp-maybe-compressed-blob 11.0.0",
  "sp-wasm-interface 20.0.0",
@@ -22923,7 +23008,7 @@ name = "sc-executor-polkavm"
 version = "0.29.0"
 dependencies = [
  "log",
- "polkavm 0.9.3",
+ "polkavm 0.18.0",
  "sc-executor-common 0.29.0",
  "sp-wasm-interface 20.0.0",
 ]
@@ -26713,7 +26798,7 @@ dependencies = [
  "libsecp256k1",
  "log",
  "parity-scale-codec",
- "polkavm-derive 0.17.0",
+ "polkavm-derive 0.18.0",
  "rustversion",
  "secp256k1 0.28.2",
  "sp-core 28.0.0",
@@ -27197,7 +27282,7 @@ dependencies = [
  "bytes",
  "impl-trait-for-tuples",
  "parity-scale-codec",
- "polkavm-derive 0.17.0",
+ "polkavm-derive 0.18.0",
  "primitive-types 0.13.1",
  "rustversion",
  "sp-core 28.0.0",
@@ -28841,7 +28926,7 @@ dependencies = [
  "merkleized-metadata",
  "parity-scale-codec",
  "parity-wasm",
- "polkavm-linker 0.17.1",
+ "polkavm-linker 0.18.0",
  "sc-executor 0.32.0",
  "shlex",
  "sp-core 28.0.0",
diff --git a/Cargo.toml b/Cargo.toml
index 63f17efb98b57176ce5d07538e957e4b617faed9..62a5ada6cd4106929c261c76a9a31606a8b34dd4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1089,9 +1089,9 @@ polkadot-subsystem-bench = { path = "polkadot/node/subsystem-bench" }
 polkadot-test-client = { path = "polkadot/node/test/client" }
 polkadot-test-runtime = { path = "polkadot/runtime/test-runtime" }
 polkadot-test-service = { path = "polkadot/node/test/service" }
-polkavm = { version = "0.9.3", default-features = false }
-polkavm-derive = "0.17.0"
-polkavm-linker = "0.17.1"
+polkavm = { version = "0.18.0", default-features = false }
+polkavm-derive = "0.18.0"
+polkavm-linker = "0.18.0"
 portpicker = { version = "0.1.1" }
 pretty_assertions = { version = "1.3.0" }
 primitive-types = { version = "0.13.1", default-features = false, features = [
diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs
index 3304f89fc0cc273a7f3c346d7e06009addb6f408..2303e12126344f04f8961e07f23baba038e0b099 100644
--- a/polkadot/runtime/rococo/src/lib.rs
+++ b/polkadot/runtime/rococo/src/lib.rs
@@ -20,6 +20,17 @@
 // `construct_runtime!` does a lot of recursion and requires us to increase the limit.
 #![recursion_limit = "512"]
 
+#[cfg(all(any(target_arch = "riscv32", target_arch = "riscv64"), target_feature = "e"))]
+// Allocate 2 MiB stack.
+//
+// TODO: A workaround. Invoke polkavm_derive::min_stack_size!() instead
+// later on.
+::core::arch::global_asm!(
+	".pushsection .polkavm_min_stack_size,\"R\",@note\n",
+	".4byte 2097152",
+	".popsection\n",
+);
+
 extern crate alloc;
 
 use alloc::{
diff --git a/prdoc/pr_6533.prdoc b/prdoc/pr_6533.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..eb72a97db0f8129ae1599f396413da5af5e34b64
--- /dev/null
+++ b/prdoc/pr_6533.prdoc
@@ -0,0 +1,20 @@
+title: "Migrate executor into PolkaVM 0.18.0"
+doc:
+  - audience: Runtime Dev
+    description: |
+      Bump `polkavm` to 0.18.0, and update `sc-polkavm-executor` to be
+      compatible with the API changes. In addition, bump also `polkavm-derive`
+      and `polkavm-linker` in order to make sure that the all parts of the
+      Polkadot SDK use the exact same ABI for `.polkavm` binaries.
+
+      Purely relying on RV32E/RV64E ABI is not possible, as PolkaVM uses a
+      RISCV-V alike ISA, which is derived from RV32E/RV64E but it is still its
+      own microarchitecture, i.e. not fully binary compatible.
+
+crates:
+  - name: sc-executor-common
+    bump: major
+  - name: sc-executor-polkavm
+    bump: minor
+  - name: substrate-wasm-builder
+    bump: minor
diff --git a/substrate/client/executor/common/src/error.rs b/substrate/client/executor/common/src/error.rs
index 9d489eaae4200035b6e0ffc0b1bafb045b2ce2b7..a94c1d493134227a08ee77e9457f0954ffb7766c 100644
--- a/substrate/client/executor/common/src/error.rs
+++ b/substrate/client/executor/common/src/error.rs
@@ -150,8 +150,8 @@ pub enum WasmError {
 	Other(String),
 }
 
-impl From<polkavm::ProgramParseError> for WasmError {
-	fn from(error: polkavm::ProgramParseError) -> Self {
+impl From<polkavm::program::ProgramParseError> for WasmError {
+	fn from(error: polkavm::program::ProgramParseError) -> Self {
 		WasmError::Other(error.to_string())
 	}
 }
diff --git a/substrate/client/executor/common/src/runtime_blob/runtime_blob.rs b/substrate/client/executor/common/src/runtime_blob/runtime_blob.rs
index d689083b2f85cd7f3fc16f220c1b45a474dd87f5..e3f4b4ad97745edd45d69113a6697fbb091c897d 100644
--- a/substrate/client/executor/common/src/runtime_blob/runtime_blob.rs
+++ b/substrate/client/executor/common/src/runtime_blob/runtime_blob.rs
@@ -17,6 +17,7 @@
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
 use crate::{error::WasmError, wasm_runtime::HeapAllocStrategy};
+use polkavm::ArcBytes;
 use wasm_instrument::parity_wasm::elements::{
 	deserialize_buffer, serialize, ExportEntry, External, Internal, MemorySection, MemoryType,
 	Module, Section,
@@ -29,7 +30,7 @@ pub struct RuntimeBlob(BlobKind);
 #[derive(Clone)]
 enum BlobKind {
 	WebAssembly(Module),
-	PolkaVM(polkavm::ProgramBlob<'static>),
+	PolkaVM((polkavm::ProgramBlob, ArcBytes)),
 }
 
 impl RuntimeBlob {
@@ -52,9 +53,9 @@ impl RuntimeBlob {
 	pub fn new(raw_blob: &[u8]) -> Result<Self, WasmError> {
 		if raw_blob.starts_with(b"PVM\0") {
 			if crate::is_polkavm_enabled() {
-				return Ok(Self(BlobKind::PolkaVM(
-					polkavm::ProgramBlob::parse(raw_blob)?.into_owned(),
-				)));
+				let raw = ArcBytes::from(raw_blob);
+				let blob = polkavm::ProgramBlob::parse(raw.clone())?;
+				return Ok(Self(BlobKind::PolkaVM((blob, raw))));
 			} else {
 				return Err(WasmError::Other("expected a WASM runtime blob, found a PolkaVM runtime blob; set the 'SUBSTRATE_ENABLE_POLKAVM' environment variable to enable the experimental PolkaVM-based executor".to_string()));
 			}
@@ -192,7 +193,7 @@ impl RuntimeBlob {
 		match self.0 {
 			BlobKind::WebAssembly(raw_module) =>
 				serialize(raw_module).expect("serializing into a vec should succeed; qed"),
-			BlobKind::PolkaVM(ref blob) => blob.as_bytes().to_vec(),
+			BlobKind::PolkaVM(ref blob) => blob.1.to_vec(),
 		}
 	}
 
@@ -227,7 +228,7 @@ impl RuntimeBlob {
 	pub fn as_polkavm_blob(&self) -> Option<&polkavm::ProgramBlob> {
 		match self.0 {
 			BlobKind::WebAssembly(..) => None,
-			BlobKind::PolkaVM(ref blob) => Some(blob),
+			BlobKind::PolkaVM((ref blob, _)) => Some(blob),
 		}
 	}
 }
diff --git a/substrate/client/executor/polkavm/src/lib.rs b/substrate/client/executor/polkavm/src/lib.rs
index 1bd72eb33d309997832de5fca9250ca651090ce7..134f9ea3d8c42eca4bb5ddb89d4b4d46bb40e8ca 100644
--- a/substrate/client/executor/polkavm/src/lib.rs
+++ b/substrate/client/executor/polkavm/src/lib.rs
@@ -16,7 +16,7 @@
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
-use polkavm::{Caller, Reg};
+use polkavm::{CallError, Caller, Reg};
 use sc_executor_common::{
 	error::{Error, WasmError},
 	wasm_runtime::{AllocationStats, WasmInstance, WasmModule},
@@ -26,10 +26,10 @@ use sp_wasm_interface::{
 };
 
 #[repr(transparent)]
-pub struct InstancePre(polkavm::InstancePre<()>);
+pub struct InstancePre(polkavm::InstancePre<(), String>);
 
 #[repr(transparent)]
-pub struct Instance(polkavm::Instance<()>);
+pub struct Instance(polkavm::Instance<(), String>);
 
 impl WasmModule for InstancePre {
 	fn new_instance(&self) -> Result<Box<dyn WasmInstance>, Error> {
@@ -43,11 +43,13 @@ impl WasmInstance for Instance {
 		name: &str,
 		raw_data: &[u8],
 	) -> (Result<Vec<u8>, Error>, Option<AllocationStats>) {
-		let Some(method_index) = self.0.module().lookup_export(name) else {
-			return (
-				Err(format!("cannot call into the runtime: export not found: '{name}'").into()),
-				None,
-			);
+		let pc = match self.0.module().exports().find(|e| e.symbol() == name) {
+			Some(export) => export.program_counter(),
+			None =>
+				return (
+					Err(format!("cannot call into the runtime: export not found: '{name}'").into()),
+					None,
+				),
 		};
 
 		let Ok(raw_data_length) = u32::try_from(raw_data.len()) else {
@@ -58,56 +60,60 @@ impl WasmInstance for Instance {
 		};
 
 		// TODO: This will leak guest memory; find a better solution.
-		let mut state_args = polkavm::StateArgs::new();
 
-		// Make sure the memory is cleared...
-		state_args.reset_memory(true);
-		// ...and allocate space for the input payload.
-		state_args.sbrk(raw_data_length);
+		// Make sure that the memory is cleared...
+		if let Err(err) = self.0.reset_memory() {
+			return (
+				Err(format!(
+					"call into the runtime method '{name}' failed: reset memory failed: {err}"
+				)
+				.into()),
+				None,
+			);
+		}
 
-		match self.0.update_state(state_args) {
-			Ok(()) => {},
-			Err(polkavm::ExecutionError::Trap(trap)) => {
-				return (Err(format!("call into the runtime method '{name}' failed: failed to prepare the guest's memory: {trap}").into()), None);
-			},
-			Err(polkavm::ExecutionError::Error(error)) => {
-				return (Err(format!("call into the runtime method '{name}' failed: failed to prepare the guest's memory: {error}").into()), None);
-			},
-			Err(polkavm::ExecutionError::OutOfGas) => unreachable!("gas metering is never enabled"),
+		// ... and allocate space for the input payload.
+		if let Err(err) = self.0.sbrk(raw_data_length) {
+			return (
+				Err(format!(
+					"call into the runtime method '{name}' failed: reset memory failed: {err}"
+				)
+				.into()),
+				None,
+			);
 		}
 
 		// Grab the address of where the guest's heap starts; that's where we've just allocated
 		// the memory for the input payload.
 		let data_pointer = self.0.module().memory_map().heap_base();
 
-		if let Err(error) = self.0.write_memory(data_pointer, raw_data) {
-			return (Err(format!("call into the runtime method '{name}': failed to write the input payload into guest memory: {error}").into()), None);
+		if let Err(err) = self.0.write_memory(data_pointer, raw_data) {
+			return (Err(format!("call into the runtime method '{name}': failed to write the input payload into guest memory: {err}").into()), None);
 		}
 
-		let mut state = ();
-		let mut call_args = polkavm::CallArgs::new(&mut state, method_index);
-		call_args.args_untyped(&[data_pointer, raw_data_length]);
-
-		match self.0.call(Default::default(), call_args) {
+		match self.0.call_typed(&mut (), pc, (data_pointer, raw_data_length)) {
 			Ok(()) => {},
-			Err(polkavm::ExecutionError::Trap(trap)) => {
+			Err(CallError::Trap) =>
 				return (
-					Err(format!("call into the runtime method '{name}' failed: {trap}").into()),
+					Err(format!("call into the runtime method '{name}' failed: trap").into()),
 					None,
-				);
-			},
-			Err(polkavm::ExecutionError::Error(error)) => {
+				),
+			Err(CallError::Error(err)) =>
 				return (
-					Err(format!("call into the runtime method '{name}' failed: {error}").into()),
+					Err(format!("call into the runtime method '{name}' failed: {err}").into()),
 					None,
-				);
-			},
-			Err(polkavm::ExecutionError::OutOfGas) => unreachable!("gas metering is never enabled"),
-		}
+				),
+			Err(CallError::User(err)) =>
+				return (
+					Err(format!("call into the runtime method '{name}' failed: {err}").into()),
+					None,
+				),
+			Err(CallError::NotEnoughGas) => unreachable!("gas metering is never enabled"),
+		};
 
-		let result_pointer = self.0.get_reg(Reg::A0);
-		let result_length = self.0.get_reg(Reg::A1);
-		let output = match self.0.read_memory_into_vec(result_pointer, result_length) {
+		let result_pointer = self.0.reg(Reg::A0);
+		let result_length = self.0.reg(Reg::A1);
+		let output = match self.0.read_memory(result_pointer as u32, result_length as u32) {
 			Ok(output) => output,
 			Err(error) => {
 				return (Err(format!("call into the runtime method '{name}' failed: failed to read the return payload: {error}").into()), None)
@@ -127,20 +133,31 @@ impl<'r, 'a> FunctionContext for Context<'r, 'a> {
 		dest: &mut [u8],
 	) -> sp_wasm_interface::Result<()> {
 		self.0
-			.read_memory_into_slice(u32::from(address), dest)
+			.instance
+			.read_memory_into(u32::from(address), dest)
 			.map_err(|error| error.to_string())
 			.map(|_| ())
 	}
 
 	fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> sp_wasm_interface::Result<()> {
-		self.0.write_memory(u32::from(address), data).map_err(|error| error.to_string())
+		self.0
+			.instance
+			.write_memory(u32::from(address), data)
+			.map_err(|error| error.to_string())
 	}
 
 	fn allocate_memory(&mut self, size: WordSize) -> sp_wasm_interface::Result<Pointer<u8>> {
-		let pointer = self.0.sbrk(0).expect("fetching the current heap pointer never fails");
+		let pointer = match self.0.instance.sbrk(0) {
+			Ok(pointer) => pointer.expect("fetching the current heap pointer never fails"),
+			Err(err) => return Err(format!("sbrk failed: {err}")),
+		};
 
 		// TODO: This will leak guest memory; find a better solution.
-		self.0.sbrk(size).ok_or_else(|| String::from("allocation failed"))?;
+		match self.0.instance.sbrk(size) {
+			Ok(Some(_)) => (),
+			Ok(None) => return Err(String::from("allocation error")),
+			Err(err) => return Err(format!("sbrk failed: {err}")),
+		}
 
 		Ok(Pointer::new(pointer))
 	}
@@ -155,41 +172,46 @@ impl<'r, 'a> FunctionContext for Context<'r, 'a> {
 	}
 }
 
-fn call_host_function(
-	caller: &mut Caller<()>,
-	function: &dyn Function,
-) -> Result<(), polkavm::Trap> {
+fn call_host_function(caller: &mut Caller<()>, function: &dyn Function) -> Result<(), String> {
 	let mut args = [Value::I64(0); Reg::ARG_REGS.len()];
 	let mut nth_reg = 0;
 	for (nth_arg, kind) in function.signature().args.iter().enumerate() {
 		match kind {
 			ValueType::I32 => {
-				args[nth_arg] = Value::I32(caller.get_reg(Reg::ARG_REGS[nth_reg]) as i32);
+				args[nth_arg] = Value::I32(caller.instance.reg(Reg::ARG_REGS[nth_reg]) as i32);
 				nth_reg += 1;
 			},
 			ValueType::F32 => {
-				args[nth_arg] = Value::F32(caller.get_reg(Reg::ARG_REGS[nth_reg]));
-				nth_reg += 1;
-			},
-			ValueType::I64 => {
-				let value_lo = caller.get_reg(Reg::ARG_REGS[nth_reg]);
-				nth_reg += 1;
-
-				let value_hi = caller.get_reg(Reg::ARG_REGS[nth_reg]);
-				nth_reg += 1;
-
-				args[nth_arg] =
-					Value::I64((u64::from(value_lo) | (u64::from(value_hi) << 32)) as i64);
-			},
-			ValueType::F64 => {
-				let value_lo = caller.get_reg(Reg::ARG_REGS[nth_reg]);
+				args[nth_arg] = Value::F32(caller.instance.reg(Reg::ARG_REGS[nth_reg]) as u32);
 				nth_reg += 1;
-
-				let value_hi = caller.get_reg(Reg::ARG_REGS[nth_reg]);
-				nth_reg += 1;
-
-				args[nth_arg] = Value::F64(u64::from(value_lo) | (u64::from(value_hi) << 32));
 			},
+			ValueType::I64 =>
+				if caller.instance.is_64_bit() {
+					args[nth_arg] = Value::I64(caller.instance.reg(Reg::ARG_REGS[nth_reg]) as i64);
+					nth_reg += 1;
+				} else {
+					let value_lo = caller.instance.reg(Reg::ARG_REGS[nth_reg]);
+					nth_reg += 1;
+
+					let value_hi = caller.instance.reg(Reg::ARG_REGS[nth_reg]);
+					nth_reg += 1;
+
+					args[nth_arg] =
+						Value::I64((u64::from(value_lo) | (u64::from(value_hi) << 32)) as i64);
+				},
+			ValueType::F64 =>
+				if caller.instance.is_64_bit() {
+					args[nth_arg] = Value::F64(caller.instance.reg(Reg::ARG_REGS[nth_reg]));
+					nth_reg += 1;
+				} else {
+					let value_lo = caller.instance.reg(Reg::ARG_REGS[nth_reg]);
+					nth_reg += 1;
+
+					let value_hi = caller.instance.reg(Reg::ARG_REGS[nth_reg]);
+					nth_reg += 1;
+
+					args[nth_arg] = Value::F64(u64::from(value_lo) | (u64::from(value_hi) << 32));
+				},
 		}
 	}
 
@@ -204,27 +226,33 @@ fn call_host_function(
 	{
 		Ok(value) => value,
 		Err(error) => {
-			log::warn!("Call into the host function '{}' failed: {error}", function.name());
-			return Err(polkavm::Trap::default());
+			let name = function.name();
+			return Err(format!("call into the host function '{name}' failed: {error}"))
 		},
 	};
 
 	if let Some(value) = value {
 		match value {
 			Value::I32(value) => {
-				caller.set_reg(Reg::A0, value as u32);
+				caller.instance.set_reg(Reg::A0, value as u64);
 			},
 			Value::F32(value) => {
-				caller.set_reg(Reg::A0, value);
-			},
-			Value::I64(value) => {
-				caller.set_reg(Reg::A0, value as u32);
-				caller.set_reg(Reg::A1, (value >> 32) as u32);
-			},
-			Value::F64(value) => {
-				caller.set_reg(Reg::A0, value as u32);
-				caller.set_reg(Reg::A1, (value >> 32) as u32);
+				caller.instance.set_reg(Reg::A0, value as u64);
 			},
+			Value::I64(value) =>
+				if caller.instance.is_64_bit() {
+					caller.instance.set_reg(Reg::A0, value as u64);
+				} else {
+					caller.instance.set_reg(Reg::A0, value as u64);
+					caller.instance.set_reg(Reg::A1, (value >> 32) as u64);
+				},
+			Value::F64(value) =>
+				if caller.instance.is_64_bit() {
+					caller.instance.set_reg(Reg::A0, value as u64);
+				} else {
+					caller.instance.set_reg(Reg::A0, value as u64);
+					caller.instance.set_reg(Reg::A1, (value >> 32) as u64);
+				},
 		}
 	}
 
@@ -250,12 +278,16 @@ where
 		},
 	};
 
-	let module = polkavm::Module::from_blob(&engine, &polkavm::ModuleConfig::default(), blob)?;
-	let mut linker = polkavm::Linker::new(&engine);
+	let module =
+		polkavm::Module::from_blob(&engine, &polkavm::ModuleConfig::default(), blob.clone())?;
+
+	let mut linker = polkavm::Linker::new();
+
 	for function in H::host_functions() {
-		linker.func_new(function.name(), |mut caller| call_host_function(&mut caller, function))?;
+		linker.define_untyped(function.name(), |mut caller: Caller<()>| {
+			call_host_function(&mut caller, function)
+		})?;
 	}
-
 	let instance_pre = linker.instantiate_pre(&module)?;
 	Ok(Box::new(InstancePre(instance_pre)))
 }