存取記憶體的指令基本上區分為 load
系列與、store
系列。對於整數的 load
指令,都會從堆疊中取出一個 i32
數值作為位元組偏移量(也就是定址之用),名稱上可以識別從記憶體取出的位元長度,以及擴充方式,無論是整數或浮點數,若只有 load 字樣,依指令前的型態決定位元長度,取出的數值會置入堆疊。
i32.load8_s
:載入 8 個位元,視為有號整數擴充為i32
。i32.load8_u
:載入 8 個位元,視為無號整數擴充為i32
(左邊補 0)。i32.load16_s
:載入 16 個位元,視為有號整數擴充為i32
。i32.load16_u
:載入 16 個位元,視為無號整數擴充為i32
(左邊補 0)。i32.load
:載入 32 個位元,以i32
放入堆疊。i64.load8_s
:載入 8 個位元,視為有號整數擴充為i64
。i64.load8_u
:載入 8 個位元,視為無號整數擴充為i64
(左邊補 0)。i64.load16_s
:載入 16 個位元,視為有號整數擴充為i64
。i64.load16_u
:載入 16 個位元,視為無號整數擴充為i64
(左邊補 0)。i64.load32_s
:載入 32 個位元,視為有號整數擴充為i64
。i64.load32_u
:載入 32 個位元,視為無號整數擴充為i64
(左邊補 0)。i64.load
:載入 64 個位元,以i64
放入堆疊。f32.load
:載入 32 個位元,以f32
放入堆疊。f64.load
:載入 64 個位元,以f64
放入堆疊。
底下這個模組,使用 i32.load8_s
每次讀取 8 個位元,共讀取五次:
(module
(import "env" "log" (func $log (param i32)))
(memory (data "Hello"))
(func $main
(local $i i32)
loop
(i32.load8_s (get_local $i))
call $log
(set_local $i
(i32.add
(get_local $i)
(i32.const 1)
)
)
(br_if 0
(i32.ne
(get_local $i)
(i32.const 5)
)
)
end
)
(start $main)
)
匯入的 $log
函式,會依指定的數值以 String.fromCharCode
轉為字串,因此會顯示 Hello:
const importObj = {
env: {
log(n) {
console.log(String.fromCharCode(n));
}
}
};
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj);
指定位元組偏移量若超出了記憶體的位元組長度,會發生超出邊界的錯誤!
在儲存方面,對於整數的 store
指令,名稱上可以識別要儲存的位元長度,它會從堆疊中取出一個數值,接著再從堆疊中取得一個 i32
作為位元組偏移量,例如從記憶體開頭讀寫 72 這個數值:
(module
(import "env" "log" (func $log (param i32)))
(memory 1)
(func $main
i32.const 0 ;; 先指定偏移量
i32.const 72 ;; 再指定要存放的數值
i32.store8
i32.const 0
i32.load8_s
call $log
)
(start $main)
)
這樣看來很不直覺,不過,若寫成以下就容易閱讀了:
(module
(import "env" "log" (func $log (param i32)))
(memory 1)
(func $main
(i32.store8 (i32.const 0) (i32.const 72))
(i32.load8_s (i32.const 0))
call $log
)
(start $main)
)
可以使用的儲存指令有:
i32.store8
:將i32
數值取 8 位元存入,多的位元捨去。i32.store16
:將i32
數值取 16 位元存入,多的位元捨去。i32.store
:(不轉換)將i32
數值的位元存入。i64.store8
:將i64
數值取 8 位元存入,多的位元捨去。i64.store16
:將i32
數值取 16 位元存入,多的位元捨去。i64.store32
:將i32
數值取 32 位元存入,多的位元捨去。i64.store
:(不轉換)將i64
數值的位元存入。f32.store
:(不轉換)將f32
數值的位元存入。f64.store
:(不轉換)將f64
數值的位元存入。