WebGL Tutorial
and more

Integer Numeric Instructions

撰写时间:2025-01-20

修订时间:2025-01-23

与整数有关的指令

nn, mm ::= 32 | 64 sx ::= u | s instr ::= inn.const unn | inn.iunop | inn.ibinop | inn.itestop | inn.irelop | inn.extend8_s | inn.extend16_s | i64.extend32_s | i32.wrap_i64 | i64.exend_i32_sx | inn.trunc_fmm_sx | inn.trunc_sat_fmm_sx | inn.reinterpret_fnn | ... iunop ::= clz | ctz | popcnt ibinop ::= add | sub | mul | div_sx | rem_sx | and | or | xor | shl | shr_sx | rotl | rotr itestop ::= eqz irelop ::= eq | ne | lt_sx | gt_sx | le_sx | ge_sx

设置常数

inn.const unn将一个无符号常数压进Result Stack中。

let watSrc = ` (module (func (export "sum") (result i32) i32.const 15 i32.const 10 i32.add ) ) `; const { sum } = await WabtUtils.RunWat(watSrc); let result = sum(); pc.log(result); pc.log(typeof result);

尽管Wasm规范中的语法未明确列出i64,但i64也同样适用于const指令。

let watSrc = ` (module (func (export "sum") (result i64) i64.const 15 i64.const 10 i64.add ) ) `; const { sum } = await WabtUtils.RunWat(watSrc); let result = sum(); pc.log(result); pc.log(typeof result);

一元操作指令

一元操作指令是指只有1个操作数的指令,在语法表中使用iunop (integer unary operations) 来标识。

clz

clz指令,count leading zeros的缩写,返回操作数作为二进制时前导0的数量。

let watSrc = ` (module (func (export "getCLZ") (param i32) (result i32) local.get 0 i32.clz ) ) `; const { getCLZ } = await WabtUtils.RunWat(watSrc); let num = 0B00000001_00010001_00010001_00010001; pc.log(num); pc.log(getCLZ(num));

ctz

ctz指令,count trailing zeros的缩写,返回操作数作为二进制时位于尾部的0的数量。

let watSrc = ` (module (func (export "getCTZ") (param i32) (result i32) local.get 0 i32.ctz ) ) `; const { getCTZ } = await WabtUtils.RunWat(watSrc); let num = 0B00000000_00010001_00010001_00010000; pc.log(num); pc.log(getCTZ(num));

popcnt

popcnt指令,population count的缩写,返回操作数作为二进制时1的数量。

let watSrc = ` (module (func (export "countOnes") (param i32) (result i32) local.get 0 i32.popcnt ) ) `; const { countOnes } = await WabtUtils.RunWat(watSrc); let num = 0B00000000_00010001_00010001_00010000; pc.log(num); pc.log(countOnes(num));

二元操作指令

二元操作指令是指有2个操作数的指令,在语法表中使用ibinop (integer binnary operations) 来标识。

分为3大类,分别涉及加减乘除运算、位域逻辑运算,及位移运算。

加减乘除运算

add

add指令将两数相加。

let watSrc = ` (module (func (export "sum") (param i32 i32) (result i32) local.get 0 local.get 1 i32.add ) ) `; const { sum } = await WabtUtils.RunWat(watSrc); pc.log(sum(10, 20));

sub

sub指令对两数相减,栈底数值为被减数,栈顶数值为减数。

let watSrc = ` (module (func (export "sub") (param i32 i32) (result i32) local.get 0 local.get 1 i32.sub ) ) `; const { sub } = await WabtUtils.RunWat(watSrc); pc.log(sub(10, 20));

mul

mul指令对两数相乘。

let watSrc = ` (module (func (export "mul") (param i32 i32) (result i32) local.get 0 local.get 1 i32.mul ) ) `; const { mul } = await WabtUtils.RunWat(watSrc); pc.log(mul(3, 5));

div

div指令对两数相除,栈底数值为被除数,栈顶数值为除数。

let watSrc = ` (module (func (export "div") (param i32 i32) (result i32) local.get 0 local.get 1 i32.div_s ) ) `; const { div } = await WabtUtils.RunWat(watSrc); pc.log(div(-13, 5)); pc.log(-13 / 5);

Wasm的指令是基于特定类型的指令,因此i32i64div总是返回一个整数值,这与JavaScript的除法不同。当不能整除时,Wasm的除法结果只返回整数部分,而JavaScript的除法结果返回一个浮点数。若要得到一个浮点数值,可调用f32f64div指令。

此外,在表示加减乘除的相应指令add, sub, mul, div, rem中,只有divrem需明确指定是否有符号。

rem

rem指令对两数取余,需指定是否有符号。

let watSrc = ` (module (func (export "mod") (param i32 i32) (result i32) local.get 0 local.get 1 i32.rem_s ) ) `; const { mod } = await WabtUtils.RunWat(watSrc); pc.log(mod(-13, 5));

位域逻辑运算

and

and指令对两数的数位进行位与运算:两数位的值,只有同为1时,结果方为1;否则,结果为0

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "and") (param i32 i32) (result i32) local.get 0 local.get 1 i32.and ) ) `; const { and } = await WabtUtils.RunWat(watSrc); let num1 = 0B0010_0011; let num2 = 0B0110_1101; let result = and(num1, num2); pc.log("%s", getBinStr(num1, 4)); pc.log("%s", getBinStr(num2, 4)); pc.log("%s", '---------'); pc.log("%s", getBinStr(result, 4));

or

or指令对两数的数位进行位或运算:两数位的值,只要有1个值为1,结果就为1;否则,结果为0

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "or") (param i32 i32) (result i32) local.get 0 local.get 1 i32.or ) ) `; const { or } = await WabtUtils.RunWat(watSrc); let num1 = 0B0010_0011; let num2 = 0B0110_1101; let result = or(num1, num2); pc.log("%s", getBinStr(num1, 4)); pc.log("%s", getBinStr(num2, 4)); pc.log("%s", '---------'); pc.log("%s", getBinStr(result, 4));

xor

xor指令对两数的数位进行位异或运算:两数位的值,只有在不相等的时候,结果方为1;否则,结果为0

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "xor") (param i32 i32) (result i32) local.get 0 local.get 1 i32.xor ) ) `; const { xor } = await WabtUtils.RunWat(watSrc); let num1 = 0B0010_0011; let num2 = 0B0110_1101; let result = xor(num1, num2); pc.log("%s", getBinStr(num1, 4)); pc.log("%s", getBinStr(num2, 4)); pc.log("%s", '---------'); pc.log("%s", getBinStr(result, 4));

位移运算

shl

shl指令,shift left的缩写,对操作数左移指定的位数。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "shl") (param i32 i32) (result i32) local.get 0 local.get 1 i32.shl ) ) `; const { shl } = await WabtUtils.RunWat(watSrc); let num = 0B11000001_00000001_00000001_00100000; let bits = 3; let result = shl(num, bits); pc.log("%s (%d)", getBinStr(num, 8), num); pc.log("%s", ' <- 3 bits'); pc.log("%s", '---------------'); pc.log("%s (%d)", getBinStr(result, 8), result);

从二进制位域的移动来看,左边的指定位域被移出而舍弃,右边的位域填充0

从十进制的运算效果来看,低权重位的数值向左移动到高权重位,类似于123变成了1230,因此数值变大了。但由于是基于二进制的左移,因此在未产生溢出的情况下,其十进制的运算效果为:

num = num × 2bits;

shr

shr指令,shift right的缩写,对操作数右移指定的位数。须使用有符号或无符号的标记。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "shr") (param i32 i32) (result i32) local.get 0 local.get 1 i32.shr_u ) ) `; const { shr } = await WabtUtils.RunWat(watSrc); let num = 0B0010_0011; let bits = 3; let result = shr(num, bits); pc.log("%s (%d)", getBinStr(num, 4), num); pc.log("%s", ' -> 3 bits'); pc.log("%s", '---------------'); pc.log("%s (%d)", getBinStr(result, 4), result);

从二进制位域的移动来看,右边的指定位域被移出而舍弃,左边的位域填充0

从十进制的运算效果来看,高权重位的数值向右移动到低权重位,类似于1230变成了123,因此数值变小了。但由于是基于二进制的右移,因此其十进制的运算效果为:

num = num ÷ 2bits;  // take only the integer part

rotl

rotl指令,rotate left的缩写,对操作数左移指定的位数,超出位域范围的数位,依序转回到尾部添加。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "rotl") (param i32 i32) (result i32) local.get 0 local.get 1 i32.rotl ) ) `; const { rotl } = await WabtUtils.RunWat(watSrc); let num = 0B10000000_00000000_00000000_00000001; let bits = 2; let result = rotl(num, bits); pc.log("%s (%d)", getBinStr(num, 8), num); pc.log("%s", ' <- 2 bits'); pc.log("%s", '---------------'); pc.log("%s (%d)", getBinStr(result, 8), result);

