-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BBQJIT should support gc and funcref opcodes #14013
BBQJIT should support gc and funcref opcodes #14013
Conversation
e038447
to
3d4d4d7
Compare
EWS run on previous version of this PR (hash 3d4d4d7)
|
3d4d4d7
to
17ecb7a
Compare
EWS run on previous version of this PR (hash 17ecb7a)
|
case TypeKind::Eqref: | ||
case TypeKind::Anyref: | ||
case TypeKind::Nullref: | ||
// FIXME: Should this just be I64? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think they are I64 since it is EncodedJSValue (it can also be jsNull etc.).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I can do that.
return { }; | ||
} | ||
|
||
void emitStructSet(Value structValue, const StructType& structType, uint32_t fieldIndex, Value value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should pass GPRReg structValueGPR
here since it is confusing that we consume value while we do not consume structValue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
|
||
Location arrayLocation = loadIfNecessary(arrayref); | ||
emitThrowOnNullReference(ExceptionType::NullArrayLen, arrayLocation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dont we need to consume arrayref?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, fixed. I think it kinda works right now because result is the same temp as array and gets the same location. I was trying to think of a validation for this but I couldn't come up with anything that would obviously work.
return { }; | ||
} | ||
|
||
Location arrayLocation = loadIfNecessary(arrayref); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need to consume arrayref?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it get's consumed by the emitCCall in emitArraySetUnchecked
.
17ecb7a
to
d7ed8c0
Compare
EWS run on previous version of this PR (hash d7ed8c0)
|
|
||
LOG_INSTRUCTION("I31New", value, RESULT(result)); | ||
|
||
// Should I be able to load this from an Address? Doesn't matter on ARM tho. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On this, right now we tend to just materialize constants inline throughout BBQJIT. We don't have the luxury of lifting shared constants or knowing if a function will ever use i.e. NumberTag
(hence we'd have to conservatively always reserve a slot for it). So this is consistent with how other constants are handled in BBQJIT, although we could definitely consider improving this in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AH, yeah I should have deleted that comment. I wrote that when as part of the first op I implemented so I didn't know what I was doing.
Value::fromI32(typeIndex), | ||
Value::fromI32(args.size()), | ||
}; | ||
Value allocationResult = Value::fromTemp(TypeKind::Structref, currentControlData().enclosedHeight() + currentControlData().implicitSlots() + m_parser->expressionStack().size() + args.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this Structref
and not Arrayref
? Or I64
for that matter (since operationWasmArrayNewEmpty
returns an encoded JSValue)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy paste error, will fix.
emitArraySetUnchecked(typeIndex, pinnedResult, Value::fromI32(i), args[i]); | ||
} | ||
|
||
result = topValue(TypeKind::Structref); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@@ -385,6 +403,38 @@ class BBQJIT { | |||
return 0; | |||
} | |||
|
|||
static TypeKind toValueKind(TypeKind kind) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to start caring about distinguishing between BBQ value kind and WASM type, maybe this is the time to define a new enum ValueKind
for the former instead of using TypeKind
in the current ad-hoc way. Would make it a lot clearer what a given type is being used for at any given time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think I'll do that in a different patch though.
case TypeKind::Eqref: | ||
case TypeKind::Anyref: | ||
case TypeKind::Nullref: | ||
return TypeKind::I64; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can define an alias for TypeKind::I64
called something like TypeKind::JSValue
(or preferably, we put this in some new ValueKind
enumeration). Functionally this wouldn't change anything (and I guess we don't go this route in B3), but looking at the instruction implementations below it could be helpful for following whether we're using an I64 as an arithmetic integer v.s. as a means for storing a JS value. Maybe also enables better assertions and logging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that'd be a good idea.
|
||
if (isFloatingPointType(resultType.kind)) { | ||
consume(getResult); | ||
result = Value::fromTemp(resultType.kind, currentControlData().enclosedHeight() + currentControlData().implicitSlots() + m_parser->expressionStack().size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can just be result = topValue(resultType.kind);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
{ | ||
// Currently, we assume the Wasm calling convention is the same as the C calling convention | ||
Vector<Type, 1> resultTypes = { Type { returnType, 0u } }; | ||
Vector<Type, 1> resultTypes = { Type { result.type(), 0u } }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add an ASSERT(!result.isNone());
to make sure nobody accidentally passes in an empty value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an ASSERT(result.isTemp()) below. I'll move that to the top of the function.
else | ||
emitCCall(&operationPopcount32, Vector<Value> { operand }, TypeKind::I32, result); | ||
else { | ||
result = topValue(TypeKind::I32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The EMIT_UNARY(...)
macro will already assign result
to the top value on the stack and give it a register, so it should be able to be passed in. However, this creates a somewhat nasty tacit dependency - emitCCall
will bind result
to the returnValueGPR
, which will error if result
is already bound to a different register (to avoid mucking with the register allocator state). It shouldn't currently error specifically because we only allocate caller-saved registers, which get flushed across the call regardless; however, if we add callee-saved register allocation to BBQJIT in the future, we could get very niche errors.
All this is to say, I think the most correct thing with the current abstractions would be to
consume(result);
emitCCall(&operationPopcount32, Vector<Value> { operand }, result);
so result
is quietly unbound from its current register before we pass it into emitCCall
. This only applies to addI32Popcnt
and addI64Popcnt
since they are currently the only functions that use EMIT_UNARY
/EMIT_BINARY
and also contain a call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll make the change to consume. Unfortunately I think we won't have any bots that test this code path anyway. Since I don't think we have any bots that run on old enough CPUs that they'll fail the supportsCountPopulation()
test now that arm64 always passes. (For reference the first Intel processor with POPCNT came out 14 years ago https://en.wikipedia.org/wiki/Nehalem_(microarchitecture) and I think earlier for AMD)
else | ||
emitCCall(&operationPopcount64, Vector<Value> { operand }, TypeKind::I32, result); | ||
else { | ||
result = topValue(TypeKind::I32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't strictly related to your patch but I just realized this is slightly wrong...operationPopcount64
returns a 64-bit result, not a 32-bit one, so I32
is the wrong type. If we reuse the result
from EMIT_UNARY
like I mentioned in my previous comment, it should have the correct type already; otherwise, this should probably use TypeKind::I64
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed via switching it to consume
.
d7ed8c0
to
8fe886f
Compare
EWS run on previous version of this PR (hash 8fe886f)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r=me with comments and update of addCallRef
.
else | ||
emitCCall(&operationPopcount32, Vector<Value> { operand }, TypeKind::I32, result); | ||
else { | ||
consume(result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have explicit detailed comment about this hack here?
else | ||
emitCCall(&operationPopcount64, Vector<Value> { operand }, TypeKind::I32, result); | ||
else { | ||
consume(result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
{ | ||
ScratchScope<3, 0> scratches(*this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please update this code to the latest convention. calleeCode
always needs to be in GPRInfo::nonPreservedNonArgumentGPR1 to make shuffling work correctly. See updated addCallIndirect
code changed in 76f47aa
8fe886f
to
d85acf3
Compare
EWS run on previous version of this PR (hash d85acf3)
|
GPRReg calleeInstance; | ||
GPRReg calleeCode; | ||
GPRReg jsCalleeAnchor; | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need clobber(GPRInfo::nonPreservedNonArgumentGPR1)
d85acf3
to
66892b6
Compare
EWS run on previous version of this PR (hash 66892b6)
|
66892b6
to
6eeab0a
Compare
EWS run on previous version of this PR (hash 6eeab0a)
|
6eeab0a
to
2067201
Compare
EWS run on previous version of this PR (hash 2067201)
|
2067201
to
450b85b
Compare
EWS run on current version of this PR (hash 450b85b)
|
EWS run on current version of this PR (hash 450b85b)
|
https://bugs.webkit.org/show_bug.cgi?id=256959 Reviewed by Yusuke Suzuki. This patch adds support for the various gc and funcref opcodes to the new BBQ JIT. Most of the implementations are just translations of what the B3IRGenerator does. The main difference is that for opcodes which need to make a C call, e.g. for allocation, they do so by creating a `Value::fromTemp` that does not conflict with any parameter `Value`. This is needed because otherwise the BBQJIT allocator gets confused between the existing parameters that were not passed to the C call and the result of the C call. Also, since BBQJIT doesn't have a good way to branch over a call both `ref.cast` and `ref.test` just call an operation. Also, this patch fixes an issue where we weren't checking for the spec's limit on array.new_fixed static argument count. Lastly, there is a workaround for a clang bug where it crashed when compiling a unified source. The workaround was to @no-unify one of the files in that bundle. * JSTests/wasm/gc/array_new_fixed.js: * JSTests/wasm/gc/i31.js: (testI31Get): * Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj: * Source/JavaScriptCore/Sources.txt: * Source/JavaScriptCore/runtime/Options.cpp: (JSC::Options::notifyOptionsChanged): * Source/JavaScriptCore/wasm/WasmBBQJIT.cpp: (JSC::Wasm::BBQJIT::isValidValueTypeKind): (JSC::Wasm::BBQJIT::pointerType): (JSC::Wasm::BBQJIT::toValueKind): (JSC::Wasm::BBQJIT::Value::fromRef): (JSC::Wasm::BBQJIT::Value::fromTemp): (JSC::Wasm::BBQJIT::Value::fromLocal): (JSC::Wasm::BBQJIT::Value::pinned): (JSC::Wasm::BBQJIT::Value::type const): (JSC::Wasm::BBQJIT::BBQJIT): (JSC::Wasm::BBQJIT::addTableGet): (JSC::Wasm::BBQJIT::addTableSet): (JSC::Wasm::BBQJIT::addTableInit): (JSC::Wasm::BBQJIT::addTableSize): (JSC::Wasm::BBQJIT::addTableGrow): (JSC::Wasm::BBQJIT::addTableFill): (JSC::Wasm::BBQJIT::addTableCopy): (JSC::Wasm::BBQJIT::addGrowMemory): (JSC::Wasm::BBQJIT::addMemoryFill): (JSC::Wasm::BBQJIT::addMemoryCopy): (JSC::Wasm::BBQJIT::addMemoryInit): (JSC::Wasm::BBQJIT::atomicWait): (JSC::Wasm::BBQJIT::atomicNotify): (JSC::Wasm::BBQJIT::addI31New): (JSC::Wasm::BBQJIT::addI31GetS): (JSC::Wasm::BBQJIT::addI31GetU): (JSC::Wasm::BBQJIT::getTypeDefinition): (JSC::Wasm::BBQJIT::getArrayTypeDefinition): (JSC::Wasm::BBQJIT::getArrayElementType): (JSC::Wasm::BBQJIT::marshallToI64): (JSC::Wasm::BBQJIT::addArrayNew): (JSC::Wasm::BBQJIT::addArrayNewDefault): (JSC::Wasm::BBQJIT::pushArrayNewFromSegment): (JSC::Wasm::BBQJIT::addArrayNewData): (JSC::Wasm::BBQJIT::addArrayNewElem): (JSC::Wasm::BBQJIT::emitArraySetUnchecked): (JSC::Wasm::BBQJIT::addArrayNewFixed): (JSC::Wasm::BBQJIT::addArrayGet): (JSC::Wasm::BBQJIT::addArraySet): (JSC::Wasm::BBQJIT::addArrayLen): (JSC::Wasm::BBQJIT::emitStructSet): (JSC::Wasm::BBQJIT::addStructNewDefault): (JSC::Wasm::BBQJIT::addStructNew): (JSC::Wasm::BBQJIT::addStructGet): (JSC::Wasm::BBQJIT::addStructSet): (JSC::Wasm::BBQJIT::addRefTest): (JSC::Wasm::BBQJIT::addRefCast): (JSC::Wasm::BBQJIT::addExternInternalize): (JSC::Wasm::BBQJIT::emitThrowOnNullReference): (JSC::Wasm::BBQJIT::addI32Popcnt): (JSC::Wasm::BBQJIT::addI64Popcnt): (JSC::Wasm::BBQJIT::addRefFunc): (JSC::Wasm::BBQJIT::toB3Type): (JSC::Wasm::BBQJIT::emitCCall): (JSC::Wasm::BBQJIT::addCallRef): * Source/JavaScriptCore/wasm/WasmFunctionParser.h: (JSC::Wasm::FunctionParser<Context>::parseExpression): * Source/JavaScriptCore/wasm/WasmLimits.h: * Source/JavaScriptCore/wasm/WasmOperations.cpp: (JSC::Wasm::JSC_DEFINE_JIT_OPERATION): * Source/JavaScriptCore/wasm/WasmOperations.h: Canonical link: https://commits.webkit.org/264638@main
450b85b
to
6ef70e7
Compare
Committed 264638@main (6ef70e7): https://commits.webkit.org/264638@main Reviewed commits have been landed. Closing PR #14013 and removing active labels. |
6ef70e7
450b85b