Skip to content

Commit

Permalink
[WASM] Block with v128 results should notify SIMD uses
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=271494
rdar://125146449

Reviewed by Justin Michaud.

We should notify the SIMD use when wasm block within the code has v128 results.

* JSTests/wasm/stress/try-and-block-with-v128-results.js: Added.
(globalThis.callerIsBBQOrOMGCompiled.instantiateJsc):
(else.instantiateBrowser):
(async let):
* JSTests/wasm/stress/try-and-block-with-v128-results.wasm: Added.

Canonical link: https://commits.webkit.org/272448.873@safari-7618-branch
  • Loading branch information
hyjorc1 committed Apr 5, 2024
1 parent 854912a commit e6123d8
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 5 deletions.
208 changes: 208 additions & 0 deletions JSTests/wasm/stress/try-and-block-with-v128-results.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
let tools;
if (globalThis.callerIsBBQOrOMGCompiled) {
function instantiateJsc(filename, importObject) {
let bytes = read(filename, 'binary');
return WebAssembly.instantiate(bytes, importObject, 'x');
}
const log = debug;
const report = $.agent.report;
const isJIT = callerIsBBQOrOMGCompiled;
tools = {log, report, isJIT, instantiate: instantiateJsc};
} else {
function instantiateBrowser(filename, importObject) {
let bytes = fetch(filename);
return WebAssembly.instantiateStreaming(bytes, importObject, 'x');
}
const log = console.log;
const report = console.log;
const isJIT = () => 1;
tools = {log, report, isJIT, instantiate: instantiateBrowser};
}
const {log, report, isJIT, instantiate} = tools;
const extra = {isJIT};
(async function () {
let memory0 = new WebAssembly.Memory({initial: 2890, shared: true, maximum: 4668});
/**
@returns {void}
*/
let fn0 = function () {
};
/**
@returns {void}
*/
let fn1 = function () {
};
/**
@returns {void}
*/
let fn2 = function () {
};
/**
@param {F32} a0
@param {FuncRef} a1
@returns {[F32, FuncRef]}
*/
let fn3 = function (a0, a1) {
a0?.toString(); a1?.toString();
return [2.0643424836123336e5, a1];
};
/**
@returns {I32}
*/
let fn4 = function () {

return 46;
};
/**
@param {F32} a0
@param {FuncRef} a1
@returns {void}
*/
let fn5 = function (a0, a1) {
a0?.toString(); a1?.toString();
};
/**
@param {F32} a0
@param {FuncRef} a1
@returns {void}
*/
let fn6 = function (a0, a1) {
a0?.toString(); a1?.toString();
};
/**
@param {I64} a0
@returns {I64}
*/
let fn7 = function (a0) {
a0?.toString();
return 1777n;
};
let tag5 = new WebAssembly.Tag({parameters: []});
let tag7 = new WebAssembly.Tag({parameters: ['i64']});
let tag8 = new WebAssembly.Tag({parameters: ['f32', 'anyfunc']});
let global0 = new WebAssembly.Global({value: 'i32', mutable: true}, 3511634618);
let global2 = new WebAssembly.Global({value: 'f32', mutable: true}, 839610.408920414);
let global4 = new WebAssembly.Global({value: 'f64', mutable: true}, 777737.2126211214);
let global5 = new WebAssembly.Global({value: 'anyfunc', mutable: true}, null);
let global6 = new WebAssembly.Global({value: 'f32', mutable: true}, 417311.1223992667);
let table0 = new WebAssembly.Table({initial: 3, element: 'anyfunc'});
let m0 = {fn5, global0, global3: global0, memory0, tag5, tag10: tag5};
let m2 = {fn0, fn4, fn6, fn7, global2, global4, global5, global6, tag6: tag5, tag7, tag9: tag5};
let m1 = {fn1, fn2, fn3, global1: global0, table0, tag8, tag11: tag5};
let importObject0 = /** @type {Imports2} */ ({extra, m0, m1, m2});
let i0 = await instantiate('try-and-block-with-v128-results.wasm', importObject0);
let {fn8, fn9, fn10, global7, global8, memory1, table1, tag0, tag1, tag2, tag3, tag4} = /**
@type {{
fn8: (a0: I64) => I64,
fn9: (a0: F32, a1: FuncRef) => void,
fn10: (a0: I64) => void,
global7: WebAssembly.Global,
global8: WebAssembly.Global,
memory1: WebAssembly.Memory,
table1: WebAssembly.Table,
tag0: WebAssembly.Tag,
tag1: WebAssembly.Tag,
tag2: WebAssembly.Tag,
tag3: WebAssembly.Tag,
tag4: WebAssembly.Tag
}} */ (i0.instance.exports);
global7.value = 0n;
global2.value = 0;
global4.value = 0;
global8.value = 'a';
// log('calling fn10');
report('progress');
try {
for (let k=0; k<26; k++) {
let zzz = fn10(global7.value);
if (zzz !== undefined) { throw new Error('expected undefined but return value is '+zzz); }
}
} catch (e) {
if (e instanceof WebAssembly.Exception) {
log(e); if (e.stack) { log(e.stack); }
} else if (e instanceof TypeError) {
if (e.message === 'an exported wasm function cannot contain a v128 parameter or return value') { log(e); } else { throw e; }
} else if (e instanceof WebAssembly.RuntimeError || e instanceof RangeError) { log(e); } else { throw e; }
}
// log('calling fn8');
report('progress');
try {
for (let k=0; k<16; k++) {
let zzz = fn8(global7.value);
zzz?.toString();
}
} catch (e) {
if (e instanceof WebAssembly.Exception) {
log(e); if (e.stack) { log(e.stack); }
} else if (e instanceof TypeError) {
if (e.message === 'an exported wasm function cannot contain a v128 parameter or return value') { log(e); } else { throw e; }
} else if (e instanceof WebAssembly.RuntimeError || e instanceof RangeError) { log(e); } else { throw e; }
}
// log('calling fn9');
report('progress');
try {
for (let k=0; k<24; k++) {
let zzz = fn9(global6.value, fn8);
if (zzz !== undefined) { throw new Error('expected undefined but return value is '+zzz); }
}
} catch (e) {
if (e instanceof WebAssembly.Exception) {
log(e); if (e.stack) { log(e.stack); }
} else if (e instanceof TypeError) {
if (e.message === 'an exported wasm function cannot contain a v128 parameter or return value') { log(e); } else { throw e; }
} else if (e instanceof WebAssembly.RuntimeError || e instanceof RangeError) { log(e); } else { throw e; }
}
// log('calling fn8');
report('progress');
try {
for (let k=0; k<16; k++) {
let zzz = fn8(global7.value);
zzz?.toString();
}
} catch (e) {
if (e instanceof WebAssembly.Exception) {
log(e); if (e.stack) { log(e.stack); }
} else if (e instanceof TypeError) {
if (e.message === 'an exported wasm function cannot contain a v128 parameter or return value') { log(e); } else { throw e; }
} else if (e instanceof WebAssembly.RuntimeError || e instanceof RangeError) { log(e); } else { throw e; }
}
// log('calling fn10');
report('progress');
try {
for (let k=0; k<5; k++) {
let zzz = fn10(global7.value);
if (zzz !== undefined) { throw new Error('expected undefined but return value is '+zzz); }
}
} catch (e) {
if (e instanceof WebAssembly.Exception) {
log(e); if (e.stack) { log(e.stack); }
} else if (e instanceof TypeError) {
if (e.message === 'an exported wasm function cannot contain a v128 parameter or return value') { log(e); } else { throw e; }
} else if (e instanceof WebAssembly.RuntimeError || e instanceof RangeError) { log(e); } else { throw e; }
}
// log('calling fn9');
report('progress');
try {
for (let k=0; k<10; k++) {
let zzz = fn9(global2.value, fn10);
if (zzz !== undefined) { throw new Error('expected undefined but return value is '+zzz); }
}
} catch (e) {
if (e instanceof WebAssembly.Exception) {
log(e); if (e.stack) { log(e.stack); }
} else if (e instanceof TypeError) {
if (e.message === 'an exported wasm function cannot contain a v128 parameter or return value') { log(e); } else { throw e; }
} else if (e instanceof WebAssembly.RuntimeError || e instanceof RangeError) { log(e); } else { throw e; }
}
let tables = [table0, table1];
for (let table of tables) {
for (let k=0; k < table.length; k++) { table.get(k)?.toString(); }
}
})().then(() => {
// log('after')
report('after');
}).catch(e => {
log(e)
log('error')
report('error');
})
Binary file not shown.
1 change: 1 addition & 0 deletions Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ class B3IRGenerator {
void computeStackCheckSize(bool& needsOverflowCheck, int32_t& checkSize);

// SIMD
bool usesSIMD() { return m_info.usesSIMD(m_functionIndex); }
void notifyFunctionUsesSIMD() { ASSERT(m_info.usesSIMD(m_functionIndex)); }
PartialResult WARN_UNUSED_RETURN addSIMDLoad(ExpressionType pointer, uint32_t offset, ExpressionType& result);
PartialResult WARN_UNUSED_RETURN addSIMDStore(ExpressionType value, ExpressionType pointer, uint32_t offset);
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/wasm/WasmBBQJIT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8250,6 +8250,8 @@ class BBQJIT {

// SIMD

bool usesSIMD() { return m_usesSIMD; }

void notifyFunctionUsesSIMD()
{
m_usesSIMD = true;
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/wasm/WasmConstExprGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ class ConstExprGenerator {
PartialResult WARN_UNUSED_RETURN addCallRef(const TypeDefinition&, Vector<ExpressionType>&, ResultList&) CONST_EXPR_STUB
PartialResult WARN_UNUSED_RETURN addUnreachable() CONST_EXPR_STUB
PartialResult WARN_UNUSED_RETURN addCrash() CONST_EXPR_STUB
bool usesSIMD() { return false; }
void notifyFunctionUsesSIMD() { }
PartialResult WARN_UNUSED_RETURN addSIMDLoad(ExpressionType, uint32_t, ExpressionType&) CONST_EXPR_STUB
PartialResult WARN_UNUSED_RETURN addSIMDStore(ExpressionType, ExpressionType, uint32_t) CONST_EXPR_STUB
Expand Down
34 changes: 29 additions & 5 deletions Source/JavaScriptCore/wasm/WasmFunctionParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ class FunctionParser : public Parser<void>, public FunctionParserTypes<typename

void addReferencedFunctions(const Element&);
PartialResult WARN_UNUSED_RETURN parseArrayTypeDefinition(const char*, bool, uint32_t&, FieldType&, Type&);
PartialResult WARN_UNUSED_RETURN parseBlockSignatureAndNotifySIMDUseIfNeeded(BlockSignature&);

Context& m_context;
Stack m_expressionStack;
Expand All @@ -347,6 +348,29 @@ class FunctionParser : public Parser<void>, public FunctionParserTypes<typename
unsigned m_loopIndex { 0 };
};

template<typename Context>
auto FunctionParser<Context>::parseBlockSignatureAndNotifySIMDUseIfNeeded(BlockSignature& signature) -> PartialResult
{
auto result = parseBlockSignature(m_info, signature);

// This check ensures the valid result and the non empty signature.
if (!result || !signature)
return result;

if (m_context.usesSIMD()) {
if (!Context::tierSupportsSIMD)
WASM_TRY_ADD_TO_CONTEXT(addCrash());
return result;
}

if (signature->hasReturnVector()) {
m_context.notifyFunctionUsesSIMD();
if (!Context::tierSupportsSIMD)
WASM_TRY_ADD_TO_CONTEXT(addCrash());
}
return result;
}

template<typename ControlType>
static bool isTryOrCatch(ControlType& data)
{
Expand Down Expand Up @@ -2922,7 +2946,7 @@ FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE)
return { };

BlockSignature inlineSignature;
WASM_PARSER_FAIL_IF(!parseBlockSignature(m_info, inlineSignature), "can't get block's signature");
WASM_PARSER_FAIL_IF(!parseBlockSignatureAndNotifySIMDUseIfNeeded(inlineSignature), "can't get block's signature");

WASM_VALIDATOR_FAIL_IF(m_expressionStack.size() < inlineSignature->argumentCount(), "Too few values on stack for block. Block expects ", inlineSignature->argumentCount(), ", but only ", m_expressionStack.size(), " were present. Block has inlineSignature: ", inlineSignature->toString());
unsigned offset = m_expressionStack.size() - inlineSignature->argumentCount();
Expand All @@ -2944,7 +2968,7 @@ FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE)

case Loop: {
BlockSignature inlineSignature;
WASM_PARSER_FAIL_IF(!parseBlockSignature(m_info, inlineSignature), "can't get loop's signature");
WASM_PARSER_FAIL_IF(!parseBlockSignatureAndNotifySIMDUseIfNeeded(inlineSignature), "can't get loop's signature");

WASM_VALIDATOR_FAIL_IF(m_expressionStack.size() < inlineSignature->argumentCount(), "Too few values on stack for loop block. Loop expects ", inlineSignature->argumentCount(), ", but only ", m_expressionStack.size(), " were present. Loop has inlineSignature: ", inlineSignature->toString());
unsigned offset = m_expressionStack.size() - inlineSignature->argumentCount();
Expand All @@ -2968,7 +2992,7 @@ FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE)
case If: {
BlockSignature inlineSignature;
TypedExpression condition;
WASM_PARSER_FAIL_IF(!parseBlockSignature(m_info, inlineSignature), "can't get if's signature");
WASM_PARSER_FAIL_IF(!parseBlockSignatureAndNotifySIMDUseIfNeeded(inlineSignature), "can't get if's signature");
WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "if condition");

WASM_VALIDATOR_FAIL_IF(!condition.type().isI32(), "if condition must be i32, got ", condition.type());
Expand Down Expand Up @@ -3004,7 +3028,7 @@ FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE)

case Try: {
BlockSignature inlineSignature;
WASM_PARSER_FAIL_IF(!parseBlockSignature(m_info, inlineSignature), "can't get try's signature");
WASM_PARSER_FAIL_IF(!parseBlockSignatureAndNotifySIMDUseIfNeeded(inlineSignature), "can't get try's signature");

WASM_VALIDATOR_FAIL_IF(m_expressionStack.size() < inlineSignature->argumentCount(), "Too few arguments on stack for try block. Try expects ", inlineSignature->argumentCount(), ", but only ", m_expressionStack.size(), " were present. Try block has signature: ", inlineSignature->toString());
unsigned offset = m_expressionStack.size() - inlineSignature->argumentCount();
Expand Down Expand Up @@ -3413,7 +3437,7 @@ auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult
case Block: {
m_unreachableBlocks++;
BlockSignature unused;
WASM_PARSER_FAIL_IF(!parseBlockSignature(m_info, unused), "can't get inline type for ", m_currentOpcode, " in unreachable context");
WASM_PARSER_FAIL_IF(!parseBlockSignatureAndNotifySIMDUseIfNeeded(unused), "can't get inline type for ", m_currentOpcode, " in unreachable context");
return { };
}

Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/wasm/WasmIPIntGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class IPIntGenerator {

// SIMD

bool usesSIMD() { return m_usesSIMD; }
void notifyFunctionUsesSIMD() { ASSERT(Options::useWebAssemblySIMD()); m_usesSIMD = true; }
PartialResult WARN_UNUSED_RETURN addSIMDLoad(ExpressionType, uint32_t, ExpressionType&);
PartialResult WARN_UNUSED_RETURN addSIMDStore(ExpressionType, ExpressionType, uint32_t);
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ class LLIntGenerator : public BytecodeGeneratorBase<GeneratorTraits> {
}

void didPopValueFromStack(ExpressionType, String) { --m_stackSize; }
bool usesSIMD() { return m_usesSIMD; }
void notifyFunctionUsesSIMD() { ASSERT(Options::useWebAssemblySIMD()); m_usesSIMD = true; }

PartialResult WARN_UNUSED_RETURN addDrop(ExpressionType);
Expand Down
9 changes: 9 additions & 0 deletions Source/JavaScriptCore/wasm/WasmTypeDefinition.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,15 @@ class FunctionSignature {
return n;
}

bool hasReturnVector() const
{
for (size_t i = 0; i < returnCount(); ++i) {
if (returnType(i).isV128())
return true;
}
return false;
}

bool operator==(const FunctionSignature& other) const
{
// Function signatures are unique because it is just an view class over TypeDefinition and
Expand Down

0 comments on commit e6123d8

Please sign in to comment.