1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_COMPILER_WASM_COMPILER_H_
6 #define V8_COMPILER_WASM_COMPILER_H_
7 
8 #include <memory>
9 
10 // Clients of this interface shouldn't depend on lots of compiler internals.
11 // Do not include anything from src/compiler here!
12 #include "src/optimized-compilation-info.h"
13 #include "src/trap-handler/trap-handler.h"
14 #include "src/wasm/function-body-decoder.h"
15 #include "src/wasm/function-compiler.h"
16 #include "src/wasm/wasm-module.h"
17 #include "src/wasm/wasm-opcodes.h"
18 #include "src/wasm/wasm-result.h"
19 #include "src/zone/zone.h"
20 
21 namespace v8 {
22 namespace internal {
23 
24 class OptimizedCompilationJob;
25 
26 namespace compiler {
27 // Forward declarations for some compiler data structures.
28 class CallDescriptor;
29 class Graph;
30 class MachineGraph;
31 class Node;
32 class Operator;
33 class SourcePositionTable;
34 }  // namespace compiler
35 
36 namespace wasm {
37 struct DecodeStruct;
38 class SignatureMap;
39 // Expose {Node} and {Graph} opaquely as {wasm::TFNode} and {wasm::TFGraph}.
40 typedef compiler::Node TFNode;
41 typedef compiler::MachineGraph TFGraph;
42 class NativeModule;
43 class WasmCode;
44 }  // namespace wasm
45 
46 namespace compiler {
47 
48 // Information about Wasm compilation that needs to be plumbed through the
49 // different layers of the compiler.
50 class WasmCompilationData {
51  public:
52   explicit WasmCompilationData(
53       wasm::RuntimeExceptionSupport runtime_exception_support);
54 
55   void AddProtectedInstruction(uint32_t instr_offset, uint32_t landing_offset);
56 
57   std::unique_ptr<std::vector<trap_handler::ProtectedInstructionData>>
ReleaseProtectedInstructions()58   ReleaseProtectedInstructions() {
59     return std::move(protected_instructions_);
60   }
61 
runtime_exception_support()62   wasm::RuntimeExceptionSupport runtime_exception_support() const {
63     return runtime_exception_support_;
64   }
65 
66  private:
67   std::unique_ptr<std::vector<trap_handler::ProtectedInstructionData>>
68       protected_instructions_;
69 
70   // See ModuleEnv::runtime_exception_support_.
71   wasm::RuntimeExceptionSupport runtime_exception_support_;
72 
73   DISALLOW_COPY_AND_ASSIGN(WasmCompilationData);
74 };
75 
76 class TurbofanWasmCompilationUnit {
77  public:
78   explicit TurbofanWasmCompilationUnit(wasm::WasmCompilationUnit* wasm_unit);
79   ~TurbofanWasmCompilationUnit();
80 
81   SourcePositionTable* BuildGraphForWasmFunction(double* decode_ms);
82 
83   void ExecuteCompilation();
84 
85   wasm::WasmCode* FinishCompilation(wasm::ErrorThrower*);
86 
87  private:
88   wasm::WasmCompilationUnit* const wasm_unit_;
89   WasmCompilationData wasm_compilation_data_;
90   bool ok_ = true;
91   // The graph zone is deallocated at the end of {ExecuteCompilation} by virtue
92   // of it being zone allocated.
93   MachineGraph* mcgraph_ = nullptr;
94   // The compilation_zone_, info_, and job_ fields need to survive past
95   // {ExecuteCompilation}, onto {FinishCompilation} (which happens on the main
96   // thread).
97   std::unique_ptr<Zone> compilation_zone_;
98   std::unique_ptr<OptimizedCompilationInfo> info_;
99   std::unique_ptr<OptimizedCompilationJob> job_;
100   wasm::Result<wasm::DecodeStruct*> graph_construction_result_;
101 
102   DISALLOW_COPY_AND_ASSIGN(TurbofanWasmCompilationUnit);
103 };
104 
105 // Wraps a JS function, producing a code object that can be called from wasm.
106 Handle<Code> CompileWasmToJSWrapper(Isolate*, Handle<JSReceiver> target,
107                                     wasm::FunctionSig*, uint32_t index,
108                                     wasm::ModuleOrigin, wasm::UseTrapHandler);
109 
110 // Wraps a given wasm code object, producing a code object.
111 V8_EXPORT_PRIVATE Handle<Code> CompileJSToWasmWrapper(Isolate*,
112                                                       wasm::WasmModule*,
113                                                       Address call_target,
114                                                       uint32_t index,
115                                                       wasm::UseTrapHandler);
116 
117 // Compiles a stub that redirects a call to a wasm function to the wasm
118 // interpreter. It's ABI compatible with the compiled wasm function.
119 Handle<Code> CompileWasmInterpreterEntry(Isolate*, uint32_t func_index,
120                                          wasm::FunctionSig*);
121 
122 // Helper function to get the offset into a fixed array for a given {index}.
123 // TODO(titzer): access-builder.h is not accessible outside compiler. Move?
124 int FixedArrayOffsetMinusTag(uint32_t index);
125 
126 enum CWasmEntryParameters {
127   kCodeObject,
128   kWasmInstance,
129   kArgumentsBuffer,
130   // marker:
131   kNumParameters
132 };
133 
134 // Compiles a stub with JS linkage, taking parameters as described by
135 // {CWasmEntryParameters}. It loads the wasm parameters from the argument
136 // buffer and calls the wasm function given as first parameter.
137 Handle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig);
138 
139 // Values from the instance object are cached between WASM-level function calls.
140 // This struct allows the SSA environment handling this cache to be defined
141 // and manipulated in wasm-compiler.{h,cc} instead of inside the WASM decoder.
142 // (Note that currently, the globals base is immutable, so not cached here.)
143 struct WasmInstanceCacheNodes {
144   Node* mem_start;
145   Node* mem_size;
146   Node* mem_mask;
147 };
148 
149 // Abstracts details of building TurboFan graph nodes for wasm to separate
150 // the wasm decoder from the internal details of TurboFan.
151 typedef ZoneVector<Node*> NodeVector;
152 class WasmGraphBuilder {
153  public:
154   enum EnforceBoundsCheck : bool {
155     kNeedsBoundsCheck = true,
156     kCanOmitBoundsCheck = false
157   };
158   enum UseRetpoline : bool { kRetpoline = true, kNoRetpoline = false };
159 
160   WasmGraphBuilder(Isolate* isolate, wasm::ModuleEnv* env, Zone* zone,
161                    MachineGraph* mcgraph, Handle<Code> centry_stub,
162                    Handle<Oddball> anyref_null, wasm::FunctionSig* sig,
163                    compiler::SourcePositionTable* spt = nullptr);
164 
Buffer(size_t count)165   Node** Buffer(size_t count) {
166     if (count > cur_bufsize_) {
167       size_t new_size = count + cur_bufsize_ + 5;
168       cur_buffer_ =
169           reinterpret_cast<Node**>(zone_->New(new_size * sizeof(Node*)));
170       cur_bufsize_ = new_size;
171     }
172     return cur_buffer_;
173   }
174 
175   //-----------------------------------------------------------------------
176   // Operations independent of {control} or {effect}.
177   //-----------------------------------------------------------------------
178   Node* Error();
179   Node* Start(unsigned params);
180   Node* Param(unsigned index);
181   Node* Loop(Node* entry);
182   Node* Terminate(Node* effect, Node* control);
183   Node* Merge(unsigned count, Node** controls);
184   Node* Phi(wasm::ValueType type, unsigned count, Node** vals, Node* control);
185   Node* CreateOrMergeIntoPhi(MachineRepresentation rep, Node* merge,
186                              Node* tnode, Node* fnode);
187   Node* CreateOrMergeIntoEffectPhi(Node* merge, Node* tnode, Node* fnode);
188   Node* EffectPhi(unsigned count, Node** effects, Node* control);
189   Node* RefNull();
190   Node* Uint32Constant(uint32_t value);
191   Node* Int32Constant(int32_t value);
192   Node* Int64Constant(int64_t value);
193   Node* IntPtrConstant(intptr_t value);
194   Node* Float32Constant(float value);
195   Node* Float64Constant(double value);
196   Node* Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
197               wasm::WasmCodePosition position = wasm::kNoCodePosition);
198   Node* Unop(wasm::WasmOpcode opcode, Node* input,
199              wasm::WasmCodePosition position = wasm::kNoCodePosition);
200   Node* GrowMemory(Node* input);
201   Node* Throw(uint32_t tag, const wasm::WasmException* exception,
202               const Vector<Node*> values);
203   Node* Rethrow();
204   Node* ConvertExceptionTagToRuntimeId(uint32_t tag);
205   Node* GetExceptionRuntimeId();
206   Node** GetExceptionValues(const wasm::WasmException* except_decl);
207   bool IsPhiWithMerge(Node* phi, Node* merge);
208   bool ThrowsException(Node* node, Node** if_success, Node** if_exception);
209   void AppendToMerge(Node* merge, Node* from);
210   void AppendToPhi(Node* phi, Node* from);
211 
212   void StackCheck(wasm::WasmCodePosition position, Node** effect = nullptr,
213                   Node** control = nullptr);
214 
215   void PatchInStackCheckIfNeeded();
216 
217   //-----------------------------------------------------------------------
218   // Operations that read and/or write {control} and {effect}.
219   //-----------------------------------------------------------------------
220   Node* BranchNoHint(Node* cond, Node** true_node, Node** false_node);
221   Node* BranchExpectTrue(Node* cond, Node** true_node, Node** false_node);
222   Node* BranchExpectFalse(Node* cond, Node** true_node, Node** false_node);
223 
224   Node* TrapIfTrue(wasm::TrapReason reason, Node* cond,
225                    wasm::WasmCodePosition position);
226   Node* TrapIfFalse(wasm::TrapReason reason, Node* cond,
227                     wasm::WasmCodePosition position);
228   Node* TrapIfEq32(wasm::TrapReason reason, Node* node, int32_t val,
229                    wasm::WasmCodePosition position);
230   Node* ZeroCheck32(wasm::TrapReason reason, Node* node,
231                     wasm::WasmCodePosition position);
232   Node* TrapIfEq64(wasm::TrapReason reason, Node* node, int64_t val,
233                    wasm::WasmCodePosition position);
234   Node* ZeroCheck64(wasm::TrapReason reason, Node* node,
235                     wasm::WasmCodePosition position);
236 
237   Node* Switch(unsigned count, Node* key);
238   Node* IfValue(int32_t value, Node* sw);
239   Node* IfDefault(Node* sw);
240   Node* Return(unsigned count, Node** nodes);
241   template <typename... Nodes>
Return(Node * fst,Nodes * ...more)242   Node* Return(Node* fst, Nodes*... more) {
243     Node* arr[] = {fst, more...};
244     return Return(arraysize(arr), arr);
245   }
246   Node* ReturnVoid();
247   Node* Unreachable(wasm::WasmCodePosition position);
248 
249   Node* CallDirect(uint32_t index, Node** args, Node*** rets,
250                    wasm::WasmCodePosition position);
251   Node* CallIndirect(uint32_t index, Node** args, Node*** rets,
252                      wasm::WasmCodePosition position);
253 
254   Node* Invert(Node* node);
255 
256   //-----------------------------------------------------------------------
257   // Operations that concern the linear memory.
258   //-----------------------------------------------------------------------
259   Node* CurrentMemoryPages();
260   Node* GetGlobal(uint32_t index);
261   Node* SetGlobal(uint32_t index, Node* val);
262   Node* TraceMemoryOperation(bool is_store, MachineRepresentation, Node* index,
263                              uint32_t offset, wasm::WasmCodePosition);
264   Node* LoadMem(wasm::ValueType type, MachineType memtype, Node* index,
265                 uint32_t offset, uint32_t alignment,
266                 wasm::WasmCodePosition position);
267   Node* StoreMem(MachineRepresentation mem_rep, Node* index, uint32_t offset,
268                  uint32_t alignment, Node* val, wasm::WasmCodePosition position,
269                  wasm::ValueType type);
270   static void PrintDebugName(Node* node);
271 
set_instance_node(Node * instance_node)272   void set_instance_node(Node* instance_node) {
273     this->instance_node_ = instance_node;
274   }
275 
Control()276   Node* Control() { return *control_; }
Effect()277   Node* Effect() { return *effect_; }
278 
set_control_ptr(Node ** control)279   void set_control_ptr(Node** control) { this->control_ = control; }
280 
set_effect_ptr(Node ** effect)281   void set_effect_ptr(Node** effect) { this->effect_ = effect; }
282 
283   void GetGlobalBaseAndOffset(MachineType mem_type, const wasm::WasmGlobal&,
284                               Node** base_node, Node** offset_node);
285 
286   // Utilities to manipulate sets of instance cache nodes.
287   void InitInstanceCache(WasmInstanceCacheNodes* instance_cache);
288   void PrepareInstanceCacheForLoop(WasmInstanceCacheNodes* instance_cache,
289                                    Node* control);
290   void NewInstanceCacheMerge(WasmInstanceCacheNodes* to,
291                              WasmInstanceCacheNodes* from, Node* merge);
292   void MergeInstanceCacheInto(WasmInstanceCacheNodes* to,
293                               WasmInstanceCacheNodes* from, Node* merge);
294 
set_instance_cache(WasmInstanceCacheNodes * instance_cache)295   void set_instance_cache(WasmInstanceCacheNodes* instance_cache) {
296     this->instance_cache_ = instance_cache;
297   }
298 
GetFunctionSignature()299   wasm::FunctionSig* GetFunctionSignature() { return sig_; }
300 
301   void LowerInt64();
302 
303   void SimdScalarLoweringForTesting();
304 
305   void SetSourcePosition(Node* node, wasm::WasmCodePosition position);
306 
307   Node* S128Zero();
308   Node* S1x4Zero();
309   Node* S1x8Zero();
310   Node* S1x16Zero();
311 
312   Node* SimdOp(wasm::WasmOpcode opcode, Node* const* inputs);
313 
314   Node* SimdLaneOp(wasm::WasmOpcode opcode, uint8_t lane, Node* const* inputs);
315 
316   Node* SimdShiftOp(wasm::WasmOpcode opcode, uint8_t shift,
317                     Node* const* inputs);
318 
319   Node* Simd8x16ShuffleOp(const uint8_t shuffle[16], Node* const* inputs);
320 
321   Node* AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
322                  uint32_t alignment, uint32_t offset,
323                  wasm::WasmCodePosition position);
324 
has_simd()325   bool has_simd() const { return has_simd_; }
326 
module()327   const wasm::WasmModule* module() { return env_ ? env_->module : nullptr; }
328 
use_trap_handler()329   bool use_trap_handler() const { return env_ && env_->use_trap_handler; }
330 
mcgraph()331   MachineGraph* mcgraph() { return mcgraph_; }
332   Graph* graph();
333 
334  protected:
335   static const int kDefaultBufferSize = 16;
336 
337   Isolate* const isolate_;
338   Zone* const zone_;
339   MachineGraph* const mcgraph_;
340   wasm::ModuleEnv* const env_;
341 
342   Node** control_ = nullptr;
343   Node** effect_ = nullptr;
344   WasmInstanceCacheNodes* instance_cache_ = nullptr;
345 
346   Handle<Code> centry_stub_;
347   Handle<Oddball> anyref_null_;
348 
349   SetOncePointer<Node> instance_node_;
350   SetOncePointer<Node> globals_start_;
351   SetOncePointer<Node> imported_mutable_globals_;
352   SetOncePointer<Node> centry_stub_node_;
353   SetOncePointer<Node> anyref_null_node_;
354   SetOncePointer<Node> stack_check_builtin_code_node_;
355   const Operator* stack_check_call_operator_ = nullptr;
356 
357   Node** cur_buffer_;
358   size_t cur_bufsize_;
359   Node* def_buffer_[kDefaultBufferSize];
360   bool has_simd_ = false;
361   bool needs_stack_check_ = false;
362   const bool untrusted_code_mitigations_ = true;
363 
364   wasm::FunctionSig* const sig_;
365 
366   compiler::SourcePositionTable* const source_position_table_ = nullptr;
367 
368   Node* CEntryStub();
369   Node* NoContextConstant();
370 
371   Node* MemBuffer(uint32_t offset);
372   // BoundsCheckMem receives a uint32 {index} node and returns a ptrsize index.
373   Node* BoundsCheckMem(uint8_t access_size, Node* index, uint32_t offset,
374                        wasm::WasmCodePosition, EnforceBoundsCheck);
375   Node* Uint32ToUintptr(Node*);
376   const Operator* GetSafeLoadOperator(int offset, wasm::ValueType type);
377   const Operator* GetSafeStoreOperator(int offset, wasm::ValueType type);
378   Node* BuildChangeEndiannessStore(Node* node, MachineRepresentation rep,
379                                    wasm::ValueType wasmtype = wasm::kWasmStmt);
380   Node* BuildChangeEndiannessLoad(Node* node, MachineType type,
381                                   wasm::ValueType wasmtype = wasm::kWasmStmt);
382 
383   Node* MaskShiftCount32(Node* node);
384   Node* MaskShiftCount64(Node* node);
385 
386   template <typename... Args>
387   Node* BuildCCall(MachineSignature* sig, Node* function, Args... args);
388   Node* BuildWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
389                       wasm::WasmCodePosition position, Node* instance_node,
390                       UseRetpoline use_retpoline);
391   Node* BuildImportWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
392                             wasm::WasmCodePosition position, int func_index);
393 
394   Node* BuildF32CopySign(Node* left, Node* right);
395   Node* BuildF64CopySign(Node* left, Node* right);
396 
397   Node* BuildIntConvertFloat(Node* input, wasm::WasmCodePosition position,
398                              wasm::WasmOpcode);
399   Node* BuildI32Ctz(Node* input);
400   Node* BuildI32Popcnt(Node* input);
401   Node* BuildI64Ctz(Node* input);
402   Node* BuildI64Popcnt(Node* input);
403   Node* BuildBitCountingCall(Node* input, ExternalReference ref,
404                              MachineRepresentation input_type);
405 
406   Node* BuildCFuncInstruction(ExternalReference ref, MachineType type,
407                               Node* input0, Node* input1 = nullptr);
408   Node* BuildF32Trunc(Node* input);
409   Node* BuildF32Floor(Node* input);
410   Node* BuildF32Ceil(Node* input);
411   Node* BuildF32NearestInt(Node* input);
412   Node* BuildF64Trunc(Node* input);
413   Node* BuildF64Floor(Node* input);
414   Node* BuildF64Ceil(Node* input);
415   Node* BuildF64NearestInt(Node* input);
416   Node* BuildI32Rol(Node* left, Node* right);
417   Node* BuildI64Rol(Node* left, Node* right);
418 
419   Node* BuildF64Acos(Node* input);
420   Node* BuildF64Asin(Node* input);
421   Node* BuildF64Pow(Node* left, Node* right);
422   Node* BuildF64Mod(Node* left, Node* right);
423 
424   Node* BuildIntToFloatConversionInstruction(
425       Node* input, ExternalReference ref,
426       MachineRepresentation parameter_representation,
427       const MachineType result_type);
428   Node* BuildF32SConvertI64(Node* input);
429   Node* BuildF32UConvertI64(Node* input);
430   Node* BuildF64SConvertI64(Node* input);
431   Node* BuildF64UConvertI64(Node* input);
432 
433   Node* BuildCcallConvertFloat(Node* input, wasm::WasmCodePosition position,
434                                wasm::WasmOpcode opcode);
435 
436   Node* BuildI32DivS(Node* left, Node* right, wasm::WasmCodePosition position);
437   Node* BuildI32RemS(Node* left, Node* right, wasm::WasmCodePosition position);
438   Node* BuildI32DivU(Node* left, Node* right, wasm::WasmCodePosition position);
439   Node* BuildI32RemU(Node* left, Node* right, wasm::WasmCodePosition position);
440 
441   Node* BuildI64DivS(Node* left, Node* right, wasm::WasmCodePosition position);
442   Node* BuildI64RemS(Node* left, Node* right, wasm::WasmCodePosition position);
443   Node* BuildI64DivU(Node* left, Node* right, wasm::WasmCodePosition position);
444   Node* BuildI64RemU(Node* left, Node* right, wasm::WasmCodePosition position);
445   Node* BuildDiv64Call(Node* left, Node* right, ExternalReference ref,
446                        MachineType result_type, wasm::TrapReason trap_zero,
447                        wasm::WasmCodePosition position);
448 
449   Node* BuildChangeInt32ToSmi(Node* value);
450   Node* BuildChangeUint31ToSmi(Node* value);
451   Node* BuildSmiShiftBitsConstant();
452   Node* BuildChangeSmiToInt32(Node* value);
453 
454   Node* BuildLoadInstanceFromExportedFunction(Node* closure);
455 
456   // Asm.js specific functionality.
457   Node* BuildI32AsmjsSConvertF32(Node* input);
458   Node* BuildI32AsmjsSConvertF64(Node* input);
459   Node* BuildI32AsmjsUConvertF32(Node* input);
460   Node* BuildI32AsmjsUConvertF64(Node* input);
461   Node* BuildI32AsmjsDivS(Node* left, Node* right);
462   Node* BuildI32AsmjsRemS(Node* left, Node* right);
463   Node* BuildI32AsmjsDivU(Node* left, Node* right);
464   Node* BuildI32AsmjsRemU(Node* left, Node* right);
465   Node* BuildAsmjsLoadMem(MachineType type, Node* index);
466   Node* BuildAsmjsStoreMem(MachineType type, Node* index, Node* val);
467 
468   uint32_t GetExceptionEncodedSize(const wasm::WasmException* exception) const;
469   void BuildEncodeException32BitValue(uint32_t* index, Node* value);
470   Node* BuildDecodeException32BitValue(Node* const* values, uint32_t* index);
471 
Realloc(Node * const * buffer,size_t old_count,size_t new_count)472   Node** Realloc(Node* const* buffer, size_t old_count, size_t new_count) {
473     Node** buf = Buffer(new_count);
474     if (buf != buffer) memcpy(buf, buffer, old_count * sizeof(Node*));
475     return buf;
476   }
477 
SetNeedsStackCheck()478   void SetNeedsStackCheck() { needs_stack_check_ = true; }
479 
480   //-----------------------------------------------------------------------
481   // Operations involving the CEntry, a dependency we want to remove
482   // to get off the GC heap.
483   //-----------------------------------------------------------------------
484   Node* BuildCallToRuntime(Runtime::FunctionId f, Node** parameters,
485                            int parameter_count);
486 
487   Node* BuildCallToRuntimeWithContext(Runtime::FunctionId f, Node* js_context,
488                                       Node** parameters, int parameter_count);
489   Node* BuildCallToRuntimeWithContextFromJS(Runtime::FunctionId f,
490                                             Node* js_context,
491                                             Node* const* parameters,
492                                             int parameter_count);
493   Builtins::Name GetBuiltinIdForTrap(wasm::TrapReason reason);
494 };
495 
496 V8_EXPORT_PRIVATE CallDescriptor* GetWasmCallDescriptor(
497     Zone* zone, wasm::FunctionSig* signature,
498     WasmGraphBuilder::UseRetpoline use_retpoline =
499         WasmGraphBuilder::kNoRetpoline);
500 
501 V8_EXPORT_PRIVATE CallDescriptor* GetI32WasmCallDescriptor(
502     Zone* zone, CallDescriptor* call_descriptor);
503 
504 V8_EXPORT_PRIVATE CallDescriptor* GetI32WasmCallDescriptorForSimd(
505     Zone* zone, CallDescriptor* call_descriptor);
506 
507 }  // namespace compiler
508 }  // namespace internal
509 }  // namespace v8
510 
511 #endif  // V8_COMPILER_WASM_COMPILER_H_
512