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:
Chris Fallin 2020-10-07 03:44:45 +00:00
Родитель b1ad6c43eb
Коммит abaa8e4d14
27 изменённых файлов: 529 добавлений и 251 удалений

Просмотреть файл

@ -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);