rotr

rotr指令,rotate right的缩写,对操作数右移指定的位数,超出位域范围的数位,依序转回到头部添加。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "rotr") (param i32 i32) (result i32) local.get 0 local.get 1 i32.rotr ) ) `; const { rotr } = await WabtUtils.RunWat(watSrc); let num = 0B10000000_00000000_00000000_00000001; let bits = 2; let result = rotr(num, bits); pc.log("%s (%d)", getBinStr(num, 8), num); pc.log("%s", ' -> 2 bits'); pc.log("%s", '---------------'); pc.log("%s (%d)", getBinStr(result, 8), result);

测试指令

测试指令消耗1个操作数,返回一个表示真假的数值,1true0false,在语法表中使用itestop (integer test operations) 来标识。

测试指令只有1个指令:eqz

eqz

eqz指令,equal zero的缩写,用于测试特定操作数的数值是否等于0

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "eqz") (param i32) (result i32) local.get 0 i32.eqz ) ) `; const { eqz } = await WabtUtils.RunWat(watSrc); pc.log(eqz(35)); pc.log(eqz(0));

比较指令

比较指令消耗2个操作数,返回一个表示真假的数值,1true0false,在语法表中使用irelop (integer relation operations) 来标识。

eq

eq指令,equal的缩写,用于测试2个操作数的数值是否相等。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "eq") (param i32 i32) (result i32) local.get 0 local.get 1 i32.eq ) ) `; const { eq } = await WabtUtils.RunWat(watSrc); pc.log(eq(15, 25)); pc.log(eq(0, 15 - 15));

ne

ne指令,not equal的缩写,用于测试2个操作数的数值是否不等。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "ne") (param i32 i32) (result i32) local.get 0 local.get 1 i32.ne ) ) `; const { ne } = await WabtUtils.RunWat(watSrc); pc.log(ne(15, 25)); pc.log(ne(0, 15 - 15));

lt

lt指令,less than的缩写,用于测试第1个操作数是否小于第2个操作数。需指定是否有符号。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "lt_s") (param i32 i32) (result i32) local.get 0 local.get 1 i32.lt_s ) ) `; const { lt_s } = await WabtUtils.RunWat(watSrc); pc.log(lt_s(3, 5));

gt

gt指令,greater than的缩写,用于测试第1个操作数是否大于第2个操作数。需指定是否有符号。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "gt_s") (param i32 i32) (result i32) local.get 0 local.get 1 i32.gt_s ) ) `; const { gt_s } = await WabtUtils.RunWat(watSrc); pc.log(gt_s(3, 5));

le

le指令,less or equal的缩写,用于测试第1个操作数是否小于等于第2个操作数。需指定是否有符号。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "le_s") (param i32 i32) (result i32) local.get 0 local.get 1 i32.le_s ) ) `; const { le_s } = await WabtUtils.RunWat(watSrc); pc.log(le_s(3, 5)); pc.log(le_s(3, 3));

ge

ge指令,greater or equal的缩写,用于测试第1个操作数是否大于等于第2个操作数。需指定是否有符号。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "ge_s") (param i32 i32) (result i32) local.get 0 local.get 1 i32.ge_s ) ) `; const { ge_s } = await WabtUtils.RunWat(watSrc); pc.log(ge_s(5, 3)); pc.log(ge_s(3, 3));

转换指令

i64.extend_i32

i64.extend_i32指令,将一个i32扩展为一个i64。需指定是否有符号。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "extend") (param i32) (result i64) local.get 0 i64.extend_i32_s ) ) `; const { extend } = await WabtUtils.RunWat(watSrc); let num = 5; pc.log(num); pc.log(typeof num); pc.log("%s", getBinStr(num, 8)); let result = extend(num); pc.log(result); pc.log(typeof result); pc.log("%s", getBinStr(result, 8));

i32.wrap_i64

i32.wrap_i64指令,将一个i64缩短为一个i32

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "wrap") (param i64) (result i32) local.get 0 i32.wrap_i64 ) ) `; const { wrap } = await WabtUtils.RunWat(watSrc); let num = 0x8000000000000001n; pc.log(num); pc.log(typeof num); pc.log("%s", getBinStr(num, 8)); let result = wrap(num); pc.log(result); pc.log(typeof result); pc.log("%s", getBinStr(result, 8));

trunc系列指令

trunc系列指令,舍弃一个浮点数的小数部分,得到一个整数。需指定是否有符号。

指令名称符号标记含义
i32.trunc_f32_ssigned舍弃f32的小数部分,得到一个有符号的i32
i32.trunc_f32_uunsigned舍弃f32的小数部分,得到一个无符号的i32
i32.trunc_f64_ssigned舍弃f64的小数部分,得到一个有符号的i32
i32.trunc_f64_uunsigned舍弃f64的小数部分,得到一个无符号的i32
i64.trunc_f32_ssigned舍弃f32的小数部分,得到一个有符号的i64
i64.trunc_f32_uunsigned舍弃f32的小数部分,得到一个无符号的i64
i64.trunc_f64_ssigned舍弃f64的小数部分,得到一个有符号的i64
i64.trunc_f64_uunsigned舍弃f64的小数部分,得到一个无符号的i64

位域相等时:

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "i32_trunc_f32_s") (param f32) (result i32) local.get 0 i32.trunc_f32_s ) ) `; const { i32_trunc_f32_s } = await WabtUtils.RunWat(watSrc); let num = -7.68; pc.log(i32_trunc_f32_s(num));

舍弃宽位域的浮点数的小数部分时,先舍弃小数部分,再缩短为位域较短的数据类型。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "i32_trunc_f64_s") (param f64) (result i32) local.get 0 i32.trunc_f64_s ) ) `; const { i32_trunc_f64_s } = await WabtUtils.RunWat(watSrc); let num = -78237.1928368; pc.log(i32_trunc_f64_s(num));

此时还可使用整数饱和算法的变体i32.trunc_sat_f64

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "i32_trunc_f64_s") (param f64) (result i32) local.get 0 i32.trunc_sat_f64_s ) ) `; const { i32_trunc_f64_s } = await WabtUtils.RunWat(watSrc); let num = -78237.1928368; pc.log(i32_trunc_f64_s(num));

舍弃短位域的浮点数的小数部分时,先舍弃小数部分,再扩展为位域较宽的数据类型。

const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "i64_trunc_f32_s") (param f32) (result i64) local.get 0 i64.trunc_f32_s ) ) `; const { i64_trunc_f32_s } = await WabtUtils.RunWat(watSrc); let num = -78237.1928368; let result = i64_trunc_f32_s(num); pc.log(result); pc.log(typeof result);

reinterpret系列指令

reinterpret系列指令,根据一个浮点数的二进制,重新解释为一个位域相等的整数。

指令名称含义
i32.reinterpret_f32f32解释为i32
i64.reinterpret_f64f64解释为i64
const { getBinStr } = await import('/js/esm/BinUtils.js'); let watSrc = ` (module (func (export "reinterpret") (param f32) (result i32) local.get 0 i32.reinterpret_f32 ) ) `; const { reinterpret } = await WabtUtils.RunWat(watSrc); let num = 0.125; pc.log(num); pc.log("%s", getBinStr(num, 8)); let result = reinterpret(num); pc.log(result); pc.log("%s", getBinStr(result, 8));

参考资源

Main

  1. W3C version (single page)
  2. Doc version (webassembly.org)

Number

  1. Number Types
  2. Numeric Instructions
  3. Numerics
  4. WebAssembly Numeric Instructions (MDN)