Home | History | Annotate | Download | only in compiler
      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 #include "src/assembler.h"
      6 #include "src/base/lazy-instance.h"
      7 #include "src/macro-assembler.h"
      8 #include "src/objects-inl.h"
      9 #include "src/register-configuration.h"
     10 
     11 #include "src/wasm/wasm-module.h"
     12 
     13 #include "src/compiler/linkage.h"
     14 
     15 #include "src/zone/zone.h"
     16 
     17 namespace v8 {
     18 namespace internal {
     19 // TODO(titzer): this should not be in the WASM namespace.
     20 namespace wasm {
     21 
     22 using compiler::LocationSignature;
     23 using compiler::CallDescriptor;
     24 using compiler::LinkageLocation;
     25 
     26 namespace {
     27 
     28 MachineType MachineTypeFor(ValueType type) {
     29   switch (type) {
     30     case kWasmI32:
     31       return MachineType::Int32();
     32     case kWasmI64:
     33       return MachineType::Int64();
     34     case kWasmF64:
     35       return MachineType::Float64();
     36     case kWasmF32:
     37       return MachineType::Float32();
     38     case kWasmS128:
     39       return MachineType::Simd128();
     40     default:
     41       UNREACHABLE();
     42       return MachineType::AnyTagged();
     43   }
     44 }
     45 
     46 LinkageLocation regloc(Register reg, MachineType type) {
     47   return LinkageLocation::ForRegister(reg.code(), type);
     48 }
     49 
     50 LinkageLocation regloc(DoubleRegister reg, MachineType type) {
     51   return LinkageLocation::ForRegister(reg.code(), type);
     52 }
     53 
     54 LinkageLocation stackloc(int i, MachineType type) {
     55   return LinkageLocation::ForCallerFrameSlot(i, type);
     56 }
     57 
     58 
     59 #if V8_TARGET_ARCH_IA32
     60 // ===========================================================================
     61 // == ia32 ===================================================================
     62 // ===========================================================================
     63 #define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi
     64 #define GP_RETURN_REGISTERS eax, edx
     65 #define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
     66 #define FP_RETURN_REGISTERS xmm1, xmm2
     67 
     68 #elif V8_TARGET_ARCH_X64
     69 // ===========================================================================
     70 // == x64 ====================================================================
     71 // ===========================================================================
     72 #define GP_PARAM_REGISTERS rax, rdx, rcx, rbx, rsi, rdi
     73 #define GP_RETURN_REGISTERS rax, rdx
     74 #define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
     75 #define FP_RETURN_REGISTERS xmm1, xmm2
     76 
     77 #elif V8_TARGET_ARCH_X87
     78 // ===========================================================================
     79 // == x87 ====================================================================
     80 // ===========================================================================
     81 #define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi
     82 #define GP_RETURN_REGISTERS eax, edx
     83 #define FP_RETURN_REGISTERS stX_0
     84 
     85 #elif V8_TARGET_ARCH_ARM
     86 // ===========================================================================
     87 // == arm ====================================================================
     88 // ===========================================================================
     89 #define GP_PARAM_REGISTERS r0, r1, r2, r3
     90 #define GP_RETURN_REGISTERS r0, r1
     91 #define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7
     92 #define FP_RETURN_REGISTERS d0, d1
     93 
     94 #elif V8_TARGET_ARCH_ARM64
     95 // ===========================================================================
     96 // == arm64 ====================================================================
     97 // ===========================================================================
     98 #define GP_PARAM_REGISTERS x0, x1, x2, x3, x4, x5, x6, x7
     99 #define GP_RETURN_REGISTERS x0, x1
    100 #define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7
    101 #define FP_RETURN_REGISTERS d0, d1
    102 
    103 #elif V8_TARGET_ARCH_MIPS
    104 // ===========================================================================
    105 // == mips ===================================================================
    106 // ===========================================================================
    107 #define GP_PARAM_REGISTERS a0, a1, a2, a3
    108 #define GP_RETURN_REGISTERS v0, v1
    109 #define FP_PARAM_REGISTERS f2, f4, f6, f8, f10, f12, f14
    110 #define FP_RETURN_REGISTERS f2, f4
    111 
    112 #elif V8_TARGET_ARCH_MIPS64
    113 // ===========================================================================
    114 // == mips64 =================================================================
    115 // ===========================================================================
    116 #define GP_PARAM_REGISTERS a0, a1, a2, a3, a4, a5, a6, a7
    117 #define GP_RETURN_REGISTERS v0, v1
    118 #define FP_PARAM_REGISTERS f2, f4, f6, f8, f10, f12, f14
    119 #define FP_RETURN_REGISTERS f2, f4
    120 
    121 #elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
    122 // ===========================================================================
    123 // == ppc & ppc64 ============================================================
    124 // ===========================================================================
    125 #define GP_PARAM_REGISTERS r3, r4, r5, r6, r7, r8, r9, r10
    126 #define GP_RETURN_REGISTERS r3, r4
    127 #define FP_PARAM_REGISTERS d1, d2, d3, d4, d5, d6, d7, d8
    128 #define FP_RETURN_REGISTERS d1, d2
    129 
    130 #elif V8_TARGET_ARCH_S390X
    131 // ===========================================================================
    132 // == s390x ==================================================================
    133 // ===========================================================================
    134 #define GP_PARAM_REGISTERS r2, r3, r4, r5, r6
    135 #define GP_RETURN_REGISTERS r2, r3
    136 #define FP_PARAM_REGISTERS d0, d2, d4, d6
    137 #define FP_RETURN_REGISTERS d0, d2, d4, d6
    138 
    139 #elif V8_TARGET_ARCH_S390
    140 // ===========================================================================
    141 // == s390 ===================================================================
    142 // ===========================================================================
    143 #define GP_PARAM_REGISTERS r2, r3, r4, r5, r6
    144 #define GP_RETURN_REGISTERS r2, r3
    145 #define FP_PARAM_REGISTERS d0, d2
    146 #define FP_RETURN_REGISTERS d0, d2
    147 
    148 #else
    149 // ===========================================================================
    150 // == unknown ================================================================
    151 // ===========================================================================
    152 // Don't define anything. We'll just always use the stack.
    153 #endif
    154 
    155 
    156 // Helper for allocating either an GP or FP reg, or the next stack slot.
    157 struct Allocator {
    158   Allocator(const Register* gp, int gpc, const DoubleRegister* fp, int fpc)
    159       : gp_count(gpc),
    160         gp_offset(0),
    161         gp_regs(gp),
    162         fp_count(fpc),
    163         fp_offset(0),
    164         fp_regs(fp),
    165         stack_offset(0) {}
    166 
    167   int gp_count;
    168   int gp_offset;
    169   const Register* gp_regs;
    170 
    171   int fp_count;
    172   int fp_offset;
    173   const DoubleRegister* fp_regs;
    174 
    175   int stack_offset;
    176 
    177   LinkageLocation Next(ValueType type) {
    178     if (IsFloatingPoint(type)) {
    179       // Allocate a floating point register/stack location.
    180       if (fp_offset < fp_count) {
    181         DoubleRegister reg = fp_regs[fp_offset++];
    182 #if V8_TARGET_ARCH_ARM
    183         // Allocate floats using a double register, but modify the code to
    184         // reflect how ARM FP registers alias.
    185         // TODO(bbudge) Modify wasm linkage to allow use of all float regs.
    186         if (type == kWasmF32) {
    187           int float_reg_code = reg.code() * 2;
    188           DCHECK(float_reg_code < RegisterConfiguration::kMaxFPRegisters);
    189           return regloc(DoubleRegister::from_code(float_reg_code),
    190                         MachineTypeFor(type));
    191         }
    192 #endif
    193         return regloc(reg, MachineTypeFor(type));
    194       } else {
    195         int offset = -1 - stack_offset;
    196         stack_offset += Words(type);
    197         return stackloc(offset, MachineTypeFor(type));
    198       }
    199     } else {
    200       // Allocate a general purpose register/stack location.
    201       if (gp_offset < gp_count) {
    202         return regloc(gp_regs[gp_offset++], MachineTypeFor(type));
    203       } else {
    204         int offset = -1 - stack_offset;
    205         stack_offset += Words(type);
    206         return stackloc(offset, MachineTypeFor(type));
    207       }
    208     }
    209   }
    210   bool IsFloatingPoint(ValueType type) {
    211     return type == kWasmF32 || type == kWasmF64;
    212   }
    213   int Words(ValueType type) {
    214     if (kPointerSize < 8 && (type == kWasmI64 || type == kWasmF64)) {
    215       return 2;
    216     }
    217     return 1;
    218   }
    219 };
    220 }  // namespace
    221 
    222 struct ParameterRegistersCreateTrait {
    223   static void Construct(Allocator* allocated_ptr) {
    224 #ifdef GP_PARAM_REGISTERS
    225     static const Register kGPParamRegisters[] = {GP_PARAM_REGISTERS};
    226     static const int kGPParamRegistersCount =
    227         static_cast<int>(arraysize(kGPParamRegisters));
    228 #else
    229     static const Register* kGPParamRegisters = nullptr;
    230     static const int kGPParamRegistersCount = 0;
    231 #endif
    232 
    233 #ifdef FP_PARAM_REGISTERS
    234     static const DoubleRegister kFPParamRegisters[] = {FP_PARAM_REGISTERS};
    235     static const int kFPParamRegistersCount =
    236         static_cast<int>(arraysize(kFPParamRegisters));
    237 #else
    238     static const DoubleRegister* kFPParamRegisters = nullptr;
    239     static const int kFPParamRegistersCount = 0;
    240 #endif
    241 
    242     new (allocated_ptr) Allocator(kGPParamRegisters, kGPParamRegistersCount,
    243                                   kFPParamRegisters, kFPParamRegistersCount);
    244   }
    245 };
    246 
    247 static base::LazyInstance<Allocator, ParameterRegistersCreateTrait>::type
    248     parameter_registers = LAZY_INSTANCE_INITIALIZER;
    249 
    250 struct ReturnRegistersCreateTrait {
    251   static void Construct(Allocator* allocated_ptr) {
    252 #ifdef GP_RETURN_REGISTERS
    253     static const Register kGPReturnRegisters[] = {GP_RETURN_REGISTERS};
    254     static const int kGPReturnRegistersCount =
    255         static_cast<int>(arraysize(kGPReturnRegisters));
    256 #else
    257     static const Register* kGPReturnRegisters = nullptr;
    258     static const int kGPReturnRegistersCount = 0;
    259 #endif
    260 
    261 #ifdef FP_RETURN_REGISTERS
    262     static const DoubleRegister kFPReturnRegisters[] = {FP_RETURN_REGISTERS};
    263     static const int kFPReturnRegistersCount =
    264         static_cast<int>(arraysize(kFPReturnRegisters));
    265 #else
    266     static const DoubleRegister* kFPReturnRegisters = nullptr;
    267     static const int kFPReturnRegistersCount = 0;
    268 #endif
    269 
    270     new (allocated_ptr) Allocator(kGPReturnRegisters, kGPReturnRegistersCount,
    271                                   kFPReturnRegisters, kFPReturnRegistersCount);
    272   }
    273 };
    274 
    275 static base::LazyInstance<Allocator, ReturnRegistersCreateTrait>::type
    276     return_registers = LAZY_INSTANCE_INITIALIZER;
    277 
    278 // General code uses the above configuration data.
    279 CallDescriptor* ModuleEnv::GetWasmCallDescriptor(Zone* zone,
    280                                                  FunctionSig* fsig) {
    281   LocationSignature::Builder locations(zone, fsig->return_count(),
    282                                        fsig->parameter_count());
    283 
    284   Allocator rets = return_registers.Get();
    285 
    286   // Add return location(s).
    287   const int return_count = static_cast<int>(locations.return_count_);
    288   for (int i = 0; i < return_count; i++) {
    289     ValueType ret = fsig->GetReturn(i);
    290     locations.AddReturn(rets.Next(ret));
    291   }
    292 
    293   Allocator params = parameter_registers.Get();
    294 
    295   // Add register and/or stack parameter(s).
    296   const int parameter_count = static_cast<int>(fsig->parameter_count());
    297   for (int i = 0; i < parameter_count; i++) {
    298     ValueType param = fsig->GetParam(i);
    299     locations.AddParam(params.Next(param));
    300   }
    301 
    302   const RegList kCalleeSaveRegisters = 0;
    303   const RegList kCalleeSaveFPRegisters = 0;
    304 
    305   // The target for WASM calls is always a code object.
    306   MachineType target_type = MachineType::AnyTagged();
    307   LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
    308 
    309   return new (zone) CallDescriptor(       // --
    310       CallDescriptor::kCallCodeObject,    // kind
    311       target_type,                        // target MachineType
    312       target_loc,                         // target location
    313       locations.Build(),                  // location_sig
    314       params.stack_offset,                // stack_parameter_count
    315       compiler::Operator::kNoProperties,  // properties
    316       kCalleeSaveRegisters,               // callee-saved registers
    317       kCalleeSaveFPRegisters,             // callee-saved fp regs
    318       CallDescriptor::kUseNativeStack,    // flags
    319       "wasm-call");
    320 }
    321 
    322 CallDescriptor* ReplaceTypeInCallDescriptorWith(
    323     Zone* zone, CallDescriptor* descriptor, size_t num_replacements,
    324     MachineType input_type, MachineRepresentation output_type) {
    325   size_t parameter_count = descriptor->ParameterCount();
    326   size_t return_count = descriptor->ReturnCount();
    327   for (size_t i = 0; i < descriptor->ParameterCount(); i++) {
    328     if (descriptor->GetParameterType(i) == input_type) {
    329       parameter_count += num_replacements - 1;
    330     }
    331   }
    332   for (size_t i = 0; i < descriptor->ReturnCount(); i++) {
    333     if (descriptor->GetReturnType(i) == input_type) {
    334       return_count += num_replacements - 1;
    335     }
    336   }
    337   if (parameter_count == descriptor->ParameterCount() &&
    338       return_count == descriptor->ReturnCount()) {
    339     return descriptor;
    340   }
    341 
    342   LocationSignature::Builder locations(zone, return_count, parameter_count);
    343 
    344   Allocator rets = return_registers.Get();
    345 
    346   for (size_t i = 0; i < descriptor->ReturnCount(); i++) {
    347     if (descriptor->GetReturnType(i) == input_type) {
    348       for (size_t j = 0; j < num_replacements; j++) {
    349         locations.AddReturn(rets.Next(output_type));
    350       }
    351     } else {
    352       locations.AddReturn(
    353           rets.Next(descriptor->GetReturnType(i).representation()));
    354     }
    355   }
    356 
    357   Allocator params = parameter_registers.Get();
    358 
    359   for (size_t i = 0; i < descriptor->ParameterCount(); i++) {
    360     if (descriptor->GetParameterType(i) == input_type) {
    361       for (size_t j = 0; j < num_replacements; j++) {
    362         locations.AddParam(params.Next(output_type));
    363       }
    364     } else {
    365       locations.AddParam(
    366           params.Next(descriptor->GetParameterType(i).representation()));
    367     }
    368   }
    369 
    370   return new (zone) CallDescriptor(          // --
    371       descriptor->kind(),                    // kind
    372       descriptor->GetInputType(0),           // target MachineType
    373       descriptor->GetInputLocation(0),       // target location
    374       locations.Build(),                     // location_sig
    375       params.stack_offset,                   // stack_parameter_count
    376       descriptor->properties(),              // properties
    377       descriptor->CalleeSavedRegisters(),    // callee-saved registers
    378       descriptor->CalleeSavedFPRegisters(),  // callee-saved fp regs
    379       descriptor->flags(),                   // flags
    380       descriptor->debug_name());
    381 }
    382 
    383 CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor(
    384     Zone* zone, CallDescriptor* descriptor) {
    385   return ReplaceTypeInCallDescriptorWith(zone, descriptor, 2,
    386                                          MachineType::Int64(),
    387                                          MachineRepresentation::kWord32);
    388 }
    389 
    390 CallDescriptor* ModuleEnv::GetI32WasmCallDescriptorForSimd(
    391     Zone* zone, CallDescriptor* descriptor) {
    392   return ReplaceTypeInCallDescriptorWith(zone, descriptor, 4,
    393                                          MachineType::Simd128(),
    394                                          MachineRepresentation::kWord32);
    395 }
    396 
    397 }  // namespace wasm
    398 }  // namespace internal
    399 }  // namespace v8
    400