Templatize CodePtr/Refs/FunctionPtrs with PtrTags.
[WebKit-https.git] / Source / JavaScriptCore / wasm / WasmCallingConvention.h
1 /*
2  * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #pragma once
27
28 #if ENABLE(WEBASSEMBLY)
29
30 #include "AllowMacroScratchRegisterUsage.h"
31 #include "B3ArgumentRegValue.h"
32 #include "B3BasicBlock.h"
33 #include "B3Const64Value.h"
34 #include "B3ConstrainedValue.h"
35 #include "B3MemoryValue.h"
36 #include "B3PatchpointValue.h"
37 #include "B3Procedure.h"
38 #include "B3StackmapGenerationParams.h"
39 #include "CallFrame.h"
40 #include "LinkBuffer.h"
41 #include "RegisterSet.h"
42 #include "WasmFormat.h"
43 #include "WasmSignature.h"
44
45 namespace JSC { namespace Wasm {
46
47 typedef unsigned (*NextOffset)(unsigned currentOffset, B3::Type type);
48
49 template<unsigned headerSize, NextOffset updateOffset>
50 class CallingConvention {
51 public:
52     CallingConvention(Vector<Reg>&& gprArgs, Vector<Reg>&& fprArgs, RegisterSet&& calleeSaveRegisters)
53         : m_gprArgs(gprArgs)
54         , m_fprArgs(fprArgs)
55         , m_calleeSaveRegisters(calleeSaveRegisters)
56     {
57     }
58
59 private:
60     B3::ValueRep marshallArgumentImpl(Vector<Reg> regArgs, B3::Type type, size_t& count, size_t& stackOffset) const
61     {
62         if (count < regArgs.size())
63             return B3::ValueRep::reg(regArgs[count++]);
64
65         count++;
66         B3::ValueRep result = B3::ValueRep::stackArgument(stackOffset);
67         stackOffset = updateOffset(stackOffset, type);
68         return result;
69     }
70
71     B3::ValueRep marshallArgument(B3::Type type, size_t& gpArgumentCount, size_t& fpArgumentCount, size_t& stackOffset) const
72     {
73         switch (type) {
74         case B3::Int32:
75         case B3::Int64:
76             return marshallArgumentImpl(m_gprArgs, type, gpArgumentCount, stackOffset);
77         case B3::Float:
78         case B3::Double:
79             return marshallArgumentImpl(m_fprArgs, type, fpArgumentCount, stackOffset);
80         case B3::Void:
81             break;
82         }
83         RELEASE_ASSERT_NOT_REACHED();
84     }
85
86 public:
87     static unsigned headerSizeInBytes() { return headerSize; }
88     void setupFrameInPrologue(CodeLocationDataLabelPtr<WasmEntryPtrTag>* calleeMoveLocation, B3::Procedure& proc, B3::Origin origin, B3::BasicBlock* block) const
89     {
90         static_assert(CallFrameSlot::callee * sizeof(Register) < headerSize, "We rely on this here for now.");
91         static_assert(CallFrameSlot::codeBlock * sizeof(Register) < headerSize, "We rely on this here for now.");
92
93         B3::PatchpointValue* getCalleePatchpoint = block->appendNew<B3::PatchpointValue>(proc, B3::Int64, origin);
94         getCalleePatchpoint->resultConstraint = B3::ValueRep::SomeRegister;
95         getCalleePatchpoint->effects = B3::Effects::none();
96         getCalleePatchpoint->setGenerator(
97             [=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
98                 GPRReg result = params[0].gpr();
99                 MacroAssembler::DataLabelPtr moveLocation = jit.moveWithPatch(MacroAssembler::TrustedImmPtr(nullptr), result);
100                 jit.addLinkTask([calleeMoveLocation, moveLocation] (LinkBuffer& linkBuffer) {
101                     *calleeMoveLocation = linkBuffer.locationOf<WasmEntryPtrTag>(moveLocation);
102                 });
103             });
104
105         B3::Value* framePointer = block->appendNew<B3::Value>(proc, B3::FramePointer, origin);
106         B3::Value* offsetOfCallee = block->appendNew<B3::Const64Value>(proc, origin, CallFrameSlot::callee * sizeof(Register));
107         block->appendNew<B3::MemoryValue>(proc, B3::Store, origin,
108             getCalleePatchpoint,
109             block->appendNew<B3::Value>(proc, B3::Add, origin, framePointer, offsetOfCallee));
110
111         // FIXME: We shouldn't have to store zero into the CodeBlock* spot in the call frame,
112         // but there are places that interpret non-null CodeBlock slot to mean a valid CodeBlock.
113         // When doing unwinding, we'll need to verify that the entire runtime is OK with a non-null
114         // CodeBlock not implying that the CodeBlock is valid.
115         // https://bugs.webkit.org/show_bug.cgi?id=165321
116         B3::Value* offsetOfCodeBlock = block->appendNew<B3::Const64Value>(proc, origin, CallFrameSlot::codeBlock * sizeof(Register));
117         block->appendNew<B3::MemoryValue>(proc, B3::Store, origin,
118             block->appendNew<B3::Const64Value>(proc, origin, 0),
119             block->appendNew<B3::Value>(proc, B3::Add, origin, framePointer, offsetOfCodeBlock));
120     }
121
122     template<typename Functor>
123     void loadArguments(const Signature& signature, B3::Procedure& proc, B3::BasicBlock* block, B3::Origin origin, const Functor& functor) const
124     {
125         B3::Value* framePointer = block->appendNew<B3::Value>(proc, B3::FramePointer, origin);
126
127         size_t gpArgumentCount = 0;
128         size_t fpArgumentCount = 0;
129         size_t stackOffset = headerSize;
130
131         for (size_t i = 0; i < signature.argumentCount(); ++i) {
132             B3::Type type = toB3Type(signature.argument(i));
133             B3::Value* argument;
134             B3::ValueRep rep = marshallArgument(type, gpArgumentCount, fpArgumentCount, stackOffset);
135             if (rep.isReg()) {
136                 argument = block->appendNew<B3::ArgumentRegValue>(proc, origin, rep.reg());
137                 if (type == B3::Int32 || type == B3::Float)
138                     argument = block->appendNew<B3::Value>(proc, B3::Trunc, origin, argument);
139             } else {
140                 ASSERT(rep.isStackArgument());
141                 B3::Value* address = block->appendNew<B3::Value>(proc, B3::Add, origin, framePointer,
142                     block->appendNew<B3::Const64Value>(proc, origin, rep.offsetFromSP()));
143                 argument = block->appendNew<B3::MemoryValue>(proc, B3::Load, type, origin, address);
144             }
145             functor(argument, i);
146         }
147     }
148
149     // It's expected that the pachpointFunctor sets the generator for the call operation.
150     template<typename Functor>
151     B3::Value* setupCall(B3::Procedure& proc, B3::BasicBlock* block, B3::Origin origin, const Vector<B3::Value*>& arguments, B3::Type returnType, const Functor& patchpointFunctor) const
152     {
153         size_t gpArgumentCount = 0;
154         size_t fpArgumentCount = 0;
155         size_t stackOffset = headerSize - sizeof(CallerFrameAndPC);
156
157         Vector<B3::ConstrainedValue> constrainedArguments;
158         for (B3::Value* argument : arguments) {
159             B3::ValueRep rep = marshallArgument(argument->type(), gpArgumentCount, fpArgumentCount, stackOffset);
160             constrainedArguments.append(B3::ConstrainedValue(argument, rep));
161         }
162
163         proc.requestCallArgAreaSizeInBytes(WTF::roundUpToMultipleOf(stackAlignmentBytes(), stackOffset));
164
165         B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(proc, returnType, origin);
166         patchpoint->clobberEarly(RegisterSet::macroScratchRegisters());
167         patchpoint->clobberLate(RegisterSet::volatileRegistersForJSCall());
168         patchpointFunctor(patchpoint);
169         patchpoint->appendVector(constrainedArguments);
170
171         switch (returnType) {
172         case B3::Void:
173             return nullptr;
174         case B3::Float:
175         case B3::Double:
176             patchpoint->resultConstraint = B3::ValueRep::reg(FPRInfo::returnValueFPR);
177             break;
178         case B3::Int32:
179         case B3::Int64:
180             patchpoint->resultConstraint = B3::ValueRep::reg(GPRInfo::returnValueGPR);
181             break;
182         }
183         return patchpoint;
184     }
185
186     const Vector<Reg> m_gprArgs;
187     const Vector<Reg> m_fprArgs;
188     const RegisterSet m_calleeSaveRegisters;
189     const RegisterSet m_callerSaveRegisters;
190 };
191
192 inline unsigned nextJSCOffset(unsigned currentOffset, B3::Type)
193 {
194     return currentOffset + sizeof(Register);
195 }
196
197 constexpr unsigned jscHeaderSize = ExecState::headerSizeInRegisters * sizeof(Register);
198 typedef CallingConvention<jscHeaderSize, nextJSCOffset> JSCCallingConvention;
199
200 typedef JSCCallingConvention WasmCallingConvention;
201
202 const JSCCallingConvention& jscCallingConvention();
203 const WasmCallingConvention& wasmCallingConvention();
204
205 } } // namespace JSC::Wasm
206
207 #endif // ENABLE(WEBASSEMBLY)