diff options
Diffstat (limited to 'deps/v8/src/wasm/baseline/liftoff-compiler.cc')
-rw-r--r-- | deps/v8/src/wasm/baseline/liftoff-compiler.cc | 740 |
1 files changed, 466 insertions, 274 deletions
diff --git a/deps/v8/src/wasm/baseline/liftoff-compiler.cc b/deps/v8/src/wasm/baseline/liftoff-compiler.cc index c6adb90f82..914fc2aafa 100644 --- a/deps/v8/src/wasm/baseline/liftoff-compiler.cc +++ b/deps/v8/src/wasm/baseline/liftoff-compiler.cc @@ -19,6 +19,8 @@ namespace v8 { namespace internal { namespace wasm { +using WasmCompilationData = compiler::WasmCompilationData; + constexpr auto kRegister = LiftoffAssembler::VarState::kRegister; constexpr auto KIntConst = LiftoffAssembler::VarState::KIntConst; constexpr auto kStack = LiftoffAssembler::VarState::kStack; @@ -32,6 +34,16 @@ namespace { if (FLAG_trace_liftoff) PrintF("[liftoff] " __VA_ARGS__); \ } while (false) +#define WASM_INSTANCE_OBJECT_OFFSET(name) \ + (WasmInstanceObject::k##name##Offset - kHeapObjectTag) + +#define LOAD_INSTANCE_FIELD(dst, name, type) \ + __ LoadFromInstance(dst.gp(), WASM_INSTANCE_OBJECT_OFFSET(name), \ + LoadType(type).size()); + +constexpr LoadType::LoadTypeValue kPointerLoadType = + kPointerSize == 8 ? LoadType::kI64Load : LoadType::kI32Load; + #if V8_TARGET_ARCH_ARM64 // On ARM64, the Assembler keeps track of pointers to Labels to resolve // branches to distant targets. Moving labels would confuse the Assembler, @@ -64,16 +76,6 @@ class MovableLabel { }; #endif -wasm::WasmValue WasmPtrValue(uintptr_t ptr) { - using int_t = std::conditional<kPointerSize == 8, uint64_t, uint32_t>::type; - static_assert(sizeof(int_t) == sizeof(uintptr_t), "weird uintptr_t"); - return wasm::WasmValue(static_cast<int_t>(ptr)); -} - -wasm::WasmValue WasmPtrValue(void* ptr) { - return WasmPtrValue(reinterpret_cast<uintptr_t>(ptr)); -} - compiler::CallDescriptor* GetLoweredCallDescriptor( Zone* zone, compiler::CallDescriptor* call_desc) { return kPointerSize == 4 ? compiler::GetI32WasmCallDescriptor(zone, call_desc) @@ -131,11 +133,10 @@ class LiftoffCompiler { LiftoffCompiler(LiftoffAssembler* liftoff_asm, compiler::CallDescriptor* call_descriptor, compiler::ModuleEnv* env, - compiler::RuntimeExceptionSupport runtime_exception_support, SourcePositionTableBuilder* source_position_table_builder, - std::vector<trap_handler::ProtectedInstructionData>* - protected_instructions, - Zone* compilation_zone, std::unique_ptr<Zone>* codegen_zone) + WasmCompilationData* wasm_compilation_data, + Zone* compilation_zone, std::unique_ptr<Zone>* codegen_zone, + WasmCode* const* code_table_entry) : asm_(liftoff_asm), descriptor_( GetLoweredCallDescriptor(compilation_zone, call_descriptor)), @@ -145,12 +146,12 @@ class LiftoffCompiler { ? env_->module->maximum_pages : wasm::kV8MaxWasmMemoryPages} * wasm::kWasmPageSize), - runtime_exception_support_(runtime_exception_support), source_position_table_builder_(source_position_table_builder), - protected_instructions_(protected_instructions), + wasm_compilation_data_(wasm_compilation_data), compilation_zone_(compilation_zone), codegen_zone_(codegen_zone), - safepoint_table_builder_(compilation_zone_) {} + safepoint_table_builder_(compilation_zone_), + code_table_entry_(code_table_entry) {} ~LiftoffCompiler() { BindUnboundLabels(nullptr); } @@ -217,7 +218,7 @@ class LiftoffCompiler { // Returns the number of inputs processed (1 or 2). uint32_t ProcessParameter(ValueType type, uint32_t input_idx) { - const int num_lowered_params = 1 + (kNeedI64RegPair && type == kWasmI64); + const int num_lowered_params = 1 + needs_reg_pair(type); // Initialize to anything, will be set in the loop and used afterwards. LiftoffRegister reg = LiftoffRegister::from_code(kGpReg, 0); RegClass rc = num_lowered_params == 1 ? reg_class_for(type) : kGpReg; @@ -252,7 +253,8 @@ class LiftoffCompiler { __ LoadCallerFrameSlot(in_reg, -param_loc.AsCallerFrameSlot(), lowered_type); } - reg = pair_idx == 0 ? in_reg : LiftoffRegister::ForPair(reg, in_reg); + reg = pair_idx == 0 ? in_reg + : LiftoffRegister::ForPair(reg.gp(), in_reg.gp()); pinned.set(reg); } __ PushRegister(type, reg); @@ -260,7 +262,10 @@ class LiftoffCompiler { } void StackCheck(wasm::WasmCodePosition position) { - if (FLAG_wasm_no_stack_checks || !runtime_exception_support_) return; + if (FLAG_wasm_no_stack_checks || + !wasm_compilation_data_->runtime_exception_support()) { + return; + } out_of_line_code_.push_back( OutOfLineCode::StackCheck(position, __ cache_state()->used_registers)); OutOfLineCode& ool = out_of_line_code_.back(); @@ -268,6 +273,49 @@ class LiftoffCompiler { if (ool.continuation) __ bind(ool.continuation.get()); } + // Inserts a check whether the optimized version of this code already exists. + // If so, it redirects execution to the optimized code. + void JumpToOptimizedCodeIfExisting() { + // Check whether we have an optimized function before + // continuing to execute the Liftoff-compiled code. + // TODO(clemensh): Reduce number of temporary registers. + LiftoffRegList pinned; + LiftoffRegister wasm_code_addr = + pinned.set(__ GetUnusedRegister(kGpReg, pinned)); + LiftoffRegister target_code_addr = + pinned.set(__ GetUnusedRegister(kGpReg, pinned)); + LiftoffRegister code_start_address = + pinned.set(__ GetUnusedRegister(kGpReg, pinned)); + + // Get the current code's target address ({instructions_.start()}). + __ ComputeCodeStartAddress(code_start_address.gp()); + + static LoadType kPointerLoadType = + LoadType::ForValueType(LiftoffAssembler::kWasmIntPtr); + using int_t = std::conditional<kPointerSize == 8, uint64_t, uint32_t>::type; + static_assert(sizeof(int_t) == sizeof(uintptr_t), "weird uintptr_t"); + // Get the address of the WasmCode* currently stored in the code table. + __ LoadConstant(target_code_addr, + WasmValue(reinterpret_cast<int_t>(code_table_entry_)), + RelocInfo::WASM_CODE_TABLE_ENTRY); + // Load the corresponding WasmCode*. + __ Load(wasm_code_addr, target_code_addr.gp(), Register::no_reg(), 0, + kPointerLoadType, pinned); + // Load its target address ({instuctions_.start()}). + __ Load(target_code_addr, wasm_code_addr.gp(), Register::no_reg(), + WasmCode::kInstructionStartOffset, kPointerLoadType, pinned); + + // If the current code's target address is the same as the + // target address of the stored WasmCode, then continue executing, otherwise + // jump to the updated WasmCode. + Label cont; + __ emit_cond_jump(kEqual, &cont, LiftoffAssembler::kWasmIntPtr, + target_code_addr.gp(), code_start_address.gp()); + __ LeaveFrame(StackFrame::WASM_COMPILED); + __ emit_jump(target_code_addr.gp()); + __ bind(&cont); + } + void StartFunctionBody(Decoder* decoder, Control* block) { __ EnterFrame(StackFrame::WASM_COMPILED); __ set_has_frame(true); @@ -279,24 +327,24 @@ class LiftoffCompiler { // finish compilation without errors even if we hit unimplemented // LiftoffAssembler methods. if (DidAssemblerBailout(decoder)) return; - // Parameter 0 is the wasm context. + // Parameter 0 is the instance parameter. uint32_t num_params = static_cast<uint32_t>(decoder->sig_->parameter_count()); for (uint32_t i = 0; i < __ num_locals(); ++i) { if (!CheckSupportedType(decoder, kTypes_ilfd, __ local_type(i), "param")) return; } - // Input 0 is the call target, the context is at 1. - constexpr int kContextParameterIndex = 1; - // Store the context parameter to a special stack slot. - compiler::LinkageLocation context_loc = - descriptor_->GetInputLocation(kContextParameterIndex); - DCHECK(context_loc.IsRegister()); - DCHECK(!context_loc.IsAnyRegister()); - Register context_reg = Register::from_code(context_loc.AsRegister()); - __ SpillContext(context_reg); - // Input 0 is the code target, 1 is the context. First parameter at 2. - uint32_t input_idx = kContextParameterIndex + 1; + // Input 0 is the call target, the instance is at 1. + constexpr int kInstanceParameterIndex = 1; + // Store the instance parameter to a special stack slot. + compiler::LinkageLocation instance_loc = + descriptor_->GetInputLocation(kInstanceParameterIndex); + DCHECK(instance_loc.IsRegister()); + DCHECK(!instance_loc.IsAnyRegister()); + Register instance_reg = Register::from_code(instance_loc.AsRegister()); + __ SpillInstance(instance_reg); + // Input 0 is the code target, 1 is the instance. First parameter at 2. + uint32_t input_idx = kInstanceParameterIndex + 1; for (uint32_t param_idx = 0; param_idx < num_params; ++param_idx) { input_idx += ProcessParameter(__ local_type(param_idx), input_idx); } @@ -336,12 +384,29 @@ class LiftoffCompiler { StackCheck(0); DCHECK_EQ(__ num_locals(), __ cache_state()->stack_height()); + + // TODO(kimanh): if possible, we want to move this check further up, + // in order to avoid unnecessary overhead each time we enter + // a Liftoff-compiled function that will jump to a Turbofan-compiled + // function. + if (FLAG_wasm_tier_up) { + JumpToOptimizedCodeIfExisting(); + } } void GenerateOutOfLineCode(OutOfLineCode& ool) { __ bind(ool.label.get()); const bool is_stack_check = ool.builtin == Builtins::kWasmStackGuard; - if (!runtime_exception_support_) { + const bool is_mem_out_of_bounds = + ool.builtin == Builtins::kThrowWasmTrapMemOutOfBounds; + + if (is_mem_out_of_bounds && env_->use_trap_handler) { + uint32_t pc = static_cast<uint32_t>(__ pc_offset()); + DCHECK_EQ(pc, __ pc_offset()); + wasm_compilation_data_->AddProtectedInstruction(ool.pc, pc); + } + + if (!wasm_compilation_data_->runtime_exception_support()) { // We cannot test calls to the runtime in cctest/test-run-wasm. // Therefore we emit a call to C here instead of a call to the runtime. // In this mode, we never generate stack checks. @@ -352,13 +417,6 @@ class LiftoffCompiler { return; } - if (!is_stack_check && env_->use_trap_handler) { - uint32_t pc = static_cast<uint32_t>(__ pc_offset()); - DCHECK_EQ(pc, __ pc_offset()); - protected_instructions_->emplace_back( - trap_handler::ProtectedInstructionData{ool.pc, pc}); - } - if (!ool.regs_to_save.is_empty()) __ PushRegisters(ool.regs_to_save); source_position_table_builder_->AddPosition( @@ -432,7 +490,7 @@ class LiftoffCompiler { if_block->else_state = base::make_unique<ElseState>(); // Test the condition, jump to else if zero. - Register value = __ PopToRegister(kGpReg).gp(); + Register value = __ PopToRegister().gp(); __ emit_cond_jump(kEqual, if_block->else_state->label.get(), kWasmI32, value); @@ -465,68 +523,109 @@ class LiftoffCompiler { void EndControl(Decoder* decoder, Control* c) {} - void GenerateCCall(Register res_reg, uint32_t num_args, - const Register* arg_regs, ExternalReference ext_ref) { - static constexpr int kNumReturns = 1; + enum CCallReturn : bool { kHasReturn = true, kNoReturn = false }; + + void GenerateCCall(const LiftoffRegister* result_regs, FunctionSig* sig, + ValueType out_argument_type, + const LiftoffRegister* arg_regs, + ExternalReference ext_ref) { + static constexpr int kMaxReturns = 1; static constexpr int kMaxArgs = 2; static constexpr MachineType kReps[]{ MachineType::Uint32(), MachineType::Pointer(), MachineType::Pointer()}; - static_assert(arraysize(kReps) == kNumReturns + kMaxArgs, "mismatch"); + static_assert(arraysize(kReps) == kMaxReturns + kMaxArgs, "mismatch"); + + const bool has_out_argument = out_argument_type != kWasmStmt; + const uint32_t num_returns = static_cast<uint32_t>(sig->return_count()); + // {total_num_args} is {num_args + 1} if the return value is stored in an + // out parameter, or {num_args} otherwise. + const uint32_t num_args = static_cast<uint32_t>(sig->parameter_count()); + const uint32_t total_num_args = num_args + has_out_argument; DCHECK_LE(num_args, kMaxArgs); + DCHECK_LE(num_returns, kMaxReturns); - MachineSignature sig(kNumReturns, num_args, kReps); - auto call_descriptor = - compiler::Linkage::GetSimplifiedCDescriptor(compilation_zone_, &sig); + MachineSignature machine_sig(num_returns, total_num_args, + kReps + (kMaxReturns - num_returns)); + auto* call_descriptor = compiler::Linkage::GetSimplifiedCDescriptor( + compilation_zone_, &machine_sig); // Before making a call, spill all cache registers. __ SpillAllRegisters(); // Store arguments on our stack, then align the stack for calling to C. - uint32_t num_params = - static_cast<uint32_t>(call_descriptor->ParameterCount()); - __ PrepareCCall(num_params, arg_regs); - - // Set parameters (in sp[0], sp[8], ...). - uint32_t num_stack_params = 0; - for (uint32_t param = 0; param < num_params; ++param) { - constexpr size_t kInputShift = 1; // Input 0 is the call target. - + __ PrepareCCall(sig, arg_regs, out_argument_type); + + // The arguments to the c function are pointers to the stack slots we just + // pushed. + int num_stack_params = 0; // Number of stack parameters. + int input_idx = 1; // Input 0 is the call target. + int param_byte_offset = 0; // Byte offset into the pushed arguments. + auto add_argument = [&](ValueType arg_type) { compiler::LinkageLocation loc = - call_descriptor->GetInputLocation(param + kInputShift); + call_descriptor->GetInputLocation(input_idx); + param_byte_offset += + RoundUp<kPointerSize>(WasmOpcodes::MemSize(arg_type)); + ++input_idx; if (loc.IsRegister()) { Register reg = Register::from_code(loc.AsRegister()); // Load address of that parameter to the register. - __ SetCCallRegParamAddr(reg, param, num_params); + __ SetCCallRegParamAddr(reg, param_byte_offset, arg_type); } else { DCHECK(loc.IsCallerFrameSlot()); - __ SetCCallStackParamAddr(num_stack_params, param, num_params); + __ SetCCallStackParamAddr(num_stack_params, param_byte_offset, + arg_type); ++num_stack_params; } + }; + for (ValueType arg_type : sig->parameters()) { + add_argument(arg_type); } + if (has_out_argument) { + add_argument(out_argument_type); + } + DCHECK_EQ(input_idx, call_descriptor->InputCount()); // Now execute the call. - __ CallC(ext_ref, num_params); + uint32_t c_call_arg_count = + static_cast<uint32_t>(sig->parameter_count()) + has_out_argument; + __ CallC(ext_ref, c_call_arg_count); + + // Reset the stack pointer. + __ FinishCCall(); // Load return value. - compiler::LinkageLocation return_loc = - call_descriptor->GetReturnLocation(0); - DCHECK(return_loc.IsRegister()); - Register return_reg = Register::from_code(return_loc.AsRegister()); - if (return_reg != res_reg) { - DCHECK_EQ(MachineRepresentation::kWord32, - sig.GetReturn(0).representation()); - __ Move(LiftoffRegister(res_reg), LiftoffRegister(return_reg), kWasmI32); + const LiftoffRegister* next_result_reg = result_regs; + if (sig->return_count() > 0) { + DCHECK_EQ(1, sig->return_count()); + compiler::LinkageLocation return_loc = + call_descriptor->GetReturnLocation(0); + DCHECK(return_loc.IsRegister()); + Register return_reg = Register::from_code(return_loc.AsRegister()); + if (return_reg != next_result_reg->gp()) { + __ Move(*next_result_reg, LiftoffRegister(return_reg), + sig->GetReturn(0)); + } + ++next_result_reg; + } + + // Load potential return value from output argument. + if (has_out_argument) { + __ LoadCCallOutArgument(*next_result_reg, out_argument_type, + param_byte_offset); } } - template <ValueType type, class EmitFn> + template <ValueType src_type, ValueType result_type, class EmitFn> void EmitUnOp(EmitFn fn) { - static RegClass rc = reg_class_for(type); + static RegClass src_rc = reg_class_for(src_type); + static RegClass result_rc = reg_class_for(result_type); LiftoffRegList pinned; - LiftoffRegister dst = pinned.set(__ GetUnaryOpTargetRegister(rc)); - LiftoffRegister src = __ PopToRegister(rc, pinned); + LiftoffRegister src = pinned.set(__ PopToRegister(pinned)); + LiftoffRegister dst = src_rc == result_rc + ? __ GetUnusedRegister(result_rc, {src}, pinned) + : __ GetUnusedRegister(result_rc, pinned); fn(dst, src); - __ PushRegister(type, dst); + __ PushRegister(result_type, dst); } void EmitI32UnOpWithCFallback(bool (LiftoffAssembler::*emit_fn)(Register, @@ -535,64 +634,122 @@ class LiftoffCompiler { auto emit_with_c_fallback = [=](LiftoffRegister dst, LiftoffRegister src) { if (emit_fn && (asm_->*emit_fn)(dst.gp(), src.gp())) return; ExternalReference ext_ref = fallback_fn(asm_->isolate()); - Register args[] = {src.gp()}; - GenerateCCall(dst.gp(), arraysize(args), args, ext_ref); + ValueType sig_i_i_reps[] = {kWasmI32, kWasmI32}; + FunctionSig sig_i_i(1, 1, sig_i_i_reps); + GenerateCCall(&dst, &sig_i_i, kWasmStmt, &src, ext_ref); }; - EmitUnOp<kWasmI32>(emit_with_c_fallback); + EmitUnOp<kWasmI32, kWasmI32>(emit_with_c_fallback); + } + + void EmitTypeConversion(WasmOpcode opcode, ValueType dst_type, + ValueType src_type, + ExternalReference (*fallback_fn)(Isolate*)) { + RegClass src_rc = reg_class_for(src_type); + RegClass dst_rc = reg_class_for(dst_type); + LiftoffRegList pinned; + LiftoffRegister src = pinned.set(__ PopToRegister()); + LiftoffRegister dst = src_rc == dst_rc + ? __ GetUnusedRegister(dst_rc, {src}, pinned) + : __ GetUnusedRegister(dst_rc, pinned); + if (!__ emit_type_conversion(opcode, dst, src)) { + DCHECK_NOT_NULL(fallback_fn); + ExternalReference ext_ref = fallback_fn(asm_->isolate()); + ValueType sig_reps[] = {src_type}; + FunctionSig sig(0, 1, sig_reps); + GenerateCCall(&dst, &sig, dst_type, &src, ext_ref); + } + __ PushRegister(dst_type, dst); } void UnOp(Decoder* decoder, WasmOpcode opcode, FunctionSig*, const Value& value, Value* result) { -#define CASE_I32_UNOP(opcode, fn) \ - case WasmOpcode::kExpr##opcode: \ - EmitUnOp<kWasmI32>([=](LiftoffRegister dst, LiftoffRegister src) { \ - __ emit_##fn(dst.gp(), src.gp()); \ - }); \ +#define CASE_I32_UNOP(opcode, fn) \ + case WasmOpcode::kExpr##opcode: \ + EmitUnOp<kWasmI32, kWasmI32>( \ + [=](LiftoffRegister dst, LiftoffRegister src) { \ + __ emit_##fn(dst.gp(), src.gp()); \ + }); \ break; -#define CASE_FLOAT_UNOP(opcode, type, fn) \ - case WasmOpcode::kExpr##opcode: \ - EmitUnOp<kWasm##type>([=](LiftoffRegister dst, LiftoffRegister src) { \ - __ emit_##fn(dst.fp(), src.fp()); \ - }); \ +#define CASE_FLOAT_UNOP(opcode, type, fn) \ + case WasmOpcode::kExpr##opcode: \ + EmitUnOp<kWasm##type, kWasm##type>( \ + [=](LiftoffRegister dst, LiftoffRegister src) { \ + __ emit_##fn(dst.fp(), src.fp()); \ + }); \ + break; +#define CASE_TYPE_CONVERSION(opcode, dst_type, src_type, ext_ref) \ + case WasmOpcode::kExpr##opcode: \ + EmitTypeConversion(kExpr##opcode, kWasm##dst_type, kWasm##src_type, \ + ext_ref); \ break; switch (opcode) { + CASE_I32_UNOP(I32Eqz, i32_eqz) CASE_I32_UNOP(I32Clz, i32_clz) CASE_I32_UNOP(I32Ctz, i32_ctz) + CASE_FLOAT_UNOP(F32Abs, F32, f32_abs) + CASE_FLOAT_UNOP(F32Neg, F32, f32_neg) + CASE_FLOAT_UNOP(F32Ceil, F32, f32_ceil) + CASE_FLOAT_UNOP(F32Floor, F32, f32_floor) + CASE_FLOAT_UNOP(F32Trunc, F32, f32_trunc) + CASE_FLOAT_UNOP(F32NearestInt, F32, f32_nearest_int) + CASE_FLOAT_UNOP(F32Sqrt, F32, f32_sqrt) + CASE_FLOAT_UNOP(F64Abs, F64, f64_abs) + CASE_FLOAT_UNOP(F64Neg, F64, f64_neg) + CASE_FLOAT_UNOP(F64Ceil, F64, f64_ceil) + CASE_FLOAT_UNOP(F64Floor, F64, f64_floor) + CASE_FLOAT_UNOP(F64Trunc, F64, f64_trunc) + CASE_FLOAT_UNOP(F64NearestInt, F64, f64_nearest_int) + CASE_FLOAT_UNOP(F64Sqrt, F64, f64_sqrt) + CASE_TYPE_CONVERSION(I32ConvertI64, I32, I64, nullptr) + CASE_TYPE_CONVERSION(I32ReinterpretF32, I32, F32, nullptr) + CASE_TYPE_CONVERSION(I64SConvertI32, I64, I32, nullptr) + CASE_TYPE_CONVERSION(I64UConvertI32, I64, I32, nullptr) + CASE_TYPE_CONVERSION(I64ReinterpretF64, I64, F64, nullptr) + CASE_TYPE_CONVERSION(F32SConvertI32, F32, I32, nullptr) + CASE_TYPE_CONVERSION(F32UConvertI32, F32, I32, nullptr) + CASE_TYPE_CONVERSION(F32SConvertI64, F32, I64, + &ExternalReference::wasm_int64_to_float32) + CASE_TYPE_CONVERSION(F32UConvertI64, F32, I64, + &ExternalReference::wasm_uint64_to_float32) + CASE_TYPE_CONVERSION(F32ConvertF64, F32, F64, nullptr) + CASE_TYPE_CONVERSION(F32ReinterpretI32, F32, I32, nullptr) + CASE_TYPE_CONVERSION(F64SConvertI32, F64, I32, nullptr) + CASE_TYPE_CONVERSION(F64UConvertI32, F64, I32, nullptr) + CASE_TYPE_CONVERSION(F64SConvertI64, F64, I64, + &ExternalReference::wasm_int64_to_float64) + CASE_TYPE_CONVERSION(F64UConvertI64, F64, I64, + &ExternalReference::wasm_uint64_to_float64) + CASE_TYPE_CONVERSION(F64ConvertF32, F64, F32, nullptr) + CASE_TYPE_CONVERSION(F64ReinterpretI64, F64, I64, nullptr) case kExprI32Popcnt: EmitI32UnOpWithCFallback(&LiftoffAssembler::emit_i32_popcnt, &ExternalReference::wasm_word32_popcnt); break; - case kExprI32Eqz: - EmitUnOp<kWasmI32>([=](LiftoffRegister dst, LiftoffRegister src) { - __ emit_i32_set_cond(kEqual, dst.gp(), src.gp()); - }); + case WasmOpcode::kExprI64Eqz: + EmitUnOp<kWasmI64, kWasmI32>( + [=](LiftoffRegister dst, LiftoffRegister src) { + __ emit_i64_eqz(dst.gp(), src); + }); break; - CASE_FLOAT_UNOP(F32Neg, F32, f32_neg) - CASE_FLOAT_UNOP(F64Neg, F64, f64_neg) default: return unsupported(decoder, WasmOpcodes::OpcodeName(opcode)); } #undef CASE_I32_UNOP #undef CASE_FLOAT_UNOP +#undef CASE_TYPE_CONVERSION } - template <ValueType type, typename EmitFn> - void EmitMonomorphicBinOp(EmitFn fn) { - static constexpr RegClass rc = reg_class_for(type); - LiftoffRegList pinned; - LiftoffRegister dst = pinned.set(__ GetBinaryOpTargetRegister(rc)); - LiftoffRegister rhs = pinned.set(__ PopToRegister(rc, pinned)); - LiftoffRegister lhs = __ PopToRegister(rc, pinned); - fn(dst, lhs, rhs); - __ PushRegister(type, dst); - } - - template <ValueType result_type, RegClass src_rc, typename EmitFn> - void EmitBinOpWithDifferentResultType(EmitFn fn) { + template <ValueType src_type, ValueType result_type, typename EmitFn> + void EmitBinOp(EmitFn fn) { + static constexpr RegClass src_rc = reg_class_for(src_type); + static constexpr RegClass result_rc = reg_class_for(result_type); LiftoffRegList pinned; - LiftoffRegister rhs = pinned.set(__ PopToRegister(src_rc, pinned)); - LiftoffRegister lhs = pinned.set(__ PopToRegister(src_rc, pinned)); - LiftoffRegister dst = __ GetUnusedRegister(reg_class_for(result_type)); + LiftoffRegister rhs = pinned.set(__ PopToRegister(pinned)); + LiftoffRegister lhs = pinned.set(__ PopToRegister(pinned)); + LiftoffRegister dst = + src_rc == result_rc + ? __ GetUnusedRegister(result_rc, {lhs, rhs}, pinned) + : __ GetUnusedRegister(result_rc); fn(dst, lhs, rhs); __ PushRegister(result_type, dst); } @@ -601,41 +758,69 @@ class LiftoffCompiler { const Value& lhs, const Value& rhs, Value* result) { #define CASE_I32_BINOP(opcode, fn) \ case WasmOpcode::kExpr##opcode: \ - return EmitMonomorphicBinOp<kWasmI32>( \ + return EmitBinOp<kWasmI32, kWasmI32>( \ [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ __ emit_##fn(dst.gp(), lhs.gp(), rhs.gp()); \ }); +#define CASE_I64_BINOP(opcode, fn) \ + case WasmOpcode::kExpr##opcode: \ + return EmitBinOp<kWasmI64, kWasmI64>( \ + [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ + __ emit_##fn(dst, lhs, rhs); \ + }); #define CASE_FLOAT_BINOP(opcode, type, fn) \ case WasmOpcode::kExpr##opcode: \ - return EmitMonomorphicBinOp<kWasm##type>( \ + return EmitBinOp<kWasm##type, kWasm##type>( \ [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ __ emit_##fn(dst.fp(), lhs.fp(), rhs.fp()); \ }); #define CASE_I32_CMPOP(opcode, cond) \ case WasmOpcode::kExpr##opcode: \ - return EmitMonomorphicBinOp<kWasmI32>( \ + return EmitBinOp<kWasmI32, kWasmI32>( \ [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ __ emit_i32_set_cond(cond, dst.gp(), lhs.gp(), rhs.gp()); \ }); +#define CASE_I64_CMPOP(opcode, cond) \ + case WasmOpcode::kExpr##opcode: \ + return EmitBinOp<kWasmI64, kWasmI32>( \ + [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ + __ emit_i64_set_cond(cond, dst.gp(), lhs, rhs); \ + }); #define CASE_F32_CMPOP(opcode, cond) \ case WasmOpcode::kExpr##opcode: \ - return EmitBinOpWithDifferentResultType<kWasmI32, kFpReg>( \ + return EmitBinOp<kWasmF32, kWasmI32>( \ [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ __ emit_f32_set_cond(cond, dst.gp(), lhs.fp(), rhs.fp()); \ }); -#define CASE_SHIFTOP(opcode, fn) \ +#define CASE_F64_CMPOP(opcode, cond) \ case WasmOpcode::kExpr##opcode: \ - return EmitMonomorphicBinOp<kWasmI32>( \ + return EmitBinOp<kWasmF64, kWasmI32>( \ + [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ + __ emit_f64_set_cond(cond, dst.gp(), lhs.fp(), rhs.fp()); \ + }); +#define CASE_I32_SHIFTOP(opcode, fn) \ + case WasmOpcode::kExpr##opcode: \ + return EmitBinOp<kWasmI32, kWasmI32>( \ [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ __ emit_##fn(dst.gp(), lhs.gp(), rhs.gp(), {}); \ }); +#define CASE_I64_SHIFTOP(opcode, fn) \ + case WasmOpcode::kExpr##opcode: \ + return EmitBinOp<kWasmI64, kWasmI64>([=](LiftoffRegister dst, \ + LiftoffRegister src, \ + LiftoffRegister amount) { \ + __ emit_##fn(dst, src, amount.is_pair() ? amount.low_gp() : amount.gp(), \ + {}); \ + }); #define CASE_CCALL_BINOP(opcode, type, ext_ref_fn) \ case WasmOpcode::kExpr##opcode: \ - return EmitMonomorphicBinOp<kWasmI32>( \ + return EmitBinOp<kWasmI32, kWasmI32>( \ [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ - Register args[] = {lhs.gp(), rhs.gp()}; \ + LiftoffRegister args[] = {lhs, rhs}; \ auto ext_ref = ExternalReference::ext_ref_fn(__ isolate()); \ - GenerateCCall(dst.gp(), arraysize(args), args, ext_ref); \ + ValueType sig_i_ii_reps[] = {kWasmI32, kWasmI32, kWasmI32}; \ + FunctionSig sig_i_ii(1, 2, sig_i_ii_reps); \ + GenerateCCall(&dst, &sig_i_ii, kWasmStmt, args, ext_ref); \ }); switch (opcode) { CASE_I32_BINOP(I32Add, i32_add) @@ -644,6 +829,9 @@ class LiftoffCompiler { CASE_I32_BINOP(I32And, i32_and) CASE_I32_BINOP(I32Ior, i32_or) CASE_I32_BINOP(I32Xor, i32_xor) + CASE_I64_BINOP(I64And, i64_and) + CASE_I64_BINOP(I64Ior, i64_or) + CASE_I64_BINOP(I64Xor, i64_xor) CASE_I32_CMPOP(I32Eq, kEqual) CASE_I32_CMPOP(I32Ne, kUnequal) CASE_I32_CMPOP(I32LtS, kSignedLessThan) @@ -654,31 +842,58 @@ class LiftoffCompiler { CASE_I32_CMPOP(I32LeU, kUnsignedLessEqual) CASE_I32_CMPOP(I32GeS, kSignedGreaterEqual) CASE_I32_CMPOP(I32GeU, kUnsignedGreaterEqual) + CASE_I64_BINOP(I64Add, i64_add) + CASE_I64_BINOP(I64Sub, i64_sub) + CASE_I64_CMPOP(I64Eq, kEqual) + CASE_I64_CMPOP(I64Ne, kUnequal) + CASE_I64_CMPOP(I64LtS, kSignedLessThan) + CASE_I64_CMPOP(I64LtU, kUnsignedLessThan) + CASE_I64_CMPOP(I64GtS, kSignedGreaterThan) + CASE_I64_CMPOP(I64GtU, kUnsignedGreaterThan) + CASE_I64_CMPOP(I64LeS, kSignedLessEqual) + CASE_I64_CMPOP(I64LeU, kUnsignedLessEqual) + CASE_I64_CMPOP(I64GeS, kSignedGreaterEqual) + CASE_I64_CMPOP(I64GeU, kUnsignedGreaterEqual) CASE_F32_CMPOP(F32Eq, kEqual) CASE_F32_CMPOP(F32Ne, kUnequal) CASE_F32_CMPOP(F32Lt, kUnsignedLessThan) CASE_F32_CMPOP(F32Gt, kUnsignedGreaterThan) CASE_F32_CMPOP(F32Le, kUnsignedLessEqual) CASE_F32_CMPOP(F32Ge, kUnsignedGreaterEqual) - CASE_SHIFTOP(I32Shl, i32_shl) - CASE_SHIFTOP(I32ShrS, i32_sar) - CASE_SHIFTOP(I32ShrU, i32_shr) + CASE_F64_CMPOP(F64Eq, kEqual) + CASE_F64_CMPOP(F64Ne, kUnequal) + CASE_F64_CMPOP(F64Lt, kUnsignedLessThan) + CASE_F64_CMPOP(F64Gt, kUnsignedGreaterThan) + CASE_F64_CMPOP(F64Le, kUnsignedLessEqual) + CASE_F64_CMPOP(F64Ge, kUnsignedGreaterEqual) + CASE_I32_SHIFTOP(I32Shl, i32_shl) + CASE_I32_SHIFTOP(I32ShrS, i32_sar) + CASE_I32_SHIFTOP(I32ShrU, i32_shr) + CASE_I64_SHIFTOP(I64Shl, i64_shl) + CASE_I64_SHIFTOP(I64ShrS, i64_sar) + CASE_I64_SHIFTOP(I64ShrU, i64_shr) CASE_CCALL_BINOP(I32Rol, I32, wasm_word32_rol) CASE_CCALL_BINOP(I32Ror, I32, wasm_word32_ror) CASE_FLOAT_BINOP(F32Add, F32, f32_add) CASE_FLOAT_BINOP(F32Sub, F32, f32_sub) CASE_FLOAT_BINOP(F32Mul, F32, f32_mul) + CASE_FLOAT_BINOP(F32Div, F32, f32_div) CASE_FLOAT_BINOP(F64Add, F64, f64_add) CASE_FLOAT_BINOP(F64Sub, F64, f64_sub) CASE_FLOAT_BINOP(F64Mul, F64, f64_mul) + CASE_FLOAT_BINOP(F64Div, F64, f64_div) default: return unsupported(decoder, WasmOpcodes::OpcodeName(opcode)); } #undef CASE_I32_BINOP +#undef CASE_I64_BINOP #undef CASE_FLOAT_BINOP #undef CASE_I32_CMPOP +#undef CASE_I64_CMPOP #undef CASE_F32_CMPOP -#undef CASE_SHIFTOP +#undef CASE_F64_CMPOP +#undef CASE_I32_SHIFTOP +#undef CASE_I64_SHIFTOP #undef CASE_CCALL_BINOP } @@ -713,6 +928,10 @@ class LiftoffCompiler { __ PushRegister(kWasmF64, reg); } + void RefNull(Decoder* decoder, Value* result) { + unsupported(decoder, "ref_null"); + } + void Drop(Decoder* decoder, const Value& value) { __ DropStackSlot(&__ cache_state()->stack_state.back()); __ cache_state()->stack_state.pop_back(); @@ -727,8 +946,7 @@ class LiftoffCompiler { } if (!values.is_empty()) { if (values.size() > 1) return unsupported(decoder, "multi-return"); - RegClass rc = reg_class_for(values[0].type); - LiftoffRegister reg = __ PopToRegister(rc); + LiftoffRegister reg = __ PopToRegister(); __ MoveToReturnRegister(reg, values[0].type); } __ LeaveFrame(StackFrame::WASM_COMPILED); @@ -812,35 +1030,29 @@ class LiftoffCompiler { void GetGlobal(Decoder* decoder, Value* result, const GlobalIndexOperand<validate>& operand) { const auto* global = &env_->module->globals[operand.index]; - if (global->type != kWasmI32 && global->type != kWasmI64) - return unsupported(decoder, "non-int global"); + if (!CheckSupportedType(decoder, kTypes_ilfd, global->type, "global")) + return; LiftoffRegList pinned; - Register addr = pinned.set(__ GetUnusedRegister(kGpReg)).gp(); - __ LoadFromContext(addr, offsetof(WasmContext, globals_start), - kPointerSize); + LiftoffRegister addr = pinned.set(__ GetUnusedRegister(kGpReg)); + LOAD_INSTANCE_FIELD(addr, GlobalsStart, kPointerLoadType); LiftoffRegister value = pinned.set(__ GetUnusedRegister(reg_class_for(global->type), pinned)); - LoadType type = - global->type == kWasmI32 ? LoadType::kI32Load : LoadType::kI64Load; - if (type.size() > kPointerSize) - return unsupported(decoder, "global > kPointerSize"); - __ Load(value, addr, no_reg, global->offset, type, pinned); + LoadType type = LoadType::ForValueType(global->type); + __ Load(value, addr.gp(), no_reg, global->offset, type, pinned); __ PushRegister(global->type, value); } void SetGlobal(Decoder* decoder, const Value& value, const GlobalIndexOperand<validate>& operand) { auto* global = &env_->module->globals[operand.index]; - if (global->type != kWasmI32) return unsupported(decoder, "non-i32 global"); + if (!CheckSupportedType(decoder, kTypes_ilfd, global->type, "global")) + return; LiftoffRegList pinned; - Register addr = pinned.set(__ GetUnusedRegister(kGpReg)).gp(); - __ LoadFromContext(addr, offsetof(WasmContext, globals_start), - kPointerSize); - LiftoffRegister reg = - pinned.set(__ PopToRegister(reg_class_for(global->type), pinned)); - StoreType type = - global->type == kWasmI32 ? StoreType::kI32Store : StoreType::kI64Store; - __ Store(addr, no_reg, global->offset, reg, type, pinned); + LiftoffRegister addr = pinned.set(__ GetUnusedRegister(kGpReg)); + LOAD_INSTANCE_FIELD(addr, GlobalsStart, kPointerLoadType); + LiftoffRegister reg = pinned.set(__ PopToRegister(pinned)); + StoreType type = StoreType::ForValueType(global->type); + __ Store(addr.gp(), no_reg, global->offset, reg, type, pinned); } void Unreachable(Decoder* decoder) { unsupported(decoder, "unreachable"); } @@ -865,7 +1077,7 @@ class LiftoffCompiler { void BrIf(Decoder* decoder, const Value& cond, Control* target) { Label cont_false; - Register value = __ PopToRegister(kGpReg).gp(); + Register value = __ PopToRegister().gp(); __ emit_cond_jump(kEqual, &cont_false, kWasmI32, value); Br(target); @@ -916,7 +1128,7 @@ class LiftoffCompiler { void BrTable(Decoder* decoder, const BranchTableOperand<validate>& operand, const Value& key) { LiftoffRegList pinned; - LiftoffRegister value = pinned.set(__ PopToRegister(kGpReg)); + LiftoffRegister value = pinned.set(__ PopToRegister()); BranchTableIterator<validate> table_iterator(decoder, operand); std::map<uint32_t, MovableLabel> br_targets; @@ -968,8 +1180,12 @@ class LiftoffCompiler { return false; } + // TODO(eholk): This adds protected instruction information for the jump + // instruction we are about to generate. It would be better to just not add + // protected instruction info when the pc is 0. Label* trap_label = AddOutOfLineTrap( - decoder->position(), Builtins::kThrowWasmTrapMemOutOfBounds); + decoder->position(), Builtins::kThrowWasmTrapMemOutOfBounds, + env_->use_trap_handler ? __ pc_offset() : 0); if (statically_oob) { __ emit_jump(trap_label); @@ -991,7 +1207,7 @@ class LiftoffCompiler { LiftoffRegister end_offset_reg = pinned.set(__ GetUnusedRegister(kGpReg, pinned)); LiftoffRegister mem_size = __ GetUnusedRegister(kGpReg, pinned); - __ LoadFromContext(mem_size.gp(), offsetof(WasmContext, mem_size), 4); + LOAD_INSTANCE_FIELD(mem_size, MemorySize, LoadType::kI32Load); __ LoadConstant(end_offset_reg, WasmValue(end_offset)); if (end_offset >= min_size_) { __ emit_cond_jump(kUnsignedGreaterEqual, trap_label, kWasmI32, @@ -1078,16 +1294,16 @@ class LiftoffCompiler { ValueType value_type = type.value_type(); if (!CheckSupportedType(decoder, kTypes_ilfd, value_type, "load")) return; LiftoffRegList pinned; - Register index = pinned.set(__ PopToRegister(kGpReg)).gp(); + Register index = pinned.set(__ PopToRegister()).gp(); if (BoundsCheckMem(decoder, type.size(), operand.offset, index, pinned)) { return; } - Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp(); - __ LoadFromContext(addr, offsetof(WasmContext, mem_start), kPointerSize); + LiftoffRegister addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)); + LOAD_INSTANCE_FIELD(addr, MemoryStart, kPointerLoadType); RegClass rc = reg_class_for(value_type); LiftoffRegister value = pinned.set(__ GetUnusedRegister(rc, pinned)); uint32_t protected_load_pc = 0; - __ Load(value, addr, index, operand.offset, type, pinned, + __ Load(value, addr.gp(), index, operand.offset, type, pinned, &protected_load_pc); if (env_->use_trap_handler) { AddOutOfLineTrap(decoder->position(), @@ -1107,17 +1323,16 @@ class LiftoffCompiler { const Value& index_val, const Value& value_val) { ValueType value_type = type.value_type(); if (!CheckSupportedType(decoder, kTypes_ilfd, value_type, "store")) return; - RegClass rc = reg_class_for(value_type); LiftoffRegList pinned; - LiftoffRegister value = pinned.set(__ PopToRegister(rc)); - Register index = pinned.set(__ PopToRegister(kGpReg, pinned)).gp(); + LiftoffRegister value = pinned.set(__ PopToRegister()); + Register index = pinned.set(__ PopToRegister(pinned)).gp(); if (BoundsCheckMem(decoder, type.size(), operand.offset, index, pinned)) { return; } - Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp(); - __ LoadFromContext(addr, offsetof(WasmContext, mem_start), kPointerSize); + LiftoffRegister addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)); + LOAD_INSTANCE_FIELD(addr, MemoryStart, kPointerLoadType); uint32_t protected_store_pc = 0; - __ Store(addr, index, operand.offset, value, type, pinned, + __ Store(addr.gp(), index, operand.offset, value, type, pinned, &protected_store_pc); if (env_->use_trap_handler) { AddOutOfLineTrap(decoder->position(), @@ -1152,26 +1367,55 @@ class LiftoffCompiler { call_descriptor = GetLoweredCallDescriptor(compilation_zone_, call_descriptor); - __ PrepareCall(operand.sig, call_descriptor); + if (operand.index < env_->module->num_imported_functions) { + // A direct call to an imported function. + LiftoffRegList pinned; + LiftoffRegister tmp = pinned.set(__ GetUnusedRegister(kGpReg, pinned)); + LiftoffRegister target = pinned.set(__ GetUnusedRegister(kGpReg, pinned)); + + LiftoffRegister imported_targets = tmp; + LOAD_INSTANCE_FIELD(imported_targets, ImportedFunctionTargets, + kPointerLoadType); + __ Load(target, imported_targets.gp(), no_reg, + operand.index * sizeof(Address), kPointerLoadType, pinned); + + LiftoffRegister imported_instances = tmp; + LOAD_INSTANCE_FIELD(imported_instances, ImportedFunctionInstances, + kPointerLoadType); + LiftoffRegister target_instance = tmp; + __ Load(target_instance, imported_instances.gp(), no_reg, + compiler::FixedArrayOffsetMinusTag(operand.index), + kPointerLoadType, pinned); - source_position_table_builder_->AddPosition( - __ pc_offset(), SourcePosition(decoder->position()), false); + LiftoffRegister* explicit_instance = &target_instance; + Register target_reg = target.gp(); + __ PrepareCall(operand.sig, call_descriptor, &target_reg, + explicit_instance); + source_position_table_builder_->AddPosition( + __ pc_offset(), SourcePosition(decoder->position()), false); + + __ CallIndirect(operand.sig, call_descriptor, target_reg); + + safepoint_table_builder_.DefineSafepoint(asm_, Safepoint::kSimple, 0, + Safepoint::kNoLazyDeopt); + + __ FinishCall(operand.sig, call_descriptor); + } else { + // A direct call within this module just gets the current instance. + __ PrepareCall(operand.sig, call_descriptor); + + source_position_table_builder_->AddPosition( + __ pc_offset(), SourcePosition(decoder->position()), false); - if (FLAG_wasm_jit_to_native) { // Just encode the function index. This will be patched at instantiation. Address addr = reinterpret_cast<Address>(operand.index); __ CallNativeWasmCode(addr); - } else { - Handle<Code> target = operand.index < env_->function_code.size() - ? env_->function_code[operand.index] - : env_->default_function_code; - __ Call(target, RelocInfo::CODE_TARGET); - } - safepoint_table_builder_.DefineSafepoint(asm_, Safepoint::kSimple, 0, - Safepoint::kNoLazyDeopt); + safepoint_table_builder_.DefineSafepoint(asm_, Safepoint::kSimple, 0, + Safepoint::kNoLazyDeopt); - __ FinishCall(operand.sig, call_descriptor); + __ FinishCall(operand.sig, call_descriptor); + } } void CallIndirect(Decoder* decoder, const Value& index_val, @@ -1186,11 +1430,8 @@ class LiftoffCompiler { return; } - // Assume only one table for now. - uint32_t table_index = 0; - // Pop the index. - LiftoffRegister index = __ PopToRegister(kGpReg); + LiftoffRegister index = __ PopToRegister(); // If that register is still being used after popping, we move it to another // register, because we want to modify that register. if (__ cache_state()->is_used(index)) { @@ -1207,111 +1448,56 @@ class LiftoffCompiler { pinned.set(__ GetUnusedRegister(kGpReg, pinned)); LiftoffRegister scratch = pinned.set(__ GetUnusedRegister(kGpReg, pinned)); - LiftoffRegister* explicit_context = nullptr; - // Bounds check against the table size. Label* invalid_func_label = AddOutOfLineTrap( decoder->position(), Builtins::kThrowWasmTrapFuncInvalid); - static constexpr LoadType kPointerLoadType = - kPointerSize == 8 ? LoadType::kI64Load : LoadType::kI32Load; - static constexpr int kFixedArrayOffset = - FixedArray::kHeaderSize - kHeapObjectTag; - uint32_t canonical_sig_num = env_->module->signature_ids[operand.sig_index]; DCHECK_GE(canonical_sig_num, 0); DCHECK_GE(kMaxInt, canonical_sig_num); - if (WASM_CONTEXT_TABLES) { - // Compare against table size stored in {wasm_context->table_size}. - __ LoadFromContext(tmp_const.gp(), offsetof(WasmContext, table_size), - sizeof(uint32_t)); - __ emit_cond_jump(kUnsignedGreaterEqual, invalid_func_label, kWasmI32, - index.gp(), tmp_const.gp()); - // Load the table from {wasm_context->table} - __ LoadFromContext(table.gp(), offsetof(WasmContext, table), - kPointerSize); - // Load the signature from {wasm_context->table[$index].sig_id} - // == wasm_context.table + $index * #sizeof(IndirectionFunctionTableEntry) - // + #offsetof(sig_id) - __ LoadConstant( - tmp_const, - WasmValue(static_cast<uint32_t>(sizeof(IndirectFunctionTableEntry)))); + // Compare against table size stored in + // {instance->indirect_function_table_size}. + LOAD_INSTANCE_FIELD(tmp_const, IndirectFunctionTableSize, + LoadType::kI32Load); + __ emit_cond_jump(kUnsignedGreaterEqual, invalid_func_label, kWasmI32, + index.gp(), tmp_const.gp()); + + // Load the signature from {instance->ift_sig_ids[key]} + LOAD_INSTANCE_FIELD(table, IndirectFunctionTableSigIds, kPointerLoadType); + __ LoadConstant(tmp_const, + WasmValue(static_cast<uint32_t>(sizeof(uint32_t)))); + // TODO(wasm): use a emit_i32_shli() instead of a multiply. + // (currently cannot use shl on ia32/x64 because it clobbers %rcx). + __ emit_i32_mul(index.gp(), index.gp(), tmp_const.gp()); + __ Load(scratch, table.gp(), index.gp(), 0, LoadType::kI32Load, pinned); + + // Compare against expected signature. + __ LoadConstant(tmp_const, WasmValue(canonical_sig_num)); + + Label* sig_mismatch_label = AddOutOfLineTrap( + decoder->position(), Builtins::kThrowWasmTrapFuncSigMismatch); + __ emit_cond_jump(kUnequal, sig_mismatch_label, + LiftoffAssembler::kWasmIntPtr, scratch.gp(), + tmp_const.gp()); + + if (kPointerSize == 8) { + // {index} has already been multiplied by 4. Multiply by another 2. + __ LoadConstant(tmp_const, WasmValue(2)); __ emit_i32_mul(index.gp(), index.gp(), tmp_const.gp()); - __ Load(scratch, table.gp(), index.gp(), - offsetof(IndirectFunctionTableEntry, sig_id), LoadType::kI32Load, - pinned); - - __ LoadConstant(tmp_const, WasmValue(canonical_sig_num)); - - Label* sig_mismatch_label = AddOutOfLineTrap( - decoder->position(), Builtins::kThrowWasmTrapFuncSigMismatch); - __ emit_cond_jump(kUnequal, sig_mismatch_label, - LiftoffAssembler::kWasmIntPtr, scratch.gp(), - tmp_const.gp()); - - // Load the target address from {wasm_context->table[$index].target} - __ Load(scratch, table.gp(), index.gp(), - offsetof(IndirectFunctionTableEntry, target), kPointerLoadType, - pinned); - - // Load the context from {wasm_context->table[$index].context} - // TODO(wasm): directly allocate the correct context register to avoid - // any potential moves. - __ Load(tmp_const, table.gp(), index.gp(), - offsetof(IndirectFunctionTableEntry, context), kPointerLoadType, - pinned); - explicit_context = &tmp_const; - } else { - // Compare against table size, which is a patchable constant. - uint32_t table_size = - env_->module->function_tables[table_index].initial_size; - - __ LoadConstant(tmp_const, WasmValue(table_size), - RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE); - - __ emit_cond_jump(kUnsignedGreaterEqual, invalid_func_label, kWasmI32, - index.gp(), tmp_const.gp()); - - wasm::GlobalHandleAddress function_table_handle_address = - env_->function_tables[table_index]; - __ LoadConstant(table, WasmPtrValue(function_table_handle_address), - RelocInfo::WASM_GLOBAL_HANDLE); - __ Load(table, table.gp(), no_reg, 0, kPointerLoadType, pinned); - - // Load signature from the table and check. - // The table is a FixedArray; signatures are encoded as SMIs. - // [sig1, code1, sig2, code2, sig3, code3, ...] - static_assert(compiler::kFunctionTableEntrySize == 2, "consistency"); - static_assert(compiler::kFunctionTableSignatureOffset == 0, - "consistency"); - static_assert(compiler::kFunctionTableCodeOffset == 1, "consistency"); - __ LoadConstant(tmp_const, WasmValue(kPointerSizeLog2 + 1)); - // Shift index such that it's the offset of the signature in the - // FixedArray. - __ emit_i32_shl(index.gp(), index.gp(), tmp_const.gp(), pinned); - - // Load the signature. - __ Load(scratch, table.gp(), index.gp(), kFixedArrayOffset, - kPointerLoadType, pinned); - - __ LoadConstant(tmp_const, WasmPtrValue(Smi::FromInt(canonical_sig_num))); + } - Label* sig_mismatch_label = AddOutOfLineTrap( - decoder->position(), Builtins::kThrowWasmTrapFuncSigMismatch); - __ emit_cond_jump(kUnequal, sig_mismatch_label, - LiftoffAssembler::kWasmIntPtr, scratch.gp(), - tmp_const.gp()); + // Load the target from {instance->ift_targets[key]} + LOAD_INSTANCE_FIELD(table, IndirectFunctionTableTargets, kPointerLoadType); + __ Load(scratch, table.gp(), index.gp(), 0, kPointerLoadType, pinned); - // Load code object. - __ Load(scratch, table.gp(), index.gp(), kFixedArrayOffset + kPointerSize, - kPointerLoadType, pinned); - - // Move the pointer from the Code object to the instruction start. - __ LoadConstant(tmp_const, - WasmPtrValue(Code::kHeaderSize - kHeapObjectTag)); - __ emit_ptrsize_add(scratch.gp(), scratch.gp(), tmp_const.gp()); - } + // Load the instance from {instance->ift_instances[key]} + LOAD_INSTANCE_FIELD(table, IndirectFunctionTableInstances, + kPointerLoadType); + __ Load(tmp_const, table.gp(), index.gp(), + (FixedArray::kHeaderSize - kHeapObjectTag), kPointerLoadType, + pinned); + LiftoffRegister* explicit_instance = &tmp_const; source_position_table_builder_->AddPosition( __ pc_offset(), SourcePosition(decoder->position()), false); @@ -1322,7 +1508,7 @@ class LiftoffCompiler { GetLoweredCallDescriptor(compilation_zone_, call_descriptor); Register target = scratch.gp(); - __ PrepareCall(operand.sig, call_descriptor, &target, explicit_context); + __ PrepareCall(operand.sig, call_descriptor, &target, explicit_instance); __ CallIndirect(operand.sig, call_descriptor, target); safepoint_table_builder_.DefineSafepoint(asm_, Safepoint::kSimple, 0, @@ -1372,11 +1558,10 @@ class LiftoffCompiler { // {min_size_} and {max_size_} are cached values computed from the ModuleEnv. const uint64_t min_size_; const uint64_t max_size_; - const compiler::RuntimeExceptionSupport runtime_exception_support_; bool ok_ = true; std::vector<OutOfLineCode> out_of_line_code_; SourcePositionTableBuilder* const source_position_table_builder_; - std::vector<trap_handler::ProtectedInstructionData>* protected_instructions_; + WasmCompilationData* wasm_compilation_data_; // Zone used to store information during compilation. The result will be // stored independently, such that this zone can die together with the // LiftoffCompiler after compilation. @@ -1389,6 +1574,10 @@ class LiftoffCompiler { // patch the actually needed stack size in the end. uint32_t pc_offset_stack_frame_construction_ = 0; + // Points to the cell within the {code_table_} of the NativeModule, + // which corresponds to the currently compiled function + WasmCode* const* code_table_entry_ = nullptr; + void TraceCacheState(Decoder* decoder) const { #ifdef DEBUG if (!FLAG_trace_liftoff || !FLAG_trace_wasm_decoder) return; @@ -1425,11 +1614,12 @@ bool compiler::WasmCompilationUnit::ExecuteLiftoffCompilation() { auto call_descriptor = compiler::GetWasmCallDescriptor(&zone, func_body_.sig); base::Optional<TimedHistogramScope> liftoff_compile_time_scope( base::in_place, counters()->liftoff_compile_time()); + wasm::WasmCode* const* code_table_entry = + native_module_->code_table().data() + func_index_; wasm::WasmFullDecoder<wasm::Decoder::kValidate, wasm::LiftoffCompiler> decoder(&zone, module, func_body_, &liftoff_.asm_, call_descriptor, env_, - runtime_exception_support_, - &liftoff_.source_position_table_builder_, - protected_instructions_.get(), &zone, &liftoff_.codegen_zone_); + &liftoff_.source_position_table_builder_, &wasm_compilation_data_, + &zone, &liftoff_.codegen_zone_, code_table_entry); decoder.Decode(); liftoff_compile_time_scope.reset(); if (!decoder.interface().ok()) { @@ -1458,6 +1648,8 @@ bool compiler::WasmCompilationUnit::ExecuteLiftoffCompilation() { #undef __ #undef TRACE +#undef WASM_INSTANCE_OBJECT_OFFSET +#undef LOAD_INSTANCE_FIELD } // namespace internal } // namespace v8 |