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的指令是基于特定类型的指令,因此i32及i64的div总是返回一个整数值,这与JavaScript的除法不同。当不能整除时,Wasm的除法结果只返回整数部分,而JavaScript的除法结果返回一个浮点数。若要得到一个浮点数值,可调用f32或f64的div指令。
此外,在表示加减乘除的相应指令add, sub, mul, div, rem中,只有div及rem需明确指定是否有符号。
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个操作数,返回一个表示真假的数值,1为true,0为false,在语法表中使用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个操作数,返回一个表示真假的数值,1为true,0为false,在语法表中使用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_s | signed | 舍弃f32的小数部分,得到一个有符号的i32 |
i32.trunc_f32_u | unsigned | 舍弃f32的小数部分,得到一个无符号的i32 |
i32.trunc_f64_s | signed | 舍弃f64的小数部分,得到一个有符号的i32 |
i32.trunc_f64_u | unsigned | 舍弃f64的小数部分,得到一个无符号的i32 |
i64.trunc_f32_s | signed | 舍弃f32的小数部分,得到一个有符号的i64 |
i64.trunc_f32_u | unsigned | 舍弃f32的小数部分,得到一个无符号的i64 |
i64.trunc_f64_s | signed | 舍弃f64的小数部分,得到一个有符号的i64 |
i64.trunc_f64_u | unsigned | 舍弃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_f32 | 将f32解释为i32 |
i64.reinterpret_f64 | 将f64解释为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));