2 * Copyright (C) 2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "WasmB3IRGenerator.h"
29 #if ENABLE(WEBASSEMBLY)
31 #include "B3BasicBlockInlines.h"
32 #include "B3CCallValue.h"
33 #include "B3ConstPtrValue.h"
35 #include "B3StackmapGenerationParams.h"
36 #include "B3SwitchValue.h"
37 #include "B3Validate.h"
38 #include "B3ValueInlines.h"
39 #include "B3Variable.h"
40 #include "B3VariableValue.h"
41 #include "B3WasmAddressValue.h"
42 #include "B3WasmBoundsCheckValue.h"
43 #include "VirtualRegister.h"
44 #include "WasmCallingConvention.h"
45 #include "WasmFunctionParser.h"
46 #include "WasmMemory.h"
47 #include <wtf/Optional.h>
49 void dumpProcedure(void* ptr)
51 JSC::B3::Procedure* proc = static_cast<JSC::B3::Procedure*>(ptr);
52 proc->dump(WTF::dataFile());
55 namespace JSC { namespace Wasm {
60 const bool verbose = false;
66 ControlData(Procedure& proc, Type signature, BlockType type, BasicBlock* continuation, BasicBlock* special = nullptr)
68 , continuation(continuation)
71 if (signature != Void)
72 result.append(proc.addVariable(toB3Type(signature)));
79 void dump(PrintStream& out) const
85 case BlockType::Block:
92 out.print("Continuation: ", *continuation, ", Special: ");
99 BlockType type() const { return blockType; }
101 bool hasNonVoidSignature() const { return result.size(); }
103 BasicBlock* targetBlockForBranch()
105 if (type() == BlockType::Loop)
110 void convertIfToBlock()
112 ASSERT(type() == BlockType::If);
113 blockType = BlockType::Block;
118 friend class B3IRGenerator;
120 BasicBlock* continuation;
122 Vector<Variable*, 1> result;
125 typedef Value* ExpressionType;
126 typedef ControlData ControlType;
127 typedef Vector<ExpressionType, 1> ExpressionList;
128 typedef Vector<Variable*, 1> ResultList;
129 typedef FunctionParser<B3IRGenerator>::ControlEntry ControlEntry;
131 static constexpr ExpressionType emptyExpression = nullptr;
133 B3IRGenerator(MemoryInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&);
135 bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
136 bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
137 ExpressionType addConstant(Type, uint64_t);
140 bool WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
141 bool WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
144 bool WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
145 bool WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
149 bool WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result);
151 bool WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result);
152 bool WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result);
155 ControlData WARN_UNUSED_RETURN addBlock(Type signature);
156 ControlData WARN_UNUSED_RETURN addLoop(Type signature);
157 bool WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
158 bool WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
159 bool WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
161 bool WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
162 bool WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues);
163 bool WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const ExpressionList& expressionStack);
164 bool WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
165 bool WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
167 bool WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature*, Vector<ExpressionType>& args, ExpressionType& result);
169 void dump(const Vector<ControlEntry>& controlStack, const ExpressionList& expressionStack);
171 void setErrorMessage(String&&) { UNREACHABLE_FOR_PLATFORM(); }
174 ExpressionType emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOp);
175 ExpressionType emitLoadOp(LoadOpType, Origin, ExpressionType pointer, uint32_t offset);
176 void emitStoreOp(StoreOpType, Origin, ExpressionType pointer, ExpressionType value, uint32_t offset);
178 void unify(Variable* target, const ExpressionType source);
179 void unifyValuesWithBlock(const ExpressionList& resultStack, ResultList& stack);
180 Value* zeroForType(Type);
183 BasicBlock* m_currentBlock;
184 Vector<Variable*> m_locals;
185 Vector<UnlinkedWasmToWasmCall>& m_unlinkedWasmToWasmCalls; // List each call site and the function index whose address it should be patched with.
186 GPRReg m_memoryBaseGPR;
187 GPRReg m_memorySizeGPR;
188 Value* m_zeroValues[numTypes];
191 B3IRGenerator::B3IRGenerator(MemoryInformation& memory, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls)
193 , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
195 m_currentBlock = m_proc.addBlock();
197 for (unsigned i = 0; i < numTypes; ++i) {
198 switch (B3::Type b3Type = toB3Type(linearizedToType(i))) {
203 m_zeroValues[i] = m_currentBlock->appendIntConstant(m_proc, Origin(), b3Type, 0);
206 m_zeroValues[i] = nullptr;
212 m_memoryBaseGPR = memory.pinnedRegisters().baseMemoryPointer;
213 m_proc.pinRegister(m_memoryBaseGPR);
214 ASSERT(!memory.pinnedRegisters().sizeRegisters[0].sizeOffset);
215 m_memorySizeGPR = memory.pinnedRegisters().sizeRegisters[0].sizeRegister;
216 for (const PinnedSizeRegisterInfo& info : memory.pinnedRegisters().sizeRegisters)
217 m_proc.pinRegister(info.sizeRegister);
219 m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) {
220 ASSERT_UNUSED(pinnedGPR, m_memorySizeGPR == pinnedGPR);
221 // FIXME: This should unwind the stack and throw a JS exception. See: https://bugs.webkit.org/show_bug.cgi?id=163351
226 wasmCallingConvention().setupFrameInPrologue(compilation, m_proc, Origin(), m_currentBlock);
229 Value* B3IRGenerator::zeroForType(Type type)
231 ASSERT(type != Void);
232 Value* zeroValue = m_zeroValues[linearizeType(type)];
237 bool B3IRGenerator::addLocal(Type type, uint32_t count)
239 if (!m_locals.tryReserveCapacity(m_locals.size() + count))
242 for (uint32_t i = 0; i < count; ++i) {
243 Variable* local = m_proc.addVariable(toB3Type(type));
244 m_locals.uncheckedAppend(local);
245 m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), local, zeroForType(type));
250 bool B3IRGenerator::addArguments(const Vector<Type>& types)
252 ASSERT(!m_locals.size());
253 if (!m_locals.tryReserveCapacity(types.size()))
256 m_locals.grow(types.size());
257 wasmCallingConvention().loadArguments(types, m_proc, m_currentBlock, Origin(),
258 [&] (ExpressionType argument, unsigned i) {
259 Variable* argumentVariable = m_proc.addVariable(argument->type());
260 m_locals[i] = argumentVariable;
261 m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), argumentVariable, argument);
266 bool B3IRGenerator::getLocal(uint32_t index, ExpressionType& result)
268 ASSERT(m_locals[index]);
269 result = m_currentBlock->appendNew<VariableValue>(m_proc, B3::Get, Origin(), m_locals[index]);
273 bool B3IRGenerator::setLocal(uint32_t index, ExpressionType value)
275 ASSERT(m_locals[index]);
276 m_currentBlock->appendNew<VariableValue>(m_proc, B3::Set, Origin(), m_locals[index], value);
280 inline Value* B3IRGenerator::emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOperation)
282 ASSERT(m_memoryBaseGPR && m_memorySizeGPR);
283 ASSERT(sizeOfOperation + offset > offset);
284 m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, Origin(), pointer, m_memorySizeGPR, sizeOfOperation + offset - 1);
285 pointer = m_currentBlock->appendNew<Value>(m_proc, ZExt32, Origin(), pointer);
286 return m_currentBlock->appendNew<WasmAddressValue>(m_proc, Origin(), pointer, m_memoryBaseGPR);
289 inline uint32_t sizeOfLoadOp(LoadOpType op)
292 case LoadOpType::I32Load8S:
293 case LoadOpType::I32Load8U:
294 case LoadOpType::I64Load8S:
295 case LoadOpType::I64Load8U:
297 case LoadOpType::I32Load16S:
298 case LoadOpType::I64Load16S:
300 case LoadOpType::I32Load:
301 case LoadOpType::I64Load32S:
302 case LoadOpType::I64Load32U:
303 case LoadOpType::F32Load:
305 case LoadOpType::I64Load:
306 case LoadOpType::F64Load:
308 case LoadOpType::I32Load16U:
309 case LoadOpType::I64Load16U:
312 RELEASE_ASSERT_NOT_REACHED();
315 inline Value* B3IRGenerator::emitLoadOp(LoadOpType op, Origin origin, ExpressionType pointer, uint32_t offset)
318 case LoadOpType::I32Load8S: {
319 return m_currentBlock->appendNew<MemoryValue>(m_proc, Load8S, origin, pointer, offset);
322 case LoadOpType::I64Load8S: {
323 Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8S, origin, pointer, offset);
324 return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin, value);
327 case LoadOpType::I32Load8U: {
328 return m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, origin, pointer, offset);
331 case LoadOpType::I64Load8U: {
332 Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, origin, pointer, offset);
333 return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin, value);
336 case LoadOpType::I32Load16S: {
337 return m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset);
339 case LoadOpType::I64Load16S: {
340 Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset);
341 return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin, value);
344 case LoadOpType::I32Load: {
345 return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin, pointer);
348 case LoadOpType::I64Load32U: {
349 Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin, pointer);
350 return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin, value);
353 case LoadOpType::I64Load32S: {
354 Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin, pointer);
355 return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin, value);
358 case LoadOpType::I64Load: {
359 return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int64, origin, pointer);
362 case LoadOpType::F32Load: {
363 return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Float, origin, pointer);
366 case LoadOpType::F64Load: {
367 return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Double, origin, pointer);
370 // B3 doesn't support Load16Z yet.
371 case LoadOpType::I32Load16U:
372 case LoadOpType::I64Load16U:
375 RELEASE_ASSERT_NOT_REACHED();
378 bool B3IRGenerator::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t offset)
380 ASSERT(pointer->type() == Int32);
382 result = emitLoadOp(op, Origin(), emitCheckAndPreparePointer(pointer, offset, sizeOfLoadOp(op)), offset);
386 inline uint32_t sizeOfStoreOp(StoreOpType op)
389 case StoreOpType::I32Store8:
390 case StoreOpType::I64Store8:
392 case StoreOpType::I32Store16:
393 case StoreOpType::I64Store16:
395 case StoreOpType::I32Store:
396 case StoreOpType::I64Store32:
397 case StoreOpType::F32Store:
399 case StoreOpType::I64Store:
400 case StoreOpType::F64Store:
403 RELEASE_ASSERT_NOT_REACHED();
407 inline void B3IRGenerator::emitStoreOp(StoreOpType op, Origin origin, ExpressionType pointer, ExpressionType value, uint32_t offset)
410 case StoreOpType::I64Store8:
411 value = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin, value);
414 case StoreOpType::I32Store8:
415 m_currentBlock->appendNew<MemoryValue>(m_proc, Store8, origin, value, pointer, offset);
418 case StoreOpType::I64Store16:
419 value = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin, value);
422 case StoreOpType::I32Store16:
423 m_currentBlock->appendNew<MemoryValue>(m_proc, Store16, origin, value, pointer, offset);
426 case StoreOpType::I64Store32:
427 value = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin, value);
430 case StoreOpType::I64Store:
431 case StoreOpType::I32Store:
432 case StoreOpType::F32Store:
433 case StoreOpType::F64Store:
434 m_currentBlock->appendNew<MemoryValue>(m_proc, Store, origin, value, pointer, offset);
437 RELEASE_ASSERT_NOT_REACHED();
440 bool B3IRGenerator::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t offset)
442 ASSERT(pointer->type() == Int32);
444 emitStoreOp(op, Origin(), emitCheckAndPreparePointer(pointer, offset, sizeOfStoreOp(op)), value, offset);
448 bool B3IRGenerator::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result)
450 result = m_currentBlock->appendNew<Value>(m_proc, B3::Select, Origin(), condition, nonZero, zero);
454 B3IRGenerator::ExpressionType B3IRGenerator::addConstant(Type type, uint64_t value)
458 return m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), static_cast<int32_t>(value));
460 return m_currentBlock->appendNew<Const64Value>(m_proc, Origin(), value);
462 return m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), bitwise_cast<float>(static_cast<int32_t>(value)));
464 return m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), bitwise_cast<double>(value));
470 RELEASE_ASSERT_NOT_REACHED();
474 B3IRGenerator::ControlData B3IRGenerator::addBlock(Type signature)
476 return ControlData(m_proc, signature, BlockType::Block, m_proc.addBlock());
479 B3IRGenerator::ControlData B3IRGenerator::addLoop(Type signature)
481 BasicBlock* body = m_proc.addBlock();
482 BasicBlock* continuation = m_proc.addBlock();
483 m_currentBlock->appendNewControlValue(m_proc, Jump, Origin(), body);
484 body->addPredecessor(m_currentBlock);
485 m_currentBlock = body;
486 return ControlData(m_proc, signature, BlockType::Loop, continuation, body);
489 bool B3IRGenerator::addIf(ExpressionType condition, Type signature, ControlType& result)
491 // FIXME: This needs to do some kind of stack passing.
493 BasicBlock* taken = m_proc.addBlock();
494 BasicBlock* notTaken = m_proc.addBlock();
495 BasicBlock* continuation = m_proc.addBlock();
497 m_currentBlock->appendNew<Value>(m_proc, B3::Branch, Origin(), condition);
498 m_currentBlock->setSuccessors(FrequentedBlock(taken), FrequentedBlock(notTaken));
499 taken->addPredecessor(m_currentBlock);
500 notTaken->addPredecessor(m_currentBlock);
502 m_currentBlock = taken;
503 result = ControlData(m_proc, signature, BlockType::If, continuation, notTaken);
507 bool B3IRGenerator::addElse(ControlData& data, const ExpressionList& currentStack)
509 unifyValuesWithBlock(currentStack, data.result);
510 m_currentBlock->appendNewControlValue(m_proc, Jump, Origin(), data.continuation);
511 return addElseToUnreachable(data);
514 bool B3IRGenerator::addElseToUnreachable(ControlData& data)
516 ASSERT(data.type() == BlockType::If);
517 m_currentBlock = data.special;
518 data.convertIfToBlock();
522 bool B3IRGenerator::addReturn(const ExpressionList& returnValues)
524 ASSERT(returnValues.size() <= 1);
525 if (returnValues.size())
526 m_currentBlock->appendNewControlValue(m_proc, B3::Return, Origin(), returnValues[0]);
528 m_currentBlock->appendNewControlValue(m_proc, B3::Return, Origin());
532 bool B3IRGenerator::addBranch(ControlData& data, ExpressionType condition, const ExpressionList& returnValues)
534 if (data.type() != BlockType::Loop)
535 unifyValuesWithBlock(returnValues, data.result);
537 BasicBlock* target = data.targetBlockForBranch();
539 BasicBlock* continuation = m_proc.addBlock();
540 m_currentBlock->appendNew<Value>(m_proc, B3::Branch, Origin(), condition);
541 m_currentBlock->setSuccessors(FrequentedBlock(target), FrequentedBlock(continuation));
542 target->addPredecessor(m_currentBlock);
543 continuation->addPredecessor(m_currentBlock);
544 m_currentBlock = continuation;
546 m_currentBlock->appendNewControlValue(m_proc, Jump, Origin(), FrequentedBlock(target));
547 target->addPredecessor(m_currentBlock);
553 bool B3IRGenerator::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack)
555 for (size_t i = 0; i < targets.size(); ++i)
556 unifyValuesWithBlock(expressionStack, targets[i]->result);
557 unifyValuesWithBlock(expressionStack, defaultTarget.result);
559 SwitchValue* switchValue = m_currentBlock->appendNew<SwitchValue>(m_proc, Origin(), condition);
560 switchValue->setFallThrough(FrequentedBlock(defaultTarget.targetBlockForBranch()));
561 for (size_t i = 0; i < targets.size(); ++i)
562 switchValue->appendCase(SwitchCase(i, FrequentedBlock(targets[i]->targetBlockForBranch())));
567 bool B3IRGenerator::endBlock(ControlEntry& entry, ExpressionList& expressionStack)
569 ControlData& data = entry.controlData;
571 unifyValuesWithBlock(expressionStack, data.result);
572 m_currentBlock->appendNewControlValue(m_proc, Jump, Origin(), data.continuation);
573 data.continuation->addPredecessor(m_currentBlock);
575 return addEndToUnreachable(entry);
579 bool B3IRGenerator::addEndToUnreachable(ControlEntry& entry)
581 ControlData& data = entry.controlData;
582 m_currentBlock = data.continuation;
584 if (data.type() == BlockType::If) {
585 data.special->appendNewControlValue(m_proc, Jump, Origin(), m_currentBlock);
586 m_currentBlock->addPredecessor(data.special);
589 for (Variable* result : data.result)
590 entry.enclosedExpressionStack.append(m_currentBlock->appendNew<VariableValue>(m_proc, B3::Get, Origin(), result));
595 bool B3IRGenerator::addCall(unsigned functionIndex, const Signature* signature, Vector<ExpressionType>& args, ExpressionType& result)
597 ASSERT(signature->arguments.size() == args.size());
599 Type returnType = signature->returnType;
601 size_t callIndex = m_unlinkedWasmToWasmCalls.size();
602 m_unlinkedWasmToWasmCalls.grow(callIndex + 1);
603 result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, Origin(), args, toB3Type(returnType),
604 [&] (PatchpointValue* patchpoint) {
605 patchpoint->effects.writesPinned = true;
606 patchpoint->effects.readsPinned = true;
608 patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
609 AllowMacroScratchRegisterUsage allowScratch(jit);
611 CCallHelpers::Call call = jit.call();
613 jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
614 m_unlinkedWasmToWasmCalls[callIndex] = { linkBuffer.locationOf(call), functionIndex };
621 void B3IRGenerator::unify(Variable* variable, ExpressionType source)
623 m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), variable, source);
626 void B3IRGenerator::unifyValuesWithBlock(const ExpressionList& resultStack, ResultList& result)
628 ASSERT(result.size() <= resultStack.size());
630 for (size_t i = 0; i < result.size(); ++i)
631 unify(result[result.size() - 1 - i], resultStack[resultStack.size() - 1 - i]);
634 static void dumpExpressionStack(const CommaPrinter& comma, const B3IRGenerator::ExpressionList& expressionStack)
636 dataLogLn(comma, "ExpressionStack:");
637 for (const auto& expression : expressionStack)
638 dataLogLn(comma, *expression);
641 void B3IRGenerator::dump(const Vector<ControlEntry>& controlStack, const ExpressionList& expressionStack)
643 dataLogLn("Processing Graph:");
645 dataLogLn("With current block:", *m_currentBlock);
646 dataLogLn("Control stack:");
647 for (auto& data : controlStack) {
648 dataLogLn(" ", data.controlData);
649 if (data.enclosedExpressionStack.size()) {
650 CommaPrinter comma(" ", " with ");
651 dumpExpressionStack(comma, data.enclosedExpressionStack);
655 CommaPrinter comma(" ", "");
656 dumpExpressionStack(comma, expressionStack);
660 static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signature* signature, MacroAssemblerCodePtr mainFunction, MemoryInformation& memory)
663 BasicBlock* block = proc.addBlock();
665 // Check argument count is sane.
666 Value* framePointer = block->appendNew<B3::Value>(proc, B3::FramePointer, Origin());
667 Value* offSetOfArgumentCount = block->appendNew<Const64Value>(proc, Origin(), CallFrameSlot::argumentCount * sizeof(Register));
668 Value* argumentCount = block->appendNew<MemoryValue>(proc, Load, Int32, Origin(),
669 block->appendNew<Value>(proc, Add, Origin(), framePointer, offSetOfArgumentCount));
671 Value* expectedArgumentCount = block->appendNew<Const32Value>(proc, Origin(), signature->arguments.size());
673 CheckValue* argumentCountCheck = block->appendNew<CheckValue>(proc, Check, Origin(),
674 block->appendNew<Value>(proc, Above, Origin(), expectedArgumentCount, argumentCount));
675 argumentCountCheck->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
679 // Move memory values to the approriate places, if needed.
680 Value* baseMemory = nullptr;
681 Vector<Value*> sizes;
683 baseMemory = block->appendNew<MemoryValue>(proc, Load, Int64, Origin(),
684 block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topWasmMemoryPointer));
685 Value* size = block->appendNew<MemoryValue>(proc, Load, Int32, Origin(),
686 block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topWasmMemorySize));
687 sizes.reserveCapacity(memory.pinnedRegisters().sizeRegisters.size());
688 for (auto info : memory.pinnedRegisters().sizeRegisters) {
689 sizes.append(block->appendNew<Value>(proc, Sub, Origin(), size,
690 block->appendNew<Const32Value>(proc, Origin(), info.sizeOffset)));
694 // Get our arguments.
695 Vector<Value*> arguments;
696 jscCallingConvention().loadArguments(signature->arguments, proc, block, Origin(), [&] (Value* argument, unsigned) {
697 arguments.append(argument);
700 // Move the arguments into place.
701 Value* result = wasmCallingConvention().setupCall(proc, block, Origin(), arguments, toB3Type(signature->returnType), [&] (PatchpointValue* patchpoint) {
703 ASSERT(sizes.size() == memory.pinnedRegisters().sizeRegisters.size());
704 patchpoint->append(ConstrainedValue(baseMemory, ValueRep::reg(memory.pinnedRegisters().baseMemoryPointer)));
705 for (unsigned i = 0; i < sizes.size(); ++i)
706 patchpoint->append(ConstrainedValue(sizes[i], ValueRep::reg(memory.pinnedRegisters().sizeRegisters[i].sizeRegister)));
709 patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
710 AllowMacroScratchRegisterUsage allowScratch(jit);
712 CCallHelpers::Call call = jit.call();
713 jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
714 linkBuffer.link(call, FunctionPtr(mainFunction.executableAddress()));
719 // Return the result, if needed.
720 switch (signature->returnType) {
722 block->appendNewControlValue(proc, B3::Return, Origin());
726 result = block->appendNew<Value>(proc, BitwiseCast, Origin(), result);
730 block->appendNewControlValue(proc, B3::Return, Origin(), result);
734 RELEASE_ASSERT_NOT_REACHED();
737 return std::make_unique<Compilation>(vm, proc);
740 std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* functionStart, size_t functionLength, MemoryInformation& memory, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const FunctionIndexSpace& functionIndexSpace, unsigned optLevel)
742 auto result = std::make_unique<WasmInternalFunction>();
745 B3IRGenerator context(memory, procedure, result.get(), unlinkedWasmToWasmCalls);
746 FunctionParser<B3IRGenerator> parser(context, functionStart, functionLength, signature, functionIndexSpace);
748 RELEASE_ASSERT_NOT_REACHED();
750 procedure.resetReachability();
751 validate(procedure, "After parsing:\n");
754 dataLog("Pre SSA: ", procedure);
757 dataLog("Post SSA: ", procedure);
759 result->code = std::make_unique<Compilation>(vm, procedure, optLevel);
760 result->jsToWasmEntryPoint = createJSToWasmWrapper(vm, signature, result->code->code(), memory);
764 // Custom wasm ops. These are the ones too messy to do in wasm.json.
767 bool B3IRGenerator::addOp<OpType::I32Ctz>(ExpressionType arg, ExpressionType& result)
769 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin());
770 patchpoint->append(arg, ValueRep::SomeRegister);
771 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
772 jit.countTrailingZeros32(params[1].gpr(), params[0].gpr());
774 patchpoint->effects = Effects::none();
780 bool B3IRGenerator::addOp<OpType::I64Ctz>(ExpressionType arg, ExpressionType& result)
782 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin());
783 patchpoint->append(arg, ValueRep::SomeRegister);
784 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
785 jit.countTrailingZeros64(params[1].gpr(), params[0].gpr());
787 patchpoint->effects = Effects::none();
793 bool B3IRGenerator::addOp<OpType::I32Popcnt>(ExpressionType arg, ExpressionType& result)
795 // FIXME: This should use the popcnt instruction if SSE4 is available but we don't have code to detect SSE4 yet.
796 // see: https://bugs.webkit.org/show_bug.cgi?id=165363
797 uint32_t (*popcount)(int32_t) = [] (int32_t value) -> uint32_t { return __builtin_popcount(value); };
798 Value* funcAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), bitwise_cast<void*>(popcount));
799 result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, Origin(), Effects::none(), funcAddress, arg);
804 bool B3IRGenerator::addOp<OpType::I64Popcnt>(ExpressionType arg, ExpressionType& result)
806 // FIXME: This should use the popcnt instruction if SSE4 is available but we don't have code to detect SSE4 yet.
807 // see: https://bugs.webkit.org/show_bug.cgi?id=165363
808 uint64_t (*popcount)(int64_t) = [] (int64_t value) -> uint64_t { return __builtin_popcountll(value); };
809 Value* funcAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), bitwise_cast<void*>(popcount));
810 result = m_currentBlock->appendNew<CCallValue>(m_proc, Int64, Origin(), Effects::none(), funcAddress, arg);
815 bool B3IRGenerator::addOp<F64ConvertUI64>(ExpressionType arg, ExpressionType& result)
817 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, Origin());
819 patchpoint->numGPScratchRegisters = 1;
820 patchpoint->append(ConstrainedValue(arg, ValueRep::WarmAny));
821 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
822 AllowMacroScratchRegisterUsage allowScratch(jit);
824 jit.convertUInt64ToDouble(params[1].gpr(), params[0].fpr(), params.gpScratch(0));
826 jit.convertUInt64ToDouble(params[1].gpr(), params[0].fpr());
829 patchpoint->effects = Effects::none();
835 bool B3IRGenerator::addOp<OpType::F32ConvertUI64>(ExpressionType arg, ExpressionType& result)
837 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, Origin());
839 patchpoint->numGPScratchRegisters = 1;
840 patchpoint->append(ConstrainedValue(arg, ValueRep::WarmAny));
841 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
842 AllowMacroScratchRegisterUsage allowScratch(jit);
844 jit.convertUInt64ToFloat(params[1].gpr(), params[0].fpr(), params.gpScratch(0));
846 jit.convertUInt64ToFloat(params[1].gpr(), params[0].fpr());
849 patchpoint->effects = Effects::none();
855 bool B3IRGenerator::addOp<OpType::F64Nearest>(ExpressionType arg, ExpressionType& result)
857 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, Origin());
858 patchpoint->append(arg, ValueRep::SomeRegister);
859 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
860 jit.roundTowardNearestIntDouble(params[1].fpr(), params[0].fpr());
862 patchpoint->effects = Effects::none();
868 bool B3IRGenerator::addOp<OpType::F32Nearest>(ExpressionType arg, ExpressionType& result)
870 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, Origin());
871 patchpoint->append(arg, ValueRep::SomeRegister);
872 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
873 jit.roundTowardNearestIntFloat(params[1].fpr(), params[0].fpr());
875 patchpoint->effects = Effects::none();
881 bool B3IRGenerator::addOp<OpType::F64Trunc>(ExpressionType arg, ExpressionType& result)
883 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, Origin());
884 patchpoint->append(arg, ValueRep::SomeRegister);
885 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
886 jit.roundTowardZeroDouble(params[1].fpr(), params[0].fpr());
888 patchpoint->effects = Effects::none();
894 bool B3IRGenerator::addOp<OpType::F32Trunc>(ExpressionType arg, ExpressionType& result)
896 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, Origin());
897 patchpoint->append(arg, ValueRep::SomeRegister);
898 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
899 jit.roundTowardZeroFloat(params[1].fpr(), params[0].fpr());
901 patchpoint->effects = Effects::none();
907 bool B3IRGenerator::addOp<OpType::I32TruncSF64>(ExpressionType arg, ExpressionType& result)
909 Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -static_cast<double>(std::numeric_limits<int32_t>::min()));
910 Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int32_t>::min()));
911 Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(),
912 m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max),
913 m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, Origin(), arg, min));
914 outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32));
915 CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds);
916 trap->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
919 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin());
920 patchpoint->append(arg, ValueRep::SomeRegister);
921 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
922 jit.truncateDoubleToInt32(params[1].fpr(), params[0].gpr());
924 patchpoint->effects = Effects::none();
930 bool B3IRGenerator::addOp<OpType::I32TruncSF32>(ExpressionType arg, ExpressionType& result)
932 Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -static_cast<float>(std::numeric_limits<int32_t>::min()));
933 Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int32_t>::min()));
934 Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(),
935 m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max),
936 m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, Origin(), arg, min));
937 outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32));
938 CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds);
939 trap->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
942 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin());
943 patchpoint->append(arg, ValueRep::SomeRegister);
944 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
945 jit.truncateFloatToInt32(params[1].fpr(), params[0].gpr());
947 patchpoint->effects = Effects::none();
954 bool B3IRGenerator::addOp<OpType::I32TruncUF64>(ExpressionType arg, ExpressionType& result)
956 Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int32_t>::min()) * -2.0);
957 Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -1.0);
958 Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(),
959 m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max),
960 m_currentBlock->appendNew<Value>(m_proc, GreaterThan, Origin(), arg, min));
961 outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32));
962 CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds);
963 trap->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
966 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin());
967 patchpoint->append(arg, ValueRep::SomeRegister);
968 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
969 jit.truncateDoubleToUint32(params[1].fpr(), params[0].gpr());
971 patchpoint->effects = Effects::none();
977 bool B3IRGenerator::addOp<OpType::I32TruncUF32>(ExpressionType arg, ExpressionType& result)
979 Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int32_t>::min()) * -2.0);
980 Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -1.0);
981 Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(),
982 m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max),
983 m_currentBlock->appendNew<Value>(m_proc, GreaterThan, Origin(), arg, min));
984 outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32));
985 CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds);
986 trap->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
989 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin());
990 patchpoint->append(arg, ValueRep::SomeRegister);
991 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
992 jit.truncateFloatToUint32(params[1].fpr(), params[0].gpr());
994 patchpoint->effects = Effects::none();
1000 bool B3IRGenerator::addOp<OpType::I64TruncSF64>(ExpressionType arg, ExpressionType& result)
1002 Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -static_cast<double>(std::numeric_limits<int64_t>::min()));
1003 Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int64_t>::min()));
1004 Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(),
1005 m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max),
1006 m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, Origin(), arg, min));
1007 outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32));
1008 CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds);
1009 trap->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
1012 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin());
1013 patchpoint->append(arg, ValueRep::SomeRegister);
1014 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
1015 jit.truncateDoubleToInt64(params[1].fpr(), params[0].gpr());
1017 patchpoint->effects = Effects::none();
1018 result = patchpoint;
1023 bool B3IRGenerator::addOp<OpType::I64TruncUF64>(ExpressionType arg, ExpressionType& result)
1025 Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int64_t>::min()) * -2.0);
1026 Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -1.0);
1027 Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(),
1028 m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max),
1029 m_currentBlock->appendNew<Value>(m_proc, GreaterThan, Origin(), arg, min));
1030 outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32));
1031 CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds);
1032 trap->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
1038 // Since x86 doesn't have an instruction to convert floating points to unsigned integers, we at least try to do the smart thing if
1039 // the numbers are would be positive anyway as a signed integer. Since we cannot materialize constants into fprs we have b3 do it
1040 // so we can pool them if needed.
1041 constant = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<uint64_t>::max() - std::numeric_limits<int64_t>::max()));
1043 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin());
1044 patchpoint->append(arg, ValueRep::SomeRegister);
1046 patchpoint->append(constant, ValueRep::SomeRegister);
1047 patchpoint->numFPScratchRegisters = 1;
1049 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
1050 AllowMacroScratchRegisterUsage allowScratch(jit);
1051 FPRReg scratch = InvalidFPRReg;
1052 FPRReg constant = InvalidFPRReg;
1054 scratch = params.fpScratch(0);
1055 constant = params[2].fpr();
1057 jit.truncateDoubleToUint64(params[1].fpr(), params[0].gpr(), scratch, constant);
1059 patchpoint->effects = Effects::none();
1060 result = patchpoint;
1065 bool B3IRGenerator::addOp<OpType::I64TruncSF32>(ExpressionType arg, ExpressionType& result)
1067 Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -static_cast<float>(std::numeric_limits<int64_t>::min()));
1068 Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int64_t>::min()));
1069 Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(),
1070 m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max),
1071 m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, Origin(), arg, min));
1072 outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32));
1073 CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds);
1074 trap->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
1077 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin());
1078 patchpoint->append(arg, ValueRep::SomeRegister);
1079 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
1080 jit.truncateFloatToInt64(params[1].fpr(), params[0].gpr());
1082 patchpoint->effects = Effects::none();
1083 result = patchpoint;
1088 bool B3IRGenerator::addOp<OpType::I64TruncUF32>(ExpressionType arg, ExpressionType& result)
1090 Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int64_t>::min()) * -2.0);
1091 Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -1.0);
1092 Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(),
1093 m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max),
1094 m_currentBlock->appendNew<Value>(m_proc, GreaterThan, Origin(), arg, min));
1095 outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32));
1096 CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds);
1097 trap->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) {
1103 // Since x86 doesn't have an instruction to convert floating points to unsigned integers, we at least try to do the smart thing if
1104 // the numbers are would be positive anyway as a signed integer. Since we cannot materialize constants into fprs we have b3 do it
1105 // so we can pool them if needed.
1106 constant = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<uint64_t>::max() - std::numeric_limits<int64_t>::max()));
1108 PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin());
1109 patchpoint->append(arg, ValueRep::SomeRegister);
1111 patchpoint->append(constant, ValueRep::SomeRegister);
1112 patchpoint->numFPScratchRegisters = 1;
1114 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
1115 AllowMacroScratchRegisterUsage allowScratch(jit);
1116 FPRReg scratch = InvalidFPRReg;
1117 FPRReg constant = InvalidFPRReg;
1119 scratch = params.fpScratch(0);
1120 constant = params[2].fpr();
1122 jit.truncateFloatToUint64(params[1].fpr(), params[0].gpr(), scratch, constant);
1124 patchpoint->effects = Effects::none();
1125 result = patchpoint;
1129 } } // namespace JSC::Wasm
1131 #include "WasmB3IRGeneratorInlines.h"
1133 #endif // ENABLE(WEBASSEMBLY)