зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1655042: Support Cranelift-based Wasm validation. r=jseward
This patch is an update of Benjamin Bouvier's WIP patch that supports Cranelift's new Wasm validator functionality, as introduced in bytecodealliance/wasmtime#2059. Previously, Baldrdash allowed SpiderMonkey to validate Wasm function bodies before invoking Cranelift. The Cranelift PR now causes validation to happen in the function-body compiler. Because the Cranelift backend now takes on this responsibility, we no longer need to invoke the SpiderMonkey function-body validator. This requires some significant new glue in the Baldrdash bindings to allow Cranelift's Wasm frontend to access module type information. Co-authored-by: Benjamin Bouvier <benj@benj.me> Differential Revision: https://phabricator.services.mozilla.com/D92504
This commit is contained in:
Родитель
b1ad6c43eb
Коммит
abaa8e4d14
|
@ -59,13 +59,27 @@ function wasmCompilationShouldFail(bin, compile_error_regex) {
|
|||
}
|
||||
}
|
||||
|
||||
function typeToCraneliftName(ty) {
|
||||
switch(ty) {
|
||||
case 'externref':
|
||||
return 'ExternRef';
|
||||
case 'funcref':
|
||||
return 'FuncRef';
|
||||
default:
|
||||
return ty.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
function mismatchError(actual, expect) {
|
||||
var str = `type mismatch: expression has type ${actual} but expected ${expect}`;
|
||||
let actualCL = typeToCraneliftName(actual);
|
||||
let expectCL = typeToCraneliftName(expect);
|
||||
var str = `(type mismatch: expression has type ${actual} but expected ${expect})|` +
|
||||
`(type mismatch: expected Some\\(${expectCL}\\), found Some\\(${actualCL}\\))`;
|
||||
return RegExp(str);
|
||||
}
|
||||
|
||||
const emptyStackError = /from empty stack/;
|
||||
const unusedValuesError = /unused values not explicitly dropped by end of block/;
|
||||
const emptyStackError = /(from empty stack)|(nothing on stack)/;
|
||||
const unusedValuesError = /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/;
|
||||
|
||||
function jsify(wasmVal) {
|
||||
if (wasmVal === 'nan')
|
||||
|
|
|
@ -158,7 +158,7 @@ assertEq(wasmEvalText('(module (func (param i32) (result i32) (local.get 0)) (ex
|
|||
assertEq(wasmEvalText('(module (func (param i32) (param i32) (result i32) (local.get 0)) (export "" (func 0)))').exports[""](42, 43), 42);
|
||||
assertEq(wasmEvalText('(module (func (param i32) (param i32) (result i32) (local.get 1)) (export "" (func 0)))').exports[""](42, 43), 43);
|
||||
|
||||
wasmFailValidateText('(module (func (local.get 0)))', /local.get index out of range/);
|
||||
wasmFailValidateText('(module (func (local.get 0)))', /(local.get index out of range)|(local index out of bounds)/);
|
||||
wasmFailValidateText('(module (func (result f32) (local i32) (local.get 0)))', mismatchError("i32", "f32"));
|
||||
wasmFailValidateText('(module (func (result i32) (local f32) (local.get 0)))', mismatchError("f32", "i32"));
|
||||
wasmFailValidateText('(module (func (param i32) (result f32) (local f32) (local.get 0)))', mismatchError("i32", "f32"));
|
||||
|
@ -171,7 +171,7 @@ wasmFullPass('(module (func (result i32) (local i32) (local.get 0)) (export "run
|
|||
wasmFullPass('(module (func (param i32) (result i32) (local f32) (local.get 0)) (export "run" (func 0)))', 0);
|
||||
wasmFullPass('(module (func (param i32) (result f32) (local f32) (local.get 1)) (export "run" (func 0)))', 0);
|
||||
|
||||
wasmFailValidateText('(module (func (local.set 0 (i32.const 0))))', /local.set index out of range/);
|
||||
wasmFailValidateText('(module (func (local.set 0 (i32.const 0))))', /(local.set index out of range)|(local index out of bounds)/);
|
||||
wasmFailValidateText('(module (func (local f32) (local.set 0 (i32.const 0))))', mismatchError("i32", "f32"));
|
||||
wasmFailValidateText('(module (func (local f32) (local.set 0 (nop))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (local i32) (local f32) (local.set 0 (local.get 1))))', mismatchError("f32", "i32"));
|
||||
|
@ -215,7 +215,7 @@ wasmFailValidateText('(module (func (nop)) (func (call 0 (i32.const 0))))', unus
|
|||
|
||||
wasmFailValidateText('(module (func (param i32) (nop)) (func (call 0)))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (param f32) (nop)) (func (call 0 (i32.const 0))))', mismatchError("i32", "f32"));
|
||||
wasmFailValidateText('(module (func (nop)) (func (call 3)))', /callee index out of range/);
|
||||
wasmFailValidateText('(module (func (nop)) (func (call 3)))', /(callee index out of range)|(function index out of bounds)/);
|
||||
|
||||
wasmValidateText('(module (func (nop)) (func (call 0)))');
|
||||
wasmValidateText('(module (func (param i32) (nop)) (func (call 0 (i32.const 0))))');
|
||||
|
@ -277,7 +277,7 @@ wasmFullPass(`(module (import "" "evalcx" (func (param i32) (result i32))) (func
|
|||
if (typeof evaluate === 'function')
|
||||
evaluate(`new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module)'))) `, { fileName: null });
|
||||
|
||||
wasmFailValidateText(`(module (type $t (func)) (func (call_indirect (type $t) (i32.const 0))))`, /can't call_indirect without a table/);
|
||||
wasmFailValidateText(`(module (type $t (func)) (func (call_indirect (type $t) (i32.const 0))))`, /(can't call_indirect without a table)|(unknown table)/);
|
||||
|
||||
var {v2i, i2i, i2v} = wasmEvalText(`(module
|
||||
(type (func (result i32)))
|
||||
|
@ -359,4 +359,4 @@ wasmValidateText('(module (func (call $foo)) (func $foo (nop)))');
|
|||
wasmValidateText('(module (import "" "a" (func $bar)) (func (call $bar)) (func $foo (nop)))');
|
||||
|
||||
var exp = wasmEvalText(`(module (func (export "f")))`).exports;
|
||||
assertEq(Object.isFrozen(exp), true);
|
||||
assertEq(Object.isFrozen(exp), true);
|
||||
|
|
|
@ -133,7 +133,7 @@ wasmEval(moduleWithSections([memorySection0()]));
|
|||
assertErrorMessage(() => wasmEval(moduleWithSections([invalidMemorySection2()])), CompileError, /number of memories must be at most one/);
|
||||
|
||||
// Test early 'end'
|
||||
const bodyMismatch = /function body length mismatch/;
|
||||
const bodyMismatch = /(function body length mismatch)|(operators remaining after end of function)/;
|
||||
assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[EndCode]})])])), CompileError, bodyMismatch);
|
||||
assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[UnreachableCode,EndCode]})])])), CompileError, bodyMismatch);
|
||||
assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[EndCode,UnreachableCode]})])])), CompileError, bodyMismatch);
|
||||
|
@ -217,20 +217,20 @@ assertErrorMessage(() => wasmEval(moduleWithSections([
|
|||
|
||||
// Diagnose invalid block signature types.
|
||||
for (var bad of [0xff, 1, 0x3f])
|
||||
assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, bad, EndCode]})])])), CompileError, /invalid .*block type/);
|
||||
assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, bad, EndCode]})])])), CompileError, /(invalid .*block type)|(unknown type)/);
|
||||
|
||||
const multiValueModule = moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, 0, EndCode]})])]);
|
||||
if (wasmMultiValueEnabled()) {
|
||||
// In this test module, 0 denotes a void-to-void block type.
|
||||
assertEq(WebAssembly.validate(multiValueModule), true);
|
||||
} else {
|
||||
assertErrorMessage(() => wasmEval(multiValueModule), CompileError, /invalid .*block type/);
|
||||
assertErrorMessage(() => wasmEval(multiValueModule), CompileError, /(invalid .*block type)|(unknown type)/);
|
||||
}
|
||||
|
||||
// Ensure all invalid opcodes rejected
|
||||
for (let op of undefinedOpcodes) {
|
||||
let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[op]})])]);
|
||||
assertErrorMessage(() => wasmEval(binary), CompileError, /unrecognized opcode/);
|
||||
assertErrorMessage(() => wasmEval(binary), CompileError, /((unrecognized|Unknown) opcode)|(tail calls support is not enabled)|(Unexpected EOF)/);
|
||||
assertEq(WebAssembly.validate(binary), false);
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,7 @@ function checkIllegalPrefixed(prefix, opcode) {
|
|||
declSection([0]),
|
||||
bodySection([funcBody({locals:[],
|
||||
body:[prefix, ...varU32(opcode)]})])]);
|
||||
assertErrorMessage(() => wasmEval(binary), CompileError, /unrecognized opcode/);
|
||||
assertErrorMessage(() => wasmEval(binary), CompileError, /((unrecognized|Unknown) opcode)|(Unknown.*subopcode)|(Unexpected EOF)|(SIMD support is not enabled)|(invalid lane index)/);
|
||||
assertEq(WebAssembly.validate(binary), false);
|
||||
}
|
||||
|
||||
|
@ -330,7 +330,7 @@ for (let prefix of [ThreadPrefix, MiscPrefix, SimdPrefix, MozPrefix]) {
|
|||
// Prefix without a subsequent opcode. We must ask funcBody not to add an
|
||||
// End code after the prefix, so the body really is just the prefix byte.
|
||||
let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[prefix]}, /*withEndCode=*/false)])]);
|
||||
assertErrorMessage(() => wasmEval(binary), CompileError, /unable to read opcode/);
|
||||
assertErrorMessage(() => wasmEval(binary), CompileError, /(unable to read opcode)|(Unexpected EOF)|(Unknown opcode)/);
|
||||
assertEq(WebAssembly.validate(binary), false);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ assertEq(wasmEvalText('(module (func (result i32) (if (result i32) (i32.const 0)
|
|||
|
||||
// Even if we don't yield, sub expressions types still have to match.
|
||||
wasmFailValidateText('(module (func (param f32) (if (result i32) (i32.const 42) (i32.const 1) (local.get 0))) (export "" (func 0)))', mismatchError('f32', 'i32'));
|
||||
wasmFailValidateText('(module (func (if (result i32) (i32.const 42) (i32.const 1) (i32.const 0))) (export "" (func 0)))', /unused values not explicitly dropped by end of block/);
|
||||
wasmFailValidateText('(module (func (if (result i32) (i32.const 42) (i32.const 1) (i32.const 0))) (export "" (func 0)))', /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
wasmFullPass('(module (func (drop (if (result i32) (i32.const 42) (i32.const 1) (i32.const 0)))) (export "run" (func 0)))', undefined);
|
||||
wasmFullPass('(module (func (param f32) (if (i32.const 42) (drop (i32.const 1)) (drop (local.get 0)))) (export "run" (func 0)))', undefined, {}, 13.37);
|
||||
|
||||
|
@ -157,13 +157,13 @@ wasmFullPass(`(module
|
|||
assertEq(counter, 0);
|
||||
|
||||
// "if" doesn't return an expression value
|
||||
wasmFailValidateText('(module (func (result i32) (if (result i32) (i32.const 42) (i32.const 0))))', /if without else with a result value/);
|
||||
wasmFailValidateText('(module (func (result i32) (if (result i32) (i32.const 42) (i32.const 0))))', /(if without else with a result value)|(type mismatch: expected Some\(I32\) but nothing on stack)/);
|
||||
wasmFailValidateText('(module (func (result i32) (if (result i32) (i32.const 42) (drop (i32.const 0)))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (if (result i32) (i32.const 1) (i32.const 0) (if (result i32) (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
|
||||
wasmFailValidateText('(module (func (result i32) (if (result i32) (i32.const 1) (i32.const 0) (if (result i32) (i32.const 1) (i32.const 1)))))', /(if without else with a result value)|(type mismatch: expected Some\(I32\) but nothing on stack)/);
|
||||
wasmFailValidateText('(module (func (result i32) (if (result i32) (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (if (result i32) (i32.const 1) (i32.const 0) (if (result i32) (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
|
||||
wasmFailValidateText('(module (func (if (result i32) (i32.const 1) (i32.const 0) (if (result i32) (i32.const 1) (i32.const 1)))))', /(if without else with a result value)|(type mismatch: expected Some\(I32\) but nothing on stack)/);
|
||||
wasmFailValidateText('(module (func (if (result i32) (i32.const 1) (i32.const 0) (if (i32.const 1) (drop (i32.const 1))))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if (result i32) (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
|
||||
wasmFailValidateText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if (result i32) (i32.const 1) (i32.const 1)))))', /(if without else with a result value)|(type mismatch: expected Some\(I32\) but nothing on stack)/);
|
||||
wasmEvalText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -184,7 +184,7 @@ wasmFailValidateText('(module (func (result i32) (block (br 0))) (export "" (fun
|
|||
wasmFailValidateText('(module (func (result i32) (br 0)) (export "" (func 0)))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (block (br_if 0 (i32.const 0)))) (export "" (func 0)))', emptyStackError);
|
||||
|
||||
const DEPTH_OUT_OF_BOUNDS = /branch depth exceeds current nesting level/;
|
||||
const DEPTH_OUT_OF_BOUNDS = /(branch depth exceeds current nesting level)|(branch depth too large)/;
|
||||
|
||||
wasmFailValidateText('(module (func (br 1)))', DEPTH_OUT_OF_BOUNDS);
|
||||
wasmFailValidateText('(module (func (block (br 2))))', DEPTH_OUT_OF_BOUNDS);
|
||||
|
@ -315,7 +315,7 @@ wasmFailValidateText('(module (func (result i32) (br 0 (f32.const 42))))', misma
|
|||
wasmFailValidateText('(module (func (result i32) (block (br 0))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (block (result f32) (br 0 (f32.const 42)))))', mismatchError("f32", "i32"));
|
||||
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (block (if (result i32) (local.get 0) (br 0 (i32.const 42))))) (export "" (func 0)))`, /if without else with a result value/);
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (block (if (result i32) (local.get 0) (br 0 (i32.const 42))))) (export "" (func 0)))`, /(if without else with a result value)|(type mismatch: expected Some\(I32\) but nothing on stack)/);
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (block (result i32) (if (local.get 0) (drop (i32.const 42))) (br 0 (f32.const 42)))) (export "" (func 0)))`, mismatchError("f32", "i32"));
|
||||
|
||||
wasmFullPass('(module (func (result i32) (br 0 (i32.const 42)) (i32.const 13)) (export "run" (func 0)))', 42);
|
||||
|
@ -328,13 +328,13 @@ var f = wasmEvalText(`(module (func (param i32) (result i32) (block (result i32)
|
|||
assertEq(f(0), 43);
|
||||
assertEq(f(1), 43);
|
||||
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (block (result i32) (if (result i32) (local.get 0) (br 0 (i32.const 42))) (i32.const 43))) (export "" (func 0)))`, /if without else with a result value/);
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (block (result i32) (if (result i32) (local.get 0) (br 0 (i32.const 42))) (i32.const 43))) (export "" (func 0)))`, /(if without else with a result value)|(type mismatch: expected Some\(I32\) but nothing on stack)/);
|
||||
|
||||
var f = wasmEvalText(`(module (func (param i32) (result i32) (block (result i32) (if (local.get 0) (br 1 (i32.const 42))) (i32.const 43))) (export "" (func 0)))`).exports[""];
|
||||
assertEq(f(0), 43);
|
||||
assertEq(f(1), 42);
|
||||
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (block (br_if 0 (i32.const 42) (local.get 0)) (i32.const 43))) (export "" (func 0)))`, /unused values not explicitly dropped by end of block/);
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (block (br_if 0 (i32.const 42) (local.get 0)) (i32.const 43))) (export "" (func 0)))`, /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
|
||||
var f = wasmEvalText(`(module (func (param i32) (result i32) (block (result i32) (drop (br_if 0 (i32.const 42) (local.get 0))) (i32.const 43))) (export "" (func 0)))`).exports[""];
|
||||
assertEq(f(0), 43);
|
||||
|
@ -344,7 +344,7 @@ var f = wasmEvalText(`(module (func (param i32) (result i32) (block (result i32)
|
|||
assertEq(f(0), 43);
|
||||
assertEq(f(1), 43);
|
||||
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (block (result i32) (if (result i32) (local.get 0) (br 0 (i32.const 42))) (br 0 (i32.const 43)))) (export "" (func 0)))`, /if without else with a result value/);
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (block (result i32) (if (result i32) (local.get 0) (br 0 (i32.const 42))) (br 0 (i32.const 43)))) (export "" (func 0)))`, /(if without else with a result value)|(type mismatch: expected Some\(I32\) but nothing on stack)/);
|
||||
|
||||
var f = wasmEvalText(`(module (func (param i32) (result i32) (if (local.get 0) (br 1 (i32.const 42))) (br 0 (i32.const 43))) (export "" (func 0)))`).exports[""];
|
||||
assertEq(f(0), 43);
|
||||
|
@ -366,13 +366,13 @@ var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const
|
|||
assertEq(f(0), 0);
|
||||
assertEq(f(1), 0);
|
||||
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (result i32) (if (result i32) (local.get 0) (br 0 (i32.const 99))) (i32.const -1)))) (export "" (func 0)))`, /if without else with a result value/);
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (result i32) (if (result i32) (local.get 0) (br 0 (i32.const 99))) (i32.const -1)))) (export "" (func 0)))`, /(if without else with a result value)|(type mismatch: expected Some\(I32\) but nothing on stack)/);
|
||||
|
||||
var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (result i32) (if (local.get 0) (br 1 (i32.const 99))) (i32.const -1)))) (export "" (func 0)))`).exports[""];
|
||||
assertEq(f(0), 0);
|
||||
assertEq(f(1), 100);
|
||||
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (br_if 0 (i32.const 99) (local.get 0)) (i32.const -1)))) (export "" (func 0)))`, /unused values not explicitly dropped by end of block/);
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (br_if 0 (i32.const 99) (local.get 0)) (i32.const -1)))) (export "" (func 0)))`, /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
|
||||
var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (result i32) (drop (br_if 0 (i32.const 99) (local.get 0))) (i32.const -1)))) (export "" (func 0)))`).exports[""];
|
||||
assertEq(f(0), 0);
|
||||
|
@ -429,7 +429,7 @@ wasmFailValidateText(`(module (func (param i32) (result i32)
|
|||
(i32.const 7)
|
||||
)
|
||||
)
|
||||
) (export "" (func 0)))`, /unused values not explicitly dropped by end of block/);
|
||||
) (export "" (func 0)))`, /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
|
||||
wasmFullPass(`(module
|
||||
(func
|
||||
|
@ -467,7 +467,7 @@ wasmFullPass(`(module
|
|||
|
||||
wasmFailValidateText('(module (func (loop (br 2))))', DEPTH_OUT_OF_BOUNDS);
|
||||
|
||||
wasmFailValidateText('(module (func (result i32) (drop (loop (i32.const 2))) (i32.const 1)) (export "" (func 0)))', /unused values not explicitly dropped by end of block/);
|
||||
wasmFailValidateText('(module (func (result i32) (drop (loop (i32.const 2))) (i32.const 1)) (export "" (func 0)))', /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
wasmFullPass('(module (func (loop)) (export "run" (func 0)))', undefined);
|
||||
wasmFullPass('(module (func (result i32) (loop (drop (i32.const 2))) (i32.const 1)) (export "run" (func 0)))', 1);
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ wasmFailValidateText('(module (func (param i32) (result i32) (f32.sqrt (local.ge
|
|||
wasmFailValidateText('(module (func (param i32) (result f64) (f64.sqrt (local.get 0))))', mismatchError("i32", "f64"));
|
||||
wasmFailValidateText('(module (func (param f64) (result i32) (f64.sqrt (local.get 0))))', mismatchError("f64", "i32"));
|
||||
wasmFailValidateText('(module (func (param i32) (result i32) (f64.sqrt (local.get 0))))', mismatchError("i32", "f64"));
|
||||
wasmFailValidateText('(module (func (f32.sqrt (nop))))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func (f32.sqrt (nop))))', /(popping value from empty stack)|(type mismatch: expected Some\(F32\) but nothing on stack)/);
|
||||
|
||||
wasmFailValidateText('(module (func (param i32) (param f32) (result f32) (f32.add (local.get 0) (local.get 1))))', mismatchError("i32", "f32"));
|
||||
wasmFailValidateText('(module (func (param f32) (param i32) (result f32) (f32.add (local.get 0) (local.get 1))))', mismatchError("i32", "f32"));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const { CompileError, validate } = WebAssembly;
|
||||
|
||||
const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /unrecognized opcode|(Structure|reference|gc) types not enabled|invalid heap type|invalid inline block type|bad type|\(ref T\) types not enabled|Cranelift error in clifFunc/;
|
||||
const UNRECOGNIZED_OPCODE_OR_BAD_TYPE = /unrecognized opcode|(Structure|reference|gc) types not enabled|invalid heap type|invalid inline block type|bad type|\(ref T\) types not enabled|Invalid type|invalid function type/;
|
||||
|
||||
let simpleTests = [
|
||||
"(module (func (drop (ref.null any))))",
|
||||
|
|
|
@ -44,8 +44,8 @@ testInner('f32', 13.37, 0.1989, Math.fround);
|
|||
testInner('f64', 13.37, 0.1989, x => +x);
|
||||
|
||||
// Semantic errors.
|
||||
wasmFailValidateText(`(module (global (mut i32) (i32.const 1337)) (func (global.set 1 (i32.const 0))))`, /out of range/);
|
||||
wasmFailValidateText(`(module (global i32 (i32.const 1337)) (func (global.set 0 (i32.const 0))))`, /can't write an immutable global/);
|
||||
wasmFailValidateText(`(module (global (mut i32) (i32.const 1337)) (func (global.set 1 (i32.const 0))))`, /(out of range)|(global index out of bounds)/);
|
||||
wasmFailValidateText(`(module (global i32 (i32.const 1337)) (func (global.set 0 (i32.const 0))))`, /(can't write an immutable global)|(global is immutable)/);
|
||||
|
||||
// Big module with many variables: test that setting one doesn't overwrite the
|
||||
// other ones.
|
||||
|
|
|
@ -122,11 +122,11 @@ function testStoreOOB(type, ext, base, offset, align, value) {
|
|||
}
|
||||
|
||||
function badLoadModule(type, ext) {
|
||||
wasmFailValidateText( `(module (func (param i32) (${type}.load${ext} (local.get 0))) (export "" (func 0)))`, /can't touch memory/);
|
||||
wasmFailValidateText( `(module (func (param i32) (${type}.load${ext} (local.get 0))) (export "" (func 0)))`, /(can't touch memory)|(unknown memory 0)/);
|
||||
}
|
||||
|
||||
function badStoreModule(type, ext) {
|
||||
wasmFailValidateText(`(module (func (param i32) (${type}.store${ext} (local.get 0) (${type}.const 0))) (export "" (func 0)))`, /can't touch memory/);
|
||||
wasmFailValidateText(`(module (func (param i32) (${type}.store${ext} (local.get 0) (${type}.const 0))) (export "" (func 0)))`, /(can't touch memory)|(unknown memory 0)/);
|
||||
}
|
||||
|
||||
// Can't touch memory.
|
||||
|
|
|
@ -142,7 +142,7 @@ function tab_test_nofail(insn1, insn2,
|
|||
|
||||
// drop with no memory and no data segments
|
||||
mem_test("data.drop 0", "",
|
||||
WebAssembly.CompileError, /data.drop segment index out of range/,
|
||||
WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 0)/,
|
||||
/*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
|
||||
|
||||
// drop with no memory but with both passive and active segments, ix in range
|
||||
|
@ -154,7 +154,7 @@ mem_test("data.drop 3", "",
|
|||
|
||||
// drop with no memory but with passive segments only, ix out of range
|
||||
mem_test("data.drop 2", "",
|
||||
WebAssembly.CompileError, /data.drop segment index out of range/,
|
||||
WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 2)/,
|
||||
/*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
|
||||
|
||||
// drop with no memory but with passive segments only, ix in range
|
||||
|
@ -164,16 +164,16 @@ mem_test_nofail("data.drop 1", "",
|
|||
|
||||
// init with no memory and no data segs
|
||||
mem_test("(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError, /can't touch memory without memory/,
|
||||
WebAssembly.CompileError, /(can't touch memory without memory)|(unknown memory 0)/,
|
||||
/*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
|
||||
|
||||
// drop with data seg ix out of range
|
||||
mem_test("data.drop 4", "",
|
||||
WebAssembly.CompileError, /data.drop segment index out of range/);
|
||||
WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 4)/);
|
||||
|
||||
// init with data seg ix out of range
|
||||
mem_test("(memory.init 4 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError, /memory.init segment index out of range/);
|
||||
WebAssembly.CompileError, /(memory.init segment index out of range)|(unknown data segment 4)/);
|
||||
|
||||
// drop with data seg ix indicating an active segment
|
||||
mem_test("data.drop 2", "");
|
||||
|
@ -236,17 +236,17 @@ mem_test("",
|
|||
// drop: too many args
|
||||
mem_test("data.drop 1 (i32.const 42)", "",
|
||||
WebAssembly.CompileError,
|
||||
/unused values not explicitly dropped by end of block/);
|
||||
/(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
|
||||
// init: too many args
|
||||
mem_test("(memory.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))",
|
||||
"",
|
||||
WebAssembly.CompileError, /unused values/);
|
||||
WebAssembly.CompileError, /(unused values)|(values remaining on stack at end of block)/);
|
||||
|
||||
// init: too few args
|
||||
mem_test("(memory.init 1 (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError,
|
||||
/popping value from empty stack/);
|
||||
/(popping value from empty stack)|(expected Some\(I32\) but nothing on stack)/);
|
||||
|
||||
// invalid argument types
|
||||
{
|
||||
|
@ -268,7 +268,7 @@ mem_test("(memory.init 1 (i32.const 1) (i32.const 1))", "",
|
|||
// drop with no tables and no elem segments
|
||||
tab_test("elem.drop 0", "",
|
||||
WebAssembly.CompileError,
|
||||
/element segment index out of range for elem.drop/,
|
||||
/(element segment index out of range for elem.drop)|(segment index out of bounds)/,
|
||||
/*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
|
||||
|
||||
// drop with no tables but with both passive and active segments, ix in range
|
||||
|
@ -281,7 +281,7 @@ tab_test("elem.drop 3", "",
|
|||
// drop with no tables but with passive segments only, ix out of range
|
||||
tab_test("elem.drop 2", "",
|
||||
WebAssembly.CompileError,
|
||||
/element segment index out of range for elem.drop/,
|
||||
/(element segment index out of range for elem.drop)|(segment index out of bounds)/,
|
||||
/*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
|
||||
|
||||
// drop with no tables but with passive segments only, ix in range
|
||||
|
@ -291,16 +291,16 @@ tab_test_nofail("elem.drop 1", "",
|
|||
|
||||
// init with no table
|
||||
tab_test("(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError, /table index out of range/,
|
||||
WebAssembly.CompileError, /(table index out of range)|(table index out of bounds)/,
|
||||
/*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
|
||||
|
||||
// drop with elem seg ix out of range
|
||||
tab_test("elem.drop 4", "",
|
||||
WebAssembly.CompileError, /element segment index out of range for elem.drop/);
|
||||
WebAssembly.CompileError, /(element segment index out of range for elem.drop)|(segment index out of bounds)/);
|
||||
|
||||
// init with elem seg ix out of range
|
||||
tab_test("(table.init 4 (i32.const 12) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError, /table.init segment index out of range/);
|
||||
WebAssembly.CompileError, /(table.init segment index out of range)|(segment index out of bounds)/);
|
||||
|
||||
// drop with elem seg ix indicating an active segment
|
||||
tab_test("elem.drop 2", "");
|
||||
|
@ -362,17 +362,17 @@ tab_test("",
|
|||
// drop: too many args
|
||||
tab_test("elem.drop 1 (i32.const 42)", "",
|
||||
WebAssembly.CompileError,
|
||||
/unused values not explicitly dropped by end of block/);
|
||||
/(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
|
||||
// init: too many args
|
||||
tab_test("(table.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))",
|
||||
"",
|
||||
WebAssembly.CompileError, /unused values/);
|
||||
WebAssembly.CompileError, /(unused values)|(values remaining on stack at end of block)/);
|
||||
|
||||
// init: too few args
|
||||
tab_test("(table.init 1 (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError,
|
||||
/popping value from empty stack/);
|
||||
/(popping value from empty stack)|(expected Some\(I32\) but nothing on stack)/);
|
||||
|
||||
// invalid argument types
|
||||
{
|
||||
|
|
|
@ -294,10 +294,10 @@ checkNoDataCount([I32ConstCode, 0,
|
|||
I32ConstCode, 0,
|
||||
I32ConstCode, 0,
|
||||
MiscPrefix, MemoryInitCode, 0, 0],
|
||||
/memory.init requires a DataCount section/);
|
||||
/(memory.init requires a DataCount section)|(unknown data segment)/);
|
||||
|
||||
checkNoDataCount([MiscPrefix, DataDropCode, 0],
|
||||
/data.drop requires a DataCount section/);
|
||||
/(data.drop requires a DataCount section)|(unknown data segment)/);
|
||||
|
||||
//---------------------------------------------------------------------//
|
||||
//---------------------------------------------------------------------//
|
||||
|
@ -318,7 +318,7 @@ function checkMiscPrefixed(opcode, expect_failure) {
|
|||
MiscPrefix, ...opcode]})])]);
|
||||
if (expect_failure) {
|
||||
assertErrorMessage(() => new WebAssembly.Module(binary),
|
||||
WebAssembly.CompileError, /unrecognized opcode/);
|
||||
WebAssembly.CompileError, /(unrecognized opcode)|(Unknown.*subopcode)/);
|
||||
} else {
|
||||
assertEq(WebAssembly.validate(binary), true);
|
||||
}
|
||||
|
@ -370,7 +370,7 @@ checkMiscPrefixed([0x13], true); // table.size+1, which is currently unas
|
|||
)`;
|
||||
assertErrorMessage(() => wasmEvalText(text1),
|
||||
WebAssembly.CompileError,
|
||||
/popping value from empty stack/);
|
||||
/(popping value from empty stack)|(expected Some\(I32\) but nothing on stack)/);
|
||||
let text2 =
|
||||
`(module
|
||||
(memory (export "memory") 1 1)
|
||||
|
@ -384,7 +384,7 @@ checkMiscPrefixed([0x13], true); // table.size+1, which is currently unas
|
|||
)`;
|
||||
assertErrorMessage(() => wasmEvalText(text2),
|
||||
WebAssembly.CompileError,
|
||||
/unused values not explicitly dropped by end of block/);
|
||||
/(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,7 +399,7 @@ checkMiscPrefixed([0x13], true); // table.size+1, which is currently unas
|
|||
)`;
|
||||
assertErrorMessage(() => wasmEvalText(text),
|
||||
WebAssembly.CompileError,
|
||||
/can't touch memory without memory/);
|
||||
/(can't touch memory without memory)|(unknown memory)/);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ assertErrorMessage(() => {
|
|||
(func (result funcref) ref.func 10)
|
||||
)
|
||||
`);
|
||||
}, WebAssembly.CompileError, /function index out of range/);
|
||||
}, WebAssembly.CompileError, /(function index out of range)|(function index out of bounds)/);
|
||||
|
||||
function validFuncRefText(forwardDeclare, tbl_type) {
|
||||
return wasmEvalText(`
|
||||
|
@ -88,7 +88,7 @@ function validFuncRefText(forwardDeclare, tbl_type) {
|
|||
}
|
||||
|
||||
// referenced function must be forward declared somehow
|
||||
assertErrorMessage(() => validFuncRefText('', 'funcref'), WebAssembly.CompileError, /function index is not declared in a section before the code section/);
|
||||
assertErrorMessage(() => validFuncRefText('', 'funcref'), WebAssembly.CompileError, /(function index is not declared in a section before the code section)|(undeclared function reference)/);
|
||||
|
||||
// referenced function can be forward declared via segments
|
||||
assertEq(validFuncRefText('(elem 0 (i32.const 0) func $referenced)', 'funcref') instanceof WebAssembly.Instance, true);
|
||||
|
@ -106,7 +106,7 @@ assertEq(validFuncRefText('(global funcref (ref.func $referenced))', 'externref'
|
|||
assertEq(validFuncRefText('(export "referenced" (func $referenced))', 'externref') instanceof WebAssembly.Instance, true);
|
||||
|
||||
// reference function cannot be forward declared via start
|
||||
assertErrorMessage(() => validFuncRefText('(start $referenced)', 'externref'), WebAssembly.CompileError, /function index is not declared in a section before the code section/);
|
||||
assertErrorMessage(() => validFuncRefText('(start $referenced)', 'externref'), WebAssembly.CompileError, /(function index is not declared in a section before the code section)|(undeclared function reference)/);
|
||||
|
||||
// Tests not expressible in the text format.
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $expected-3-args-got-0
|
||||
(table.fill $t)
|
||||
))`),
|
||||
WebAssembly.CompileError, /popping value from empty stack/);
|
||||
WebAssembly.CompileError, /(popping value from empty stack)|(nothing on stack)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -158,7 +158,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $expected-3-args-got-1
|
||||
(table.fill $t (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError, /popping value from empty stack/);
|
||||
WebAssembly.CompileError, /(popping value from empty stack)|(nothing on stack)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -166,7 +166,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $expected-3-args-got-2
|
||||
(table.fill $t (ref.null extern) (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError, /popping value from empty stack/);
|
||||
WebAssembly.CompileError, /(popping value from empty stack)|(nothing on stack)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -175,7 +175,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(table.fill $t (i32.const 0) (ref.null extern) (f64.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError,
|
||||
/type mismatch: expression has type f64 but expected i32/);
|
||||
/(type mismatch: expression has type f64 but expected i32)|(type mismatch: expected Some\(I32\), found Some\(F64\))/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -184,7 +184,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(table.fill $t (i32.const 0) (f32.const 0) (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError,
|
||||
/type mismatch: expression has type f32 but expected externref/);
|
||||
/(type mismatch: expression has type f32 but expected externref)|(type mismatch: expected Some\(ExternRef\), found Some\(F32\))/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -193,7 +193,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(table.fill $t (i64.const 0) (ref.null extern) (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError,
|
||||
/type mismatch: expression has type i64 but expected i32/);
|
||||
/(type mismatch: expression has type i64 but expected i32)|(type mismatch: expected Some\(I32\), found Some\(I64\))/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -202,7 +202,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(table.fill $t (i32.const 0) (ref.null extern) (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError,
|
||||
/popping value from empty stack/);
|
||||
/(popping value from empty stack)|(nothing on stack)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -211,4 +211,4 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(table.fill (i32.const 0) (local.get $v) (i32.const 0)))
|
||||
)`),
|
||||
WebAssembly.CompileError,
|
||||
/expression has type externref but expected funcref/);
|
||||
/(expression has type externref but expected funcref)|(type mismatch: expected Some\(FuncRef\), found Some\(ExternRef\))/);
|
||||
|
|
|
@ -92,7 +92,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func (export "f")
|
||||
(table.copy 0 1 (i32.const 5) (i32.const 0) (i32.const 5))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/expression has type funcref but expected externref/);
|
||||
/(expression has type funcref but expected externref)|(type mismatch)/);
|
||||
|
||||
// Wasm: table.copy from table(externref) to table(funcref) should not work
|
||||
|
||||
|
@ -103,7 +103,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func (export "f")
|
||||
(table.copy 0 1 (i32.const 0) (i32.const 0) (i32.const 5))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/expression has type externref but expected funcref/);
|
||||
/(expression has type externref but expected funcref)|(type mismatch)/);
|
||||
|
||||
// Wasm: Element segments of funcref can't target tables of externref
|
||||
|
||||
|
@ -136,7 +136,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func
|
||||
(table.init 0 (i32.const 0) (i32.const 0) (i32.const 0))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/expression has type funcref but expected externref/);
|
||||
/(expression has type funcref but expected externref)|(type mismatch)/);
|
||||
|
||||
|
||||
// Wasm: table.init on table-of-funcref is not allowed when the segment has
|
||||
|
@ -149,7 +149,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func
|
||||
(table.init 0 (i32.const 0) (i32.const 0) (i32.const 0))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/expression has type externref but expected funcref/);
|
||||
/(expression has type externref but expected funcref)|(type mismatch)/);
|
||||
|
||||
// Wasm: table types must match at link time
|
||||
|
||||
|
@ -170,7 +170,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func (result i32)
|
||||
(call_indirect (type $t) (i32.const 37))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/indirect calls must go through a table of 'funcref'/);
|
||||
/(indirect calls must go through a table of 'funcref')|(type mismatch)/);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -243,7 +243,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func (export "f") (param i32)
|
||||
(drop (table.get (local.get 0)))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.get/);
|
||||
/(table index out of range for table.get)|(table index out of bounds)/);
|
||||
|
||||
// table.set in bounds with i32 x externref - works, no value generated
|
||||
// table.set with null - works
|
||||
|
@ -276,7 +276,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func (export "set_ref") (param i32) (param externref)
|
||||
(table.set (local.get 0) (local.get 1))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/type mismatch: expression has type externref but expected funcref/);
|
||||
/(type mismatch: expression has type externref but expected funcref)|(type mismatch: expected Some\(FuncRef\), found Some\(ExternRef\))/);
|
||||
|
||||
// table.set with non-i32 index - fails validation
|
||||
|
||||
|
@ -312,7 +312,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func (export "f") (param externref)
|
||||
(table.set (i32.const 0) (local.get 0))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.set/);
|
||||
/(table index out of range for table.set)|(table index out of bounds)/);
|
||||
|
||||
function testTableGrow(lhs_type, lhs_reftype, rhs_type, x) {
|
||||
let ins = wasmEvalText(
|
||||
|
@ -360,7 +360,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func (export "grow2") (param i32) (param externref) (result i32)
|
||||
(table.grow (local.get 1) (local.get 0))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/type mismatch: expression has type externref but expected funcref/);
|
||||
/(type mismatch: expression has type externref but expected funcref)|(type mismatch: expected Some\(FuncRef\), found Some\(ExternRef\))/);
|
||||
|
||||
// Special case for private tables without a maximum
|
||||
|
||||
|
@ -393,7 +393,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(func (export "f") (param i32)
|
||||
(table.grow (ref.null extern) (local.get 0))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.grow/);
|
||||
/(table index out of range for table.grow)|(table index out of bounds)/);
|
||||
|
||||
// table.size on table of externref
|
||||
|
||||
|
|
|
@ -326,7 +326,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $f (result externref)
|
||||
(table.get 2 (i32.const 0))))`),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.get/);
|
||||
/(table index out of range for table.get)|(table index out of bounds)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -335,7 +335,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $f (param externref)
|
||||
(table.set 2 (i32.const 0) (local.get 0))))`),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.set/);
|
||||
/(table index out of range for table.set)|(table index out of bounds)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -344,7 +344,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $f (param externref)
|
||||
(table.copy 0 2 (i32.const 0) (i32.const 0) (i32.const 2))))`),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.copy/);
|
||||
/(table index out of range for table.copy)|(table index out of bounds)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -353,7 +353,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $f (param externref)
|
||||
(table.copy 2 0 (i32.const 0) (i32.const 0) (i32.const 2))))`),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.copy/);
|
||||
/(table index out of range for table.copy)|(table index out of bounds)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -362,7 +362,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $f (result i32)
|
||||
(table.size 2)))`),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.size/);
|
||||
/(table index out of range for table.size)|(table index out of bounds)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -371,7 +371,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $f (result i32)
|
||||
(table.grow 2 (ref.null extern) (i32.const 1))))`),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.grow/);
|
||||
/(table index out of range for table.grow)|(table index out of bounds)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -380,14 +380,14 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $f (result i32)
|
||||
(table.init 2 0 (i32.const 0) (i32.const 0) (i32.const 0))))`),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for table.init/);
|
||||
/(table index out of range for table.init)|(table index out of bounds)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t0 2 funcref)
|
||||
(elem 2 (i32.const 0) func))`),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for element segment/);
|
||||
/(table index out of range for element segment)|(table index out of bounds)/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
|
@ -396,7 +396,7 @@ assertErrorMessage(() => wasmEvalText(
|
|||
(func $f (result i32)
|
||||
(call_indirect 2 (type $ft) (f64.const 3.14) (i32.const 0))))`),
|
||||
WebAssembly.CompileError,
|
||||
/table index out of range for call_indirect/);
|
||||
/(table index out of range for call_indirect)|(table index out of bounds)/);
|
||||
|
||||
// Syntax errors when parsing text
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@ wasmFailValidateText(`(module
|
|||
(func (param i32) (result i32)
|
||||
(loop (if (i32.const 0) (br 0)) (local.get 0)))
|
||||
(export "" (func 0))
|
||||
)`, /unused values not explicitly dropped by end of block/);
|
||||
)`, /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
|
||||
wasmFailValidateText(`(module
|
||||
(func (param i32)
|
||||
(loop (if (i32.const 0) (br 0)) (local.get 0)))
|
||||
(export "" (func 0))
|
||||
)`, /unused values not explicitly dropped by end of block/);
|
||||
)`, /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
|
||||
|
||||
wasmFailValidateText(`(module
|
||||
(func (param i32) (result i32)
|
||||
|
@ -204,7 +204,7 @@ wasmFailValidateText(`
|
|||
(br_table 1 0 (i32.const 15))
|
||||
)
|
||||
)
|
||||
)`, /br_table targets must all have the same arity/);
|
||||
)`, /(br_table targets must all have the same arity)|(br_table target labels have different types)/);
|
||||
|
||||
wasmFailValidateText(`
|
||||
(module
|
||||
|
@ -214,7 +214,7 @@ wasmFailValidateText(`
|
|||
(br_table 1 0 (i32.const 15))
|
||||
)
|
||||
)
|
||||
)`, /br_table targets must all have the same arity/);
|
||||
)`, /(br_table targets must all have the same arity)|(br_table target labels have different types)/);
|
||||
|
||||
wasmValidateText(`
|
||||
(module
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
wasmFailValidateText('(module (func (select (i32.const 0) (i32.const 0) (f32.const 0))))', mismatchError("f32", "i32"));
|
||||
|
||||
wasmFailValidateText('(module (func (select (i32.const 0) (f32.const 0) (i32.const 0))) (export "" (func 0)))', /select operand types must match/);
|
||||
wasmFailValidateText('(module (func (select (i32.const 0) (f32.const 0) (i32.const 0))) (export "" (func 0)))', /(select operand types must match)|(type mismatch: expected Some\(F32\), found Some\(I32\))/);
|
||||
wasmFailValidateText('(module (func (select (block ) (i32.const 0) (i32.const 0))) (export "" (func 0)))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (select (return) (i32.const 0) (i32.const 0))) (export "" (func 0)))', unusedValuesError);
|
||||
assertEq(wasmEvalText('(module (func (drop (select (return) (i32.const 0) (i32.const 0)))) (export "" (func 0)))').exports[""](), undefined);
|
||||
|
@ -169,14 +169,14 @@ if (wasmReftypesEnabled()) {
|
|||
(select (local.get 0) (local.get 0) (i32.const 0))
|
||||
drop
|
||||
))`,
|
||||
/untyped select/);
|
||||
/(untyped select)|(type mismatch: select only takes integral types)/);
|
||||
|
||||
wasmFailValidateText(`(module
|
||||
(func (param funcref)
|
||||
(select (local.get 0) (local.get 0) (i32.const 0))
|
||||
drop
|
||||
))`,
|
||||
/untyped select/);
|
||||
/(untyped select)|(type mismatch: select only takes integral types)/);
|
||||
|
||||
function testRefSelect(type, trueVal, falseVal) {
|
||||
// Always true condition
|
||||
|
|
|
@ -15,7 +15,7 @@ wasmFailValidateText(`(module (func (result v128)))`,
|
|||
/(v128 not enabled)|(bad type)/);
|
||||
|
||||
wasmFailValidateText(`(module (func (local v128)))`,
|
||||
/(v128 not enabled)|(bad type)/);
|
||||
/(v128 not enabled)|(bad type)|(SIMD support is not enabled)/);
|
||||
|
||||
wasmFailValidateText(`(module (global (import "m" "g") v128))`,
|
||||
/expected global type/);
|
||||
|
|
|
@ -20,7 +20,7 @@ assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (func (call_indi
|
|||
assertErrorMessage(() => wasmEvalText('(module (func (br $a)))'), SyntaxError, /failed to find label/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (block $a ) (br $a)))'), SyntaxError, /failed to find label/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(`(module (func (call ${0xffffffff})))`), WebAssembly.CompileError, /callee index out of range/);
|
||||
assertErrorMessage(() => wasmEvalText(`(module (func (call ${0xffffffff})))`), WebAssembly.CompileError, /(callee index out of range)|(function index out of bounds)/);
|
||||
assertErrorMessage(() => wasmEvalText(`(module (export "func" ${0xffffffff}))`), SyntaxError, parsingError);
|
||||
|
||||
wasmEvalText('(module (func (param $a i32)))');
|
||||
|
|
|
@ -292,6 +292,7 @@ class CraneliftContext {
|
|||
explicit CraneliftContext(const ModuleEnvironment& moduleEnv)
|
||||
: moduleEnv_(moduleEnv), compiler_(nullptr) {
|
||||
staticEnv_.ref_types_enabled = moduleEnv.refTypesEnabled();
|
||||
staticEnv_.threads_enabled = true;
|
||||
#ifdef WASM_SUPPORTS_HUGE_MEMORY
|
||||
if (moduleEnv.hugeMemoryEnabled()) {
|
||||
// In the huge memory configuration, we always reserve the full 4 GB
|
||||
|
@ -386,50 +387,86 @@ CraneliftModuleEnvironment::CraneliftModuleEnvironment(
|
|||
: env(&env), min_memory_length(env.minMemoryLength) {}
|
||||
|
||||
TypeCode env_unpack(BD_ValType valType) {
|
||||
return TypeCode(UnpackTypeCodeType(PackedTypeCode(valType.packed)));
|
||||
return UnpackTypeCodeType(PackedTypeCode(valType.packed));
|
||||
}
|
||||
|
||||
bool env_uses_shared_memory(const CraneliftModuleEnvironment* wrapper) {
|
||||
return wrapper->env->usesSharedMemory();
|
||||
size_t env_num_datas(const CraneliftModuleEnvironment* env) {
|
||||
return env->env->dataCount.valueOr(0);
|
||||
}
|
||||
|
||||
size_t env_num_types(const CraneliftModuleEnvironment* wrapper) {
|
||||
return wrapper->env->types.length();
|
||||
size_t env_num_elems(const CraneliftModuleEnvironment* env) {
|
||||
return env->env->elemSegments.length();
|
||||
}
|
||||
const FuncTypeWithId* env_type(const CraneliftModuleEnvironment* wrapper,
|
||||
TypeCode env_elem_typecode(const CraneliftModuleEnvironment* env,
|
||||
uint32_t index) {
|
||||
return UnpackTypeCodeType(env->env->elemSegments[index]->elemType.packed());
|
||||
}
|
||||
|
||||
uint32_t env_max_memory(const CraneliftModuleEnvironment* env) {
|
||||
return env->env->maxMemoryLength.valueOr(UINT32_MAX);
|
||||
}
|
||||
|
||||
bool env_uses_shared_memory(const CraneliftModuleEnvironment* env) {
|
||||
return env->env->usesSharedMemory();
|
||||
}
|
||||
|
||||
bool env_has_memory(const CraneliftModuleEnvironment* env) {
|
||||
return env->env->usesMemory();
|
||||
}
|
||||
|
||||
size_t env_num_types(const CraneliftModuleEnvironment* env) {
|
||||
return env->env->types.length();
|
||||
}
|
||||
const FuncTypeWithId* env_type(const CraneliftModuleEnvironment* env,
|
||||
size_t typeIndex) {
|
||||
return &wrapper->env->types[typeIndex].funcType();
|
||||
return &env->env->types[typeIndex].funcType();
|
||||
}
|
||||
|
||||
const FuncTypeWithId* env_func_sig(const CraneliftModuleEnvironment* wrapper,
|
||||
size_t env_num_funcs(const CraneliftModuleEnvironment* env) {
|
||||
return env->env->funcTypes.length();
|
||||
}
|
||||
const FuncTypeWithId* env_func_sig(const CraneliftModuleEnvironment* env,
|
||||
size_t funcIndex) {
|
||||
return wrapper->env->funcTypes[funcIndex];
|
||||
return env->env->funcTypes[funcIndex];
|
||||
}
|
||||
size_t env_func_sig_index(const CraneliftModuleEnvironment* env,
|
||||
size_t funcIndex) {
|
||||
return env->env->funcTypeIndices[funcIndex];
|
||||
}
|
||||
bool env_is_func_valid_for_ref(const CraneliftModuleEnvironment* env,
|
||||
uint32_t index) {
|
||||
return env->env->validForRefFunc.getBit(index);
|
||||
}
|
||||
|
||||
size_t env_func_import_tls_offset(const CraneliftModuleEnvironment* wrapper,
|
||||
size_t env_func_import_tls_offset(const CraneliftModuleEnvironment* env,
|
||||
size_t funcIndex) {
|
||||
return globalToTlsOffset(
|
||||
wrapper->env->funcImportGlobalDataOffsets[funcIndex]);
|
||||
return globalToTlsOffset(env->env->funcImportGlobalDataOffsets[funcIndex]);
|
||||
}
|
||||
|
||||
bool env_func_is_import(const CraneliftModuleEnvironment* wrapper,
|
||||
bool env_func_is_import(const CraneliftModuleEnvironment* env,
|
||||
size_t funcIndex) {
|
||||
return wrapper->env->funcIsImport(funcIndex);
|
||||
return env->env->funcIsImport(funcIndex);
|
||||
}
|
||||
|
||||
const FuncTypeWithId* env_signature(const CraneliftModuleEnvironment* wrapper,
|
||||
const FuncTypeWithId* env_signature(const CraneliftModuleEnvironment* env,
|
||||
size_t funcTypeIndex) {
|
||||
return &wrapper->env->types[funcTypeIndex].funcType();
|
||||
return &env->env->types[funcTypeIndex].funcType();
|
||||
}
|
||||
|
||||
const TableDesc* env_table(const CraneliftModuleEnvironment* wrapper,
|
||||
size_t env_num_tables(const CraneliftModuleEnvironment* env) {
|
||||
return env->env->tables.length();
|
||||
}
|
||||
const TableDesc* env_table(const CraneliftModuleEnvironment* env,
|
||||
size_t tableIndex) {
|
||||
return &wrapper->env->tables[tableIndex];
|
||||
return &env->env->tables[tableIndex];
|
||||
}
|
||||
|
||||
const GlobalDesc* env_global(const CraneliftModuleEnvironment* wrapper,
|
||||
size_t env_num_globals(const CraneliftModuleEnvironment* env) {
|
||||
return env->env->globals.length();
|
||||
}
|
||||
const GlobalDesc* env_global(const CraneliftModuleEnvironment* env,
|
||||
size_t globalIndex) {
|
||||
return &wrapper->env->globals[globalIndex];
|
||||
return &env->env->globals[globalIndex];
|
||||
}
|
||||
|
||||
bool wasm::CraneliftCompileFunctions(const ModuleEnvironment& moduleEnv,
|
||||
|
@ -484,9 +521,6 @@ bool wasm::CraneliftCompileFunctions(const ModuleEnvironment& moduleEnv,
|
|||
Decoder d(func.begin, func.end, func.lineOrBytecode, error);
|
||||
|
||||
size_t funcBytecodeSize = func.end - func.begin;
|
||||
if (!ValidateFunctionBody(moduleEnv, func.index, funcBytecodeSize, d)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t previousStackmapCount = code->stackMaps.length();
|
||||
|
||||
|
@ -495,8 +529,11 @@ bool wasm::CraneliftCompileFunctions(const ModuleEnvironment& moduleEnv,
|
|||
|
||||
CraneliftCompiledFunc clifFunc;
|
||||
|
||||
if (!cranelift_compile_function(*compiler, &clifInput, &clifFunc)) {
|
||||
*error = JS_smprintf("Cranelift error in clifFunc #%u", clifInput.index);
|
||||
char* clifError = nullptr;
|
||||
if (!cranelift_compile_function(*compiler, &clifInput, &clifFunc,
|
||||
&clifError)) {
|
||||
*error = JS_smprintf("%s", clifError);
|
||||
cranelift_compiler_free_error(clifError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -633,6 +670,16 @@ size_t table_tlsOffset(const TableDesc* table) {
|
|||
return globalToTlsOffset(table->globalDataOffset);
|
||||
}
|
||||
|
||||
uint32_t table_initialLimit(const TableDesc* table) {
|
||||
return table->initialLength;
|
||||
}
|
||||
uint32_t table_maximumLimit(const TableDesc* table) {
|
||||
return table->maximumLength.valueOr(UINT32_MAX);
|
||||
}
|
||||
TypeCode table_elementTypeCode(const TableDesc* table) {
|
||||
return UnpackTypeCodeType(table->elemType.packed());
|
||||
}
|
||||
|
||||
// Sig
|
||||
|
||||
size_t funcType_numArgs(const FuncTypeWithId* funcType) {
|
||||
|
@ -641,7 +688,7 @@ size_t funcType_numArgs(const FuncTypeWithId* funcType) {
|
|||
|
||||
const BD_ValType* funcType_args(const FuncTypeWithId* funcType) {
|
||||
static_assert(sizeof(BD_ValType) == sizeof(ValType), "update BD_ValType");
|
||||
return (const BD_ValType*)&funcType->args()[0];
|
||||
return (const BD_ValType*)funcType->args().begin();
|
||||
}
|
||||
|
||||
size_t funcType_numResults(const FuncTypeWithId* funcType) {
|
||||
|
@ -650,7 +697,7 @@ size_t funcType_numResults(const FuncTypeWithId* funcType) {
|
|||
|
||||
const BD_ValType* funcType_results(const FuncTypeWithId* funcType) {
|
||||
static_assert(sizeof(BD_ValType) == sizeof(ValType), "update BD_ValType");
|
||||
return (const BD_ValType*)&funcType->results()[0];
|
||||
return (const BD_ValType*)funcType->results().begin();
|
||||
}
|
||||
|
||||
FuncTypeIdDescKind funcType_idKind(const FuncTypeWithId* funcType) {
|
||||
|
|
|
@ -2069,6 +2069,9 @@ static bool DecodeImport(Decoder& d, ModuleEnvironment* env) {
|
|||
if (!env->funcTypes.append(&env->types[funcTypeIndex].funcType())) {
|
||||
return false;
|
||||
}
|
||||
if (!env->funcTypeIndices.append(funcTypeIndex)) {
|
||||
return false;
|
||||
}
|
||||
if (env->funcTypes.length() > MaxFuncs) {
|
||||
return d.fail("too many functions");
|
||||
}
|
||||
|
@ -2173,6 +2176,9 @@ static bool DecodeFunctionSection(Decoder& d, ModuleEnvironment* env) {
|
|||
if (!env->funcTypes.reserve(numFuncs.value())) {
|
||||
return false;
|
||||
}
|
||||
if (!env->funcTypeIndices.reserve(numFuncs.value())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < numDefs; i++) {
|
||||
uint32_t funcTypeIndex;
|
||||
|
@ -2180,6 +2186,7 @@ static bool DecodeFunctionSection(Decoder& d, ModuleEnvironment* env) {
|
|||
return false;
|
||||
}
|
||||
env->funcTypes.infallibleAppend(&env->types[funcTypeIndex].funcType());
|
||||
env->funcTypeIndices.infallibleAppend(funcTypeIndex);
|
||||
}
|
||||
|
||||
return d.finishSection(*range, "function");
|
||||
|
|
|
@ -138,6 +138,7 @@ struct ModuleEnvironment {
|
|||
uint32_t numStructTypes;
|
||||
TypeDefVector types;
|
||||
FuncTypeWithIdPtrVector funcTypes;
|
||||
Uint32Vector funcTypeIndices;
|
||||
Uint32Vector funcImportGlobalDataOffsets;
|
||||
GlobalDescVector globals;
|
||||
TableDescVector tables;
|
||||
|
|
|
@ -68,6 +68,7 @@ struct CraneliftStaticEnvironment {
|
|||
bool has_lzcnt;
|
||||
bool platform_is_windows;
|
||||
bool ref_types_enabled;
|
||||
bool threads_enabled;
|
||||
size_t static_memory_bound;
|
||||
size_t memory_guard_size;
|
||||
size_t memory_base_tls_offset;
|
||||
|
@ -214,12 +215,27 @@ enum class BD_SymbolicAddress : uint32_t {
|
|||
extern "C" {
|
||||
js::wasm::TypeCode env_unpack(BD_ValType type);
|
||||
|
||||
bool env_uses_shared_memory(const CraneliftModuleEnvironment* env);
|
||||
size_t env_num_tables(const CraneliftModuleEnvironment* env);
|
||||
size_t env_num_globals(const CraneliftModuleEnvironment* env);
|
||||
size_t env_num_types(const CraneliftModuleEnvironment* env);
|
||||
size_t env_num_funcs(const CraneliftModuleEnvironment* env);
|
||||
size_t env_num_elems(const CraneliftModuleEnvironment* env);
|
||||
size_t env_num_datas(const CraneliftModuleEnvironment* env);
|
||||
js::wasm::TypeCode env_elem_typecode(const CraneliftModuleEnvironment* env,
|
||||
uint32_t index);
|
||||
bool env_is_func_valid_for_ref(const CraneliftModuleEnvironment* env,
|
||||
uint32_t index);
|
||||
/// Returns the maximum memory size as an uint32, or UINT32_MAX if not defined.
|
||||
uint32_t env_max_memory(const CraneliftModuleEnvironment* env);
|
||||
|
||||
bool env_uses_shared_memory(const CraneliftModuleEnvironment* env);
|
||||
bool env_has_memory(const CraneliftModuleEnvironment* env);
|
||||
const js::wasm::FuncTypeWithId* env_type(const CraneliftModuleEnvironment* env,
|
||||
size_t typeIndex);
|
||||
const js::wasm::FuncTypeWithId* env_func_sig(
|
||||
const CraneliftModuleEnvironment* env, size_t funcIndex);
|
||||
size_t env_func_sig_index(const CraneliftModuleEnvironment* env,
|
||||
size_t funcIndex);
|
||||
size_t env_func_import_tls_offset(const CraneliftModuleEnvironment* env,
|
||||
size_t funcIndex);
|
||||
bool env_func_is_import(const CraneliftModuleEnvironment* env,
|
||||
|
@ -238,6 +254,10 @@ js::wasm::TypeCode global_type(const js::wasm::GlobalDesc*);
|
|||
size_t global_tlsOffset(const js::wasm::GlobalDesc*);
|
||||
|
||||
size_t table_tlsOffset(const js::wasm::TableDesc*);
|
||||
uint32_t table_initialLimit(const js::wasm::TableDesc*);
|
||||
// Returns the maximum limit as an uint32, or UINT32_MAX if not defined.
|
||||
uint32_t table_maximumLimit(const js::wasm::TableDesc*);
|
||||
js::wasm::TypeCode table_elementTypeCode(const js::wasm::TableDesc*);
|
||||
|
||||
size_t funcType_numArgs(const js::wasm::FuncTypeWithId*);
|
||||
const BD_ValType* funcType_args(const js::wasm::FuncTypeWithId*);
|
||||
|
|
|
@ -61,9 +61,17 @@ void cranelift_compiler_destroy(CraneliftCompiler* compiler);
|
|||
// The function described by `data` is compiled.
|
||||
//
|
||||
// Returns true on success.
|
||||
//
|
||||
// If this function returns false, an error message is returned in `*error`.
|
||||
// This string must be freed by `cranelift_compiler_free_error()` (it is on the
|
||||
// Rust heap so must not be freed by `free()` or similar).
|
||||
bool cranelift_compile_function(CraneliftCompiler* compiler,
|
||||
const CraneliftFuncCompileInput* data,
|
||||
CraneliftCompiledFunc* result);
|
||||
CraneliftCompiledFunc* result, char** error);
|
||||
|
||||
// Free an error string returned by `cranelift_compile_function()`.
|
||||
void cranelift_compiler_free_error(char* error);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // wasm_cranelift_clifapi_h
|
||||
|
|
|
@ -24,9 +24,8 @@ use cranelift_codegen::entity::EntityRef;
|
|||
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64};
|
||||
use cranelift_codegen::ir::{self, InstBuilder, SourceLoc};
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_wasm::{FuncIndex, GlobalIndex, SignatureIndex, TableIndex, WasmResult};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
use cranelift_wasm::{wasmparser, FuncIndex, GlobalIndex, SignatureIndex, TableIndex, WasmResult};
|
||||
|
||||
use crate::compile;
|
||||
use crate::utils::BasicError;
|
||||
|
@ -63,17 +62,10 @@ fn typecode_to_type(type_code: TypeCode) -> WasmResult<Option<ir::Type>> {
|
|||
|
||||
/// Convert a non-void `TypeCode` into the equivalent Cranelift type.
|
||||
#[inline]
|
||||
fn typecode_to_nonvoid_type(type_code: TypeCode) -> WasmResult<ir::Type> {
|
||||
pub(crate) fn typecode_to_nonvoid_type(type_code: TypeCode) -> WasmResult<ir::Type> {
|
||||
Ok(typecode_to_type(type_code)?.expect("unexpected void type"))
|
||||
}
|
||||
|
||||
/// Convert a `TypeCode` into the equivalent Cranelift type.
|
||||
#[inline]
|
||||
fn valtype_to_type(val_type: BD_ValType) -> WasmResult<ir::Type> {
|
||||
let type_code = unsafe { low_level::env_unpack(val_type) };
|
||||
typecode_to_nonvoid_type(type_code)
|
||||
}
|
||||
|
||||
/// Convert a u32 into a `BD_SymbolicAddress`.
|
||||
impl From<u32> for SymbolicAddress {
|
||||
fn from(x: u32) -> SymbolicAddress {
|
||||
|
@ -121,6 +113,10 @@ impl GlobalDesc {
|
|||
pub fn tls_offset(self) -> usize {
|
||||
unsafe { low_level::global_tlsOffset(self.0) }
|
||||
}
|
||||
|
||||
pub fn content_type(self) -> wasmparser::Type {
|
||||
typecode_to_parser_type(unsafe { low_level::global_type(self.0) })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -131,81 +127,156 @@ impl TableDesc {
|
|||
pub fn tls_offset(self) -> usize {
|
||||
unsafe { low_level::table_tlsOffset(self.0) }
|
||||
}
|
||||
|
||||
pub fn element_type(self) -> wasmparser::Type {
|
||||
typecode_to_parser_type(unsafe { low_level::table_elementTypeCode(self.0) })
|
||||
}
|
||||
|
||||
pub fn resizable_limits(self) -> wasmparser::ResizableLimits {
|
||||
let initial = unsafe { low_level::table_initialLimit(self.0) };
|
||||
let maximum = unsafe { low_level::table_initialLimit(self.0) };
|
||||
let maximum = if maximum == u32::max_value() {
|
||||
None
|
||||
} else {
|
||||
Some(maximum)
|
||||
};
|
||||
wasmparser::ResizableLimits { initial, maximum }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct FuncTypeWithId(*const low_level::FuncTypeWithId);
|
||||
#[derive(Clone)]
|
||||
pub struct FuncTypeWithId {
|
||||
ptr: *const low_level::FuncTypeWithId,
|
||||
args: Vec<TypeCode>,
|
||||
results: Vec<TypeCode>,
|
||||
}
|
||||
|
||||
impl FuncTypeWithId {
|
||||
pub fn args<'a>(self) -> WasmResult<SmallVec<[ir::Type; 4]>> {
|
||||
let num_args = unsafe { low_level::funcType_numArgs(self.0) };
|
||||
// The `funcType_args` callback crashes when there are no arguments. Also note that
|
||||
// `slice::from_raw_parts()` requires a non-null pointer for empty slices.
|
||||
// TODO: We should get all the parts of a signature in a single callback that returns a
|
||||
// struct.
|
||||
if num_args == 0 {
|
||||
Ok(SmallVec::new())
|
||||
} else {
|
||||
let args = unsafe { slice::from_raw_parts(low_level::funcType_args(self.0), num_args) };
|
||||
let mut ret = SmallVec::new();
|
||||
for &arg in args {
|
||||
ret.push(valtype_to_type(arg)?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
/// Creates a new FuncTypeWithId, caching all the values it requires.
|
||||
pub(crate) fn new(ptr: *const low_level::FuncTypeWithId) -> Self {
|
||||
let num_args = unsafe { low_level::funcType_numArgs(ptr) };
|
||||
let args = unsafe { slice::from_raw_parts(low_level::funcType_args(ptr), num_args) };
|
||||
let args = args
|
||||
.iter()
|
||||
.map(|val_type| unsafe { low_level::env_unpack(*val_type) })
|
||||
.collect();
|
||||
|
||||
let num_results = unsafe { low_level::funcType_numResults(ptr) };
|
||||
let results =
|
||||
unsafe { slice::from_raw_parts(low_level::funcType_results(ptr), num_results) };
|
||||
let results = results
|
||||
.iter()
|
||||
.map(|val_type| unsafe { low_level::env_unpack(*val_type) })
|
||||
.collect();
|
||||
|
||||
Self { ptr, args, results }
|
||||
}
|
||||
|
||||
pub fn results<'a>(self) -> WasmResult<Vec<ir::Type>> {
|
||||
let num_results = unsafe { low_level::funcType_numResults(self.0) };
|
||||
// The same comments as FuncTypeWithId::args apply here.
|
||||
if num_results == 0 {
|
||||
Ok(Vec::new())
|
||||
} else {
|
||||
let results =
|
||||
unsafe { slice::from_raw_parts(low_level::funcType_results(self.0), num_results) };
|
||||
let mut ret = Vec::new();
|
||||
for &result in results {
|
||||
ret.push(valtype_to_type(result)?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
pub(crate) fn id_kind(&self) -> FuncTypeIdDescKind {
|
||||
unsafe { low_level::funcType_idKind(self.ptr) }
|
||||
}
|
||||
|
||||
pub fn id_kind(self) -> FuncTypeIdDescKind {
|
||||
unsafe { low_level::funcType_idKind(self.0) }
|
||||
pub(crate) fn id_immediate(&self) -> usize {
|
||||
unsafe { low_level::funcType_idImmediate(self.ptr) }
|
||||
}
|
||||
|
||||
pub fn id_immediate(self) -> usize {
|
||||
unsafe { low_level::funcType_idImmediate(self.0) }
|
||||
pub(crate) fn id_tls_offset(&self) -> usize {
|
||||
unsafe { low_level::funcType_idTlsOffset(self.ptr) }
|
||||
}
|
||||
pub(crate) fn args(&self) -> &[TypeCode] {
|
||||
&self.args
|
||||
}
|
||||
pub(crate) fn results(&self) -> &[TypeCode] {
|
||||
&self.results
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id_tls_offset(self) -> usize {
|
||||
unsafe { low_level::funcType_idTlsOffset(self.0) }
|
||||
fn typecode_to_parser_type(ty: TypeCode) -> wasmparser::Type {
|
||||
match ty {
|
||||
TypeCode::I32 => wasmparser::Type::I32,
|
||||
TypeCode::I64 => wasmparser::Type::I64,
|
||||
TypeCode::F32 => wasmparser::Type::F32,
|
||||
TypeCode::F64 => wasmparser::Type::F64,
|
||||
TypeCode::FuncRef => wasmparser::Type::FuncRef,
|
||||
TypeCode::ExternRef => wasmparser::Type::ExternRef,
|
||||
TypeCode::BlockVoid => wasmparser::Type::EmptyBlockType,
|
||||
_ => panic!("unknown type code: {:?}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
impl wasmparser::WasmFuncType for FuncTypeWithId {
|
||||
fn len_inputs(&self) -> usize {
|
||||
self.args.len()
|
||||
}
|
||||
fn len_outputs(&self) -> usize {
|
||||
self.results.len()
|
||||
}
|
||||
fn input_at(&self, at: u32) -> Option<wasmparser::Type> {
|
||||
self.args
|
||||
.get(at as usize)
|
||||
.map(|ty| typecode_to_parser_type(*ty))
|
||||
}
|
||||
fn output_at(&self, at: u32) -> Option<wasmparser::Type> {
|
||||
self.results
|
||||
.get(at as usize)
|
||||
.map(|ty| typecode_to_parser_type(*ty))
|
||||
}
|
||||
}
|
||||
|
||||
/// Thin wrapper for the CraneliftModuleEnvironment structure.
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ModuleEnvironment<'a> {
|
||||
env: &'a CraneliftModuleEnvironment,
|
||||
/// The `WasmModuleResources` trait requires us to return a borrow to a `FuncTypeWithId`, so we
|
||||
/// eagerly construct these.
|
||||
types: Vec<FuncTypeWithId>,
|
||||
/// Similar to `types`, we need to have a persistently-stored `FuncTypeWithId` to return. The
|
||||
/// types in `func_sigs` are a subset of those in `types`, but we don't want to have to
|
||||
/// maintain an index from function to signature ID, so we store these directly.
|
||||
func_sigs: Vec<FuncTypeWithId>,
|
||||
}
|
||||
|
||||
impl<'a> ModuleEnvironment<'a> {
|
||||
pub fn new(env: &'a CraneliftModuleEnvironment) -> Self {
|
||||
Self { env }
|
||||
pub(crate) fn new(env: &'a CraneliftModuleEnvironment) -> Self {
|
||||
let num_types = unsafe { low_level::env_num_types(env) };
|
||||
let mut types = Vec::with_capacity(num_types);
|
||||
for i in 0..num_types {
|
||||
let t = FuncTypeWithId::new(unsafe { low_level::env_signature(env, i) });
|
||||
types.push(t);
|
||||
}
|
||||
let num_func_sigs = unsafe { low_level::env_num_funcs(env) };
|
||||
let mut func_sigs = Vec::with_capacity(num_func_sigs);
|
||||
for i in 0..num_func_sigs {
|
||||
let t = FuncTypeWithId::new(unsafe { low_level::env_func_sig(env, i) });
|
||||
func_sigs.push(t);
|
||||
}
|
||||
Self {
|
||||
env,
|
||||
types,
|
||||
func_sigs,
|
||||
}
|
||||
}
|
||||
pub fn has_memory(&self) -> bool {
|
||||
unsafe { low_level::env_has_memory(self.env) }
|
||||
}
|
||||
pub fn uses_shared_memory(&self) -> bool {
|
||||
unsafe { low_level::env_uses_shared_memory(self.env) }
|
||||
}
|
||||
pub fn num_tables(&self) -> usize {
|
||||
unsafe { low_level::env_num_tables(self.env) }
|
||||
}
|
||||
pub fn num_types(&self) -> usize {
|
||||
unsafe { low_level::env_num_types(self.env) }
|
||||
self.types.len()
|
||||
}
|
||||
pub fn type_(&self, index: usize) -> FuncTypeWithId {
|
||||
FuncTypeWithId(unsafe { low_level::env_type(self.env, index) })
|
||||
self.types[index].clone()
|
||||
}
|
||||
pub fn num_func_sigs(&self) -> usize {
|
||||
self.func_sigs.len()
|
||||
}
|
||||
pub fn func_sig(&self, func_index: FuncIndex) -> FuncTypeWithId {
|
||||
FuncTypeWithId(unsafe { low_level::env_func_sig(self.env, func_index.index()) })
|
||||
self.func_sigs[func_index.index()].clone()
|
||||
}
|
||||
pub fn func_sig_index(&self, func_index: FuncIndex) -> SignatureIndex {
|
||||
SignatureIndex::new(unsafe { low_level::env_func_sig_index(self.env, func_index.index()) })
|
||||
}
|
||||
pub fn func_import_tls_offset(&self, func_index: FuncIndex) -> usize {
|
||||
unsafe { low_level::env_func_import_tls_offset(self.env, func_index.index()) }
|
||||
|
@ -214,7 +285,7 @@ impl<'a> ModuleEnvironment<'a> {
|
|||
unsafe { low_level::env_func_is_import(self.env, func_index.index()) }
|
||||
}
|
||||
pub fn signature(&self, sig_index: SignatureIndex) -> FuncTypeWithId {
|
||||
FuncTypeWithId(unsafe { low_level::env_signature(self.env, sig_index.index()) })
|
||||
FuncTypeWithId::new(unsafe { low_level::env_signature(self.env, sig_index.index()) })
|
||||
}
|
||||
pub fn table(&self, table_index: TableIndex) -> TableDesc {
|
||||
TableDesc(unsafe { low_level::env_table(self.env, table_index.index()) })
|
||||
|
@ -222,8 +293,97 @@ impl<'a> ModuleEnvironment<'a> {
|
|||
pub fn global(&self, global_index: GlobalIndex) -> GlobalDesc {
|
||||
GlobalDesc(unsafe { low_level::env_global(self.env, global_index.index()) })
|
||||
}
|
||||
pub fn min_memory_length(&self) -> i64 {
|
||||
i64::from(self.env.min_memory_length)
|
||||
pub fn min_memory_length(&self) -> u32 {
|
||||
self.env.min_memory_length
|
||||
}
|
||||
pub fn max_memory_length(&self) -> Option<u32> {
|
||||
let max = unsafe { low_level::env_max_memory(self.env) };
|
||||
if max == u32::max_value() {
|
||||
None
|
||||
} else {
|
||||
Some(max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'module> wasmparser::WasmModuleResources for ModuleEnvironment<'module> {
|
||||
type FuncType = FuncTypeWithId;
|
||||
fn table_at(&self, at: u32) -> Option<wasmparser::TableType> {
|
||||
if (at as usize) < self.num_tables() {
|
||||
let desc = TableDesc(unsafe { low_level::env_table(self.env, at as usize) });
|
||||
let element_type = desc.element_type();
|
||||
let limits = desc.resizable_limits();
|
||||
Some(wasmparser::TableType {
|
||||
element_type,
|
||||
limits,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn memory_at(&self, at: u32) -> Option<wasmparser::MemoryType> {
|
||||
if at == 0 {
|
||||
let has_memory = self.has_memory();
|
||||
if has_memory {
|
||||
let shared = self.uses_shared_memory();
|
||||
let initial = self.min_memory_length() as u32;
|
||||
let maximum = self.max_memory_length();
|
||||
Some(wasmparser::MemoryType::M32 {
|
||||
limits: wasmparser::ResizableLimits { initial, maximum },
|
||||
shared,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn global_at(&self, at: u32) -> Option<wasmparser::GlobalType> {
|
||||
let num_globals = unsafe { low_level::env_num_globals(self.env) };
|
||||
if (at as usize) < num_globals {
|
||||
let desc = self.global(GlobalIndex::new(at as usize));
|
||||
let mutable = !desc.is_constant();
|
||||
let content_type = desc.content_type();
|
||||
Some(wasmparser::GlobalType {
|
||||
mutable,
|
||||
content_type,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn func_type_at(&self, type_idx: u32) -> Option<&Self::FuncType> {
|
||||
if (type_idx as usize) < self.types.len() {
|
||||
Some(&self.types[type_idx as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn type_of_function(&self, func_idx: u32) -> Option<&Self::FuncType> {
|
||||
if (func_idx as usize) < self.func_sigs.len() {
|
||||
Some(&self.func_sigs[func_idx as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn element_type_at(&self, at: u32) -> Option<wasmparser::Type> {
|
||||
let num_elems = self.element_count();
|
||||
if at < num_elems {
|
||||
let elem_type = unsafe { low_level::env_elem_typecode(self.env, at) };
|
||||
Some(typecode_to_parser_type(elem_type))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn element_count(&self) -> u32 {
|
||||
unsafe { low_level::env_num_elems(self.env) as u32 }
|
||||
}
|
||||
fn data_count(&self) -> u32 {
|
||||
unsafe { low_level::env_num_datas(self.env) as u32 }
|
||||
}
|
||||
fn is_function_referenced(&self, idx: u32) -> bool {
|
||||
unsafe { low_level::env_is_func_valid_for_ref(self.env, idx) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
use log::{debug, info};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
use cranelift_codegen::binemit::{
|
||||
Addend, CodeInfo, CodeOffset, NullStackMapSink, Reloc, RelocSink, TrapSink,
|
||||
|
@ -28,13 +29,14 @@ use cranelift_codegen::binemit::{
|
|||
use cranelift_codegen::entity::EntityRef;
|
||||
use cranelift_codegen::ir::{
|
||||
self, constant::ConstantOffset, stackslot::StackSize, ExternalName, JumpTable, SourceLoc,
|
||||
TrapCode, Type,
|
||||
TrapCode,
|
||||
};
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_codegen::machinst::MachStackMap;
|
||||
use cranelift_codegen::CodegenResult;
|
||||
use cranelift_codegen::Context;
|
||||
use cranelift_wasm::{FuncIndex, FuncTranslator, ModuleTranslationState, WasmResult};
|
||||
use cranelift_wasm::wasmparser::{FuncValidator, WasmFeatures};
|
||||
use cranelift_wasm::{FuncIndex, FuncTranslator, WasmResult};
|
||||
|
||||
use crate::bindings;
|
||||
use crate::isa::make_isa;
|
||||
|
@ -91,10 +93,9 @@ impl CompiledFunc {
|
|||
/// compilations.
|
||||
pub struct BatchCompiler<'static_env, 'module_env> {
|
||||
// Attributes that are constant accross multiple compilations.
|
||||
static_environ: &'static_env bindings::StaticEnvironment,
|
||||
static_env: &'static_env bindings::StaticEnvironment,
|
||||
|
||||
environ: bindings::ModuleEnvironment<'module_env>,
|
||||
module_state: ModuleTranslationState,
|
||||
module_env: Rc<bindings::ModuleEnvironment<'module_env>>,
|
||||
|
||||
isa: Box<dyn TargetIsa>,
|
||||
|
||||
|
@ -117,17 +118,17 @@ pub struct BatchCompiler<'static_env, 'module_env> {
|
|||
|
||||
impl<'static_env, 'module_env> BatchCompiler<'static_env, 'module_env> {
|
||||
pub fn new(
|
||||
static_environ: &'static_env bindings::StaticEnvironment,
|
||||
environ: bindings::ModuleEnvironment<'module_env>,
|
||||
static_env: &'static_env bindings::StaticEnvironment,
|
||||
module_env: bindings::ModuleEnvironment<'module_env>,
|
||||
) -> DashResult<Self> {
|
||||
let isa = make_isa(static_environ)?;
|
||||
let trans_env = TransEnv::new(&*isa, environ, static_environ);
|
||||
let isa = make_isa(static_env)?;
|
||||
let module_env = Rc::new(module_env);
|
||||
let trans_env = TransEnv::new(&*isa, module_env.clone(), static_env);
|
||||
Ok(BatchCompiler {
|
||||
static_environ,
|
||||
environ,
|
||||
static_env,
|
||||
module_env,
|
||||
isa,
|
||||
func_translator: FuncTranslator::new(),
|
||||
module_state: create_module_translation_state(&environ)?,
|
||||
context: Context::new(),
|
||||
trap_relocs: Traps::new(),
|
||||
trans_env,
|
||||
|
@ -156,11 +157,27 @@ impl<'static_env, 'module_env> BatchCompiler<'static_env, 'module_env> {
|
|||
let index = FuncIndex::new(func.index as usize);
|
||||
|
||||
self.context.func.signature =
|
||||
init_sig(&self.environ, self.static_environ.call_conv(), index)?;
|
||||
init_sig(&*self.module_env, self.static_env.call_conv(), index)?;
|
||||
self.context.func.name = wasm_function_name(index);
|
||||
|
||||
let features = WasmFeatures {
|
||||
reference_types: self.static_env.ref_types_enabled,
|
||||
module_linking: false,
|
||||
simd: false, // TODO
|
||||
multi_value: true,
|
||||
threads: self.static_env.threads_enabled,
|
||||
tail_call: false,
|
||||
bulk_memory: true,
|
||||
deterministic_only: true,
|
||||
memory64: false,
|
||||
multi_memory: false,
|
||||
};
|
||||
let sig_index = self.module_env.func_sig_index(index);
|
||||
let mut validator =
|
||||
FuncValidator::new(sig_index.index() as u32, 0, &*self.module_env, &features)?;
|
||||
|
||||
self.func_translator.translate(
|
||||
&self.module_state,
|
||||
&mut validator,
|
||||
func.bytecode(),
|
||||
func.offset_in_module as usize,
|
||||
&mut self.context.func,
|
||||
|
@ -222,7 +239,7 @@ impl<'static_env, 'module_env> BatchCompiler<'static_env, 'module_env> {
|
|||
.append(&mut self.trap_relocs.metadata);
|
||||
}
|
||||
|
||||
if self.static_environ.ref_types_enabled {
|
||||
if self.static_env.ref_types_enabled {
|
||||
self.emit_stackmaps(stackmaps);
|
||||
}
|
||||
|
||||
|
@ -287,27 +304,6 @@ impl<'static_env, 'module_env> fmt::Display for BatchCompiler<'static_env, 'modu
|
|||
}
|
||||
}
|
||||
|
||||
fn create_module_translation_state(
|
||||
env: &bindings::ModuleEnvironment,
|
||||
) -> WasmResult<ModuleTranslationState> {
|
||||
let num_sig = env.num_types();
|
||||
|
||||
let mut arg_vecs = vec![];
|
||||
let mut result_vecs = vec![];
|
||||
for i in 0..num_sig {
|
||||
let sig = env.type_(i);
|
||||
arg_vecs.push(sig.args()?);
|
||||
result_vecs.push(sig.results()?);
|
||||
}
|
||||
let types: Vec<(&[Type], &[Type])> = arg_vecs
|
||||
.iter()
|
||||
.zip(result_vecs.iter())
|
||||
.map(|(args, results)| (&args[..], &results[..]))
|
||||
.collect();
|
||||
|
||||
ModuleTranslationState::from_func_sigs(&types[..])
|
||||
}
|
||||
|
||||
/// Create a Cranelift function name representing a WebAssembly function with `index`.
|
||||
pub fn wasm_function_name(func: FuncIndex) -> ExternalName {
|
||||
ExternalName::User {
|
||||
|
|
|
@ -136,7 +136,10 @@ mod utils;
|
|||
mod wasm2clif;
|
||||
|
||||
use log::{self, error, info};
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::bindings::{CompiledFunc, FuncCompileInput, ModuleEnvironment, StaticEnvironment};
|
||||
use crate::compile::BatchCompiler;
|
||||
|
@ -197,14 +200,26 @@ pub unsafe extern "C" fn cranelift_compiler_destroy(compiler: *mut BatchCompiler
|
|||
let _box = Box::from_raw(compiler);
|
||||
}
|
||||
|
||||
fn error_to_cstring<D: Display>(err: D) -> *mut c_char {
|
||||
use std::fmt::Write;
|
||||
let mut s = String::new();
|
||||
write!(&mut s, "{}", err);
|
||||
let cstr = CString::new(s).unwrap();
|
||||
cstr.into_raw()
|
||||
}
|
||||
|
||||
/// Compile a single function.
|
||||
///
|
||||
/// This is declared in `clifapi.h`.
|
||||
///
|
||||
/// If a Wasm validation error is returned in *error, then it *must* be later
|
||||
/// freed by `cranelift_compiler_free_error()`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn cranelift_compile_function(
|
||||
compiler: *mut BatchCompiler,
|
||||
data: *const FuncCompileInput,
|
||||
result: *mut CompiledFunc,
|
||||
error: *mut *mut c_char,
|
||||
) -> bool {
|
||||
let compiler = compiler.as_mut().unwrap();
|
||||
let data = data.as_ref().unwrap();
|
||||
|
@ -213,8 +228,8 @@ pub unsafe extern "C" fn cranelift_compile_function(
|
|||
compiler.clear();
|
||||
|
||||
if let Err(e) = compiler.translate_wasm(data) {
|
||||
error!("Wasm translation error: {}\n", e);
|
||||
info!("Translated function: {}", compiler);
|
||||
let cstr = error_to_cstring(e);
|
||||
unsafe { *error = cstr; }
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -229,8 +244,8 @@ pub unsafe extern "C" fn cranelift_compile_function(
|
|||
CodegenError::ImplLimitExceeded
|
||||
| CodegenError::CodeTooLarge
|
||||
| CodegenError::Unsupported(_) => {
|
||||
error!("Cranelift compilation error: {}\n", e);
|
||||
info!("Compiled function: {}", compiler);
|
||||
let cstr = error_to_cstring(e);
|
||||
unsafe { *error = cstr; }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -244,6 +259,12 @@ pub unsafe extern "C" fn cranelift_compile_function(
|
|||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn cranelift_compiler_free_error(s: *mut c_char) {
|
||||
// Convert back into a `CString` and then let it drop.
|
||||
let _cstr = unsafe { CString::from_raw(s) };
|
||||
}
|
||||
|
||||
/// Returns true whether a platform (target ISA) is supported or not.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn cranelift_supports_platform() -> bool {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
//! internal data structures.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use cranelift_codegen::cursor::{Cursor, FuncCursor};
|
||||
use cranelift_codegen::entity::{EntityRef, PrimaryMap, SecondaryMap};
|
||||
|
@ -36,6 +37,7 @@ use cranelift_wasm::{
|
|||
use crate::bindings::{self, GlobalDesc, SymbolicAddress};
|
||||
use crate::compile::{symbolic_function_name, wasm_function_name};
|
||||
use crate::isa::{platform::USES_HEAP_REG, POINTER_SIZE};
|
||||
use bindings::typecode_to_nonvoid_type;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub const POINTER_TYPE: ir::Type = ir::types::I64;
|
||||
|
@ -71,27 +73,29 @@ fn imm64(offset: usize) -> ir::immediates::Imm64 {
|
|||
/// a callee.
|
||||
fn init_sig_from_wsig(
|
||||
call_conv: CallConv,
|
||||
wsig: bindings::FuncTypeWithId,
|
||||
wsig: &bindings::FuncTypeWithId,
|
||||
) -> WasmResult<ir::Signature> {
|
||||
let mut sig = ir::Signature::new(call_conv);
|
||||
|
||||
for arg_type in wsig.args()? {
|
||||
let arg = match arg_type {
|
||||
for arg_type in wsig.args() {
|
||||
let ty = typecode_to_nonvoid_type(*arg_type)?;
|
||||
let arg = match ty {
|
||||
// SpiderMonkey requires i32 arguments to callees (e.g., from Wasm
|
||||
// back into JS or native code) to have their high 32 bits zero so
|
||||
// that it can directly box them.
|
||||
ir::types::I32 => ir::AbiParam::new(arg_type).uext(),
|
||||
_ => ir::AbiParam::new(arg_type),
|
||||
ir::types::I32 => ir::AbiParam::new(ty).uext(),
|
||||
_ => ir::AbiParam::new(ty),
|
||||
};
|
||||
sig.params.push(arg);
|
||||
}
|
||||
|
||||
for ret_type in wsig.results()? {
|
||||
let ret = match ret_type {
|
||||
for ret_type in wsig.results() {
|
||||
let ty = typecode_to_nonvoid_type(*ret_type)?;
|
||||
let ret = match ty {
|
||||
// SpiderMonkey requires i32 returns to have their high 32 bits
|
||||
// zero so that it can directly box them.
|
||||
ir::types::I32 => ir::AbiParam::new(ret_type).uext(),
|
||||
_ => ir::AbiParam::new(ret_type),
|
||||
ir::types::I32 => ir::AbiParam::new(ty).uext(),
|
||||
_ => ir::AbiParam::new(ty),
|
||||
};
|
||||
sig.returns.push(ret);
|
||||
}
|
||||
|
@ -113,7 +117,7 @@ pub fn init_sig(
|
|||
func_index: FuncIndex,
|
||||
) -> WasmResult<ir::Signature> {
|
||||
let wsig = env.func_sig(func_index);
|
||||
init_sig_from_wsig(call_conv, wsig)
|
||||
init_sig_from_wsig(call_conv, &wsig)
|
||||
}
|
||||
|
||||
/// An instance call may return a special value to indicate that the operation
|
||||
|
@ -299,13 +303,13 @@ pub const TRAP_THROW_REPORTED: u16 = 1;
|
|||
/// A translation context that implements `FuncEnvironment` for the specific Spidermonkey
|
||||
/// translation bits.
|
||||
pub struct TransEnv<'static_env, 'module_env> {
|
||||
env: bindings::ModuleEnvironment<'module_env>,
|
||||
static_env: &'static_env bindings::StaticEnvironment,
|
||||
module_env: Rc<bindings::ModuleEnvironment<'module_env>>,
|
||||
|
||||
target_frontend_config: TargetFrontendConfig,
|
||||
|
||||
/// Information about the function pointer tables `self.env` knowns about. Indexed by table
|
||||
/// index.
|
||||
/// Information about the function pointer tables `self.module_env` knowns about. Indexed by
|
||||
/// table index.
|
||||
tables: PrimaryMap<TableIndex, TableInfo>,
|
||||
|
||||
/// For those signatures whose ID is stored in a global, keep track of the globals we have
|
||||
|
@ -349,12 +353,12 @@ pub struct TransEnv<'static_env, 'module_env> {
|
|||
impl<'static_env, 'module_env> TransEnv<'static_env, 'module_env> {
|
||||
pub fn new(
|
||||
isa: &dyn TargetIsa,
|
||||
env: bindings::ModuleEnvironment<'module_env>,
|
||||
module_env: Rc<bindings::ModuleEnvironment<'module_env>>,
|
||||
static_env: &'static_env bindings::StaticEnvironment,
|
||||
) -> Self {
|
||||
TransEnv {
|
||||
env,
|
||||
static_env,
|
||||
module_env,
|
||||
target_frontend_config: isa.frontend_config(),
|
||||
tables: PrimaryMap::new(),
|
||||
signatures: HashMap::new(),
|
||||
|
@ -401,7 +405,7 @@ impl<'static_env, 'module_env> TransEnv<'static_env, 'module_env> {
|
|||
// Allocate all tables up to the requested index.
|
||||
let vmctx = self.get_vmctx_gv(func);
|
||||
while self.tables.len() <= table.index() {
|
||||
let wtab = self.env.table(TableIndex::new(self.tables.len()));
|
||||
let wtab = self.module_env.table(TableIndex::new(self.tables.len()));
|
||||
self.tables.push(TableInfo::new(wtab, func, vmctx));
|
||||
}
|
||||
self.tables[table].clone()
|
||||
|
@ -429,7 +433,7 @@ impl<'static_env, 'module_env> TransEnv<'static_env, 'module_env> {
|
|||
let vmctx = self.get_vmctx_gv(func);
|
||||
let gv = func.create_global_value(ir::GlobalValueData::IAddImm {
|
||||
base: vmctx,
|
||||
offset: imm64(self.env.func_import_tls_offset(index)),
|
||||
offset: imm64(self.module_env.func_import_tls_offset(index)),
|
||||
global_type: POINTER_TYPE,
|
||||
});
|
||||
// Save it for next time.
|
||||
|
@ -724,7 +728,7 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
func: &mut ir::Function,
|
||||
index: GlobalIndex,
|
||||
) -> WasmResult<GlobalVariable> {
|
||||
let global = self.env.global(index);
|
||||
let global = self.module_env.global(index);
|
||||
if global.is_constant() {
|
||||
// Constant globals have a known value at compile time. We insert an instruction to
|
||||
// materialize the constant at the front of the entry block.
|
||||
|
@ -787,7 +791,7 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
ir::HeapStyle::Dynamic { bound_gv }
|
||||
};
|
||||
|
||||
let min_size = (self.env.min_memory_length() as u64).into();
|
||||
let min_size = (self.module_env.min_memory_length() as u64).into();
|
||||
let offset_guard_size = (self.static_env.memory_guard_size as u64).into();
|
||||
|
||||
Ok(func.create_heap(ir::HeapData {
|
||||
|
@ -804,8 +808,8 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
func: &mut ir::Function,
|
||||
index: SignatureIndex,
|
||||
) -> WasmResult<ir::SigRef> {
|
||||
let wsig = self.env.signature(index);
|
||||
let mut sigdata = init_sig_from_wsig(self.static_env.call_conv(), wsig)?;
|
||||
let wsig = self.module_env.signature(index);
|
||||
let mut sigdata = init_sig_from_wsig(self.static_env.call_conv(), &wsig)?;
|
||||
|
||||
if wsig.id_kind() != bindings::FuncTypeIdDescKind::None {
|
||||
// A signature to be used for an indirect call also takes a signature id.
|
||||
|
@ -852,7 +856,7 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
index: FuncIndex,
|
||||
) -> WasmResult<ir::FuncRef> {
|
||||
// Create a signature.
|
||||
let sigdata = init_sig(&self.env, self.static_env.call_conv(), index)?;
|
||||
let sigdata = init_sig(&*self.module_env, self.static_env.call_conv(), index)?;
|
||||
let signature = func.import_signature(sigdata);
|
||||
|
||||
Ok(func.import_function(ir::ExtFuncData {
|
||||
|
@ -872,7 +876,7 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
callee: ir::Value,
|
||||
call_args: &[ir::Value],
|
||||
) -> WasmResult<ir::Inst> {
|
||||
let wsig = self.env.signature(sig_index);
|
||||
let wsig = self.module_env.signature(sig_index);
|
||||
|
||||
let wtable = self.get_table(pos.func, table_index);
|
||||
|
||||
|
@ -973,7 +977,7 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
|
||||
|
||||
// Is this an imported function in a different instance, or a local function?
|
||||
if self.env.func_is_import(callee_index) {
|
||||
if self.module_env.func_is_import(callee_index) {
|
||||
// This is a call to an imported function. We need to load the callee address and vmctx
|
||||
// from the associated `FuncImportTls` struct in a global.
|
||||
let gv = self.func_import_global(pos.func, callee_index);
|
||||
|
@ -1063,7 +1067,7 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
|
||||
// We have a specialized version of `memory.copy` when we are using
|
||||
// shared memory or not.
|
||||
let ret = if self.env.uses_shared_memory() {
|
||||
let ret = if self.module_env.uses_shared_memory() {
|
||||
self.instance_call(&mut pos, &FN_MEMORY_COPY_SHARED, &[dst, src, len, mem_base])
|
||||
} else {
|
||||
self.instance_call(&mut pos, &FN_MEMORY_COPY, &[dst, src, len, mem_base])
|
||||
|
@ -1086,7 +1090,7 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
|
||||
// We have a specialized version of `memory.fill` when we are using
|
||||
// shared memory or not.
|
||||
let ret = if self.env.uses_shared_memory() {
|
||||
let ret = if self.module_env.uses_shared_memory() {
|
||||
self.instance_call(&mut pos, &FN_MEMORY_FILL_SHARED, &[dst, val, len, mem_base])
|
||||
} else {
|
||||
self.instance_call(&mut pos, &FN_MEMORY_FILL, &[dst, val, len, mem_base])
|
||||
|
@ -1258,7 +1262,7 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
mut pos: FuncCursor,
|
||||
global_index: GlobalIndex,
|
||||
) -> WasmResult<ir::Value> {
|
||||
let global = self.env.global(global_index);
|
||||
let global = self.module_env.global(global_index);
|
||||
let ty = global.value_type()?;
|
||||
debug_assert!(ty == ir::types::R32 || ty == ir::types::R64);
|
||||
|
||||
|
@ -1274,7 +1278,7 @@ impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module
|
|||
global_index: GlobalIndex,
|
||||
val: ir::Value,
|
||||
) -> WasmResult<()> {
|
||||
let global = self.env.global(global_index);
|
||||
let global = self.module_env.global(global_index);
|
||||
let ty = global.value_type()?;
|
||||
debug_assert!(ty == ir::types::R32 || ty == ir::types::R64);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче