/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * * Copyright 2016 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // This is an INTERNAL header for Wasm baseline compiler: inline methods in the // compiler for basic code generation. #ifndef wasm_wasm_baseline_codegen_inl_h #define wasm_wasm_baseline_codegen_inl_h // The templates for register management must be defined by the time we use the // templated emitters, below. #include "wasm/WasmBCRegMgmt-inl.h" namespace js { namespace wasm { ////////////////////////////////////////////////////////////////////////////// // // Register-to-register moves. void BaseCompiler::moveI32(RegI32 src, RegI32 dest) { if (src != dest) { masm.move32(src, dest); } } void BaseCompiler::moveI64(RegI64 src, RegI64 dest) { if (src != dest) { masm.move64(src, dest); } } void BaseCompiler::moveRef(RegRef src, RegRef dest) { if (src != dest) { masm.movePtr(src, dest); } } void BaseCompiler::movePtr(RegPtr src, RegPtr dest) { if (src != dest) { masm.movePtr(src, dest); } } void BaseCompiler::moveF64(RegF64 src, RegF64 dest) { if (src != dest) { masm.moveDouble(src, dest); } } void BaseCompiler::moveF32(RegF32 src, RegF32 dest) { if (src != dest) { masm.moveFloat32(src, dest); } } #ifdef ENABLE_WASM_SIMD void BaseCompiler::moveV128(RegV128 src, RegV128 dest) { if (src != dest) { masm.moveSimd128(src, dest); } } #endif template <> inline void BaseCompiler::move(RegI32 src, RegI32 dest) { moveI32(src, dest); } template <> inline void BaseCompiler::move(RegI64 src, RegI64 dest) { moveI64(src, dest); } template <> inline void BaseCompiler::move(RegF32 src, RegF32 dest) { moveF32(src, dest); } template <> inline void BaseCompiler::move(RegF64 src, RegF64 dest) { moveF64(src, dest); } template <> inline void BaseCompiler::move(RegRef src, RegRef dest) { moveRef(src, dest); } template <> inline void BaseCompiler::move(RegPtr src, RegPtr dest) { movePtr(src, dest); } #ifdef ENABLE_WASM_SIMD template <> inline void BaseCompiler::move(RegV128 src, RegV128 dest) { moveV128(src, dest); } #endif ////////////////////////////////////////////////////////////////////////////// // // Constant loads. void BaseCompiler::moveImm32(int32_t v, RegI32 dest) { masm.move32(Imm32(v), dest); } void BaseCompiler::moveImm64(int64_t v, RegI64 dest) { masm.move64(Imm64(v), dest); } void BaseCompiler::moveImmRef(intptr_t v, RegRef dest) { masm.movePtr(ImmWord(v), dest); } ////////////////////////////////////////////////////////////////////////////// // // Calls. RegI32 BaseCompiler::captureReturnedI32() { RegI32 r = RegI32(ReturnReg); MOZ_ASSERT(isAvailableI32(r)); needI32(r); #if defined(JS_64BIT) masm.widenInt32(r); #endif return r; } RegI64 BaseCompiler::captureReturnedI64() { RegI64 r = RegI64(ReturnReg64); MOZ_ASSERT(isAvailableI64(r)); needI64(r); return r; } RegF32 BaseCompiler::captureReturnedF32(const FunctionCall& call) { RegF32 r = RegF32(ReturnFloat32Reg); MOZ_ASSERT(isAvailableF32(r)); needF32(r); #if defined(JS_CODEGEN_ARM) if (call.usesSystemAbi && !call.hardFP) { masm.ma_vxfer(ReturnReg, r); } #endif return r; } RegF64 BaseCompiler::captureReturnedF64(const FunctionCall& call) { RegF64 r = RegF64(ReturnDoubleReg); MOZ_ASSERT(isAvailableF64(r)); needF64(r); #if defined(JS_CODEGEN_ARM) if (call.usesSystemAbi && !call.hardFP) { masm.ma_vxfer(ReturnReg64.low, ReturnReg64.high, r); } #endif return r; } #ifdef ENABLE_WASM_SIMD RegV128 BaseCompiler::captureReturnedV128(const FunctionCall& call) { RegV128 r = RegV128(ReturnSimd128Reg); MOZ_ASSERT(isAvailableV128(r)); needV128(r); return r; } #endif RegRef BaseCompiler::captureReturnedRef() { RegRef r = RegRef(ReturnReg); MOZ_ASSERT(isAvailableRef(r)); needRef(r); return r; } ////////////////////////////////////////////////////////////////////////////// // // Miscellaneous. void BaseCompiler::trap(Trap t) const { masm.wasmTrap(t, bytecodeOffset()); } void BaseCompiler::cmp64Set(Assembler::Condition cond, RegI64 lhs, RegI64 rhs, RegI32 dest) { #if defined(JS_PUNBOX64) masm.cmpPtrSet(cond, lhs.reg, rhs.reg, dest); #else // TODO / OPTIMIZE (Bug 1316822): This is pretty branchy, we should be // able to do better. Label done, condTrue; masm.branch64(cond, lhs, rhs, &condTrue); moveImm32(0, dest); masm.jump(&done); masm.bind(&condTrue); moveImm32(1, dest); masm.bind(&done); #endif } [[nodiscard]] bool BaseCompiler::supportsRoundInstruction(RoundingMode mode) { return Assembler::HasRoundInstruction(mode); } void BaseCompiler::roundF32(RoundingMode roundingMode, RegF32 f0) { masm.nearbyIntFloat32(roundingMode, f0, f0); } void BaseCompiler::roundF64(RoundingMode roundingMode, RegF64 f0) { masm.nearbyIntDouble(roundingMode, f0, f0); } void BaseCompiler::branchTo(Assembler::DoubleCondition c, RegF64 lhs, RegF64 rhs, Label* l) { masm.branchDouble(c, lhs, rhs, l); } void BaseCompiler::branchTo(Assembler::DoubleCondition c, RegF32 lhs, RegF32 rhs, Label* l) { masm.branchFloat(c, lhs, rhs, l); } void BaseCompiler::branchTo(Assembler::Condition c, RegI32 lhs, RegI32 rhs, Label* l) { masm.branch32(c, lhs, rhs, l); } void BaseCompiler::branchTo(Assembler::Condition c, RegI32 lhs, Imm32 rhs, Label* l) { masm.branch32(c, lhs, rhs, l); } void BaseCompiler::branchTo(Assembler::Condition c, RegI64 lhs, RegI64 rhs, Label* l) { masm.branch64(c, lhs, rhs, l); } void BaseCompiler::branchTo(Assembler::Condition c, RegI64 lhs, Imm64 rhs, Label* l) { masm.branch64(c, lhs, rhs, l); } void BaseCompiler::branchTo(Assembler::Condition c, RegRef lhs, ImmWord rhs, Label* l) { masm.branchPtr(c, lhs, rhs, l); } ////////////////////////////////////////////////////////////////////////////// // // Templated emitters template <> inline BaseCompiler& BaseCompiler::selectCompiler() { return *this; } template <> inline MacroAssembler& BaseCompiler::selectCompiler() { return masm; } template void BaseCompiler::emitUnop(void (*op)(MacroAssembler& masm, SourceType rs, DestType rd)) { SourceType rs = pop(); DestType rd = need(); op(masm, rs, rd); free(rs); push(rd); } // Specialize narrowing reuse. Consumers may assume that rs.reg==rd on 64-bit // platforms, or rs.low==rd on 32-bit platforms. template <> inline void BaseCompiler::emitUnop(void (*op)(MacroAssembler& masm, RegI64 rs, RegI32 rd)) { RegI64 rs = pop(); RegI32 rd = fromI64(rs); op(masm, rs, rd); freeI64Except(rs, rd); push(rd); } template void BaseCompiler::emitUnop(void (*op)(CompilerType& compiler, RegType rsd)) { RegType rsd = pop(); op(selectCompiler(), rsd); push(rsd); } template void BaseCompiler::emitUnop(void (*op)(BaseCompiler& bc, RegType rsd, TempType rt), TempType (*getSpecializedTemp)(BaseCompiler& bc)) { RegType rsd = pop(); TempType temp = getSpecializedTemp(*this); op(*this, rsd, temp); maybeFree(temp); push(rsd); } template void BaseCompiler::emitUnop(void (*op)(MacroAssembler& masm, SourceType rs, DestType rd, TempType temp)) { SourceType rs = pop(); DestType rd = need(); TempType temp = need(); op(masm, rs, rd, temp); free(rs); free(temp); push(rd); } template void BaseCompiler::emitUnop(ImmType immediate, void (*op)(MacroAssembler&, ImmType, SourceType, DestType)) { SourceType rs = pop(); DestType rd = need(); op(masm, immediate, rs, rd); free(rs); push(rd); } template void BaseCompiler::emitBinop(void (*op)(CompilerType& masm, RhsType src, LhsDestType srcDest)) { RhsType rs = pop(); LhsDestType rsd = pop(); op(selectCompiler(), rs, rsd); free(rs); push(rsd); } template void BaseCompiler::emitTernary(void (*op)(CompilerType&, ValType src0, ValType src1, ValType srcDest)) { ValType src2 = pop(); ValType src1 = pop(); ValType srcDest = pop(); op(selectCompiler(), src1, src2, srcDest); free(src2); free(src1); push(srcDest); } template void BaseCompiler::emitTernary(void (*op)(CompilerType&, ValType src0, ValType src1, ValType srcDest, ValType temp)) { ValType src2 = pop(); ValType src1 = pop(); ValType srcDest = pop(); ValType temp = need(); op(selectCompiler(), src1, src2, srcDest, temp); free(temp); free(src2); free(src1); push(srcDest); } template void BaseCompiler::emitTernaryResultLast(void (*op)(CompilerType&, ValType src0, ValType src1, ValType srcDest)) { ValType srcDest = pop(); ValType src2 = pop(); ValType src1 = pop(); op(selectCompiler(), src1, src2, srcDest); free(src2); free(src1); push(srcDest); } template void BaseCompiler::emitBinop(void (*op)(MacroAssembler& masm, RhsDestType src, LhsType srcDest, RhsDestOp)) { RhsDestType rsd = pop(); LhsType rs = pop(); op(masm, rsd, rs, RhsDestOp::True); free(rs); push(rsd); } template void BaseCompiler::emitBinop(void (*op)(MacroAssembler& masm, RhsType rs, LhsDestType rsd, TempType temp)) { RhsType rs = pop(); LhsDestType rsd = pop(); TempType temp = need(); op(masm, rs, rsd, temp); free(rs); free(temp); push(rsd); } template void BaseCompiler::emitBinop(void (*op)(MacroAssembler& masm, RhsType rs, LhsDestType rsd, TempType1 temp1, TempType2 temp2)) { RhsType rs = pop(); LhsDestType rsd = pop(); TempType1 temp1 = need(); TempType2 temp2 = need(); op(masm, rs, rsd, temp1, temp2); free(rs); free(temp1); free(temp2); push(rsd); } template void BaseCompiler::emitBinop(ImmType immediate, void (*op)(MacroAssembler&, ImmType, RhsType, LhsDestType)) { RhsType rs = pop(); LhsDestType rsd = pop(); op(masm, immediate, rs, rsd); free(rs); push(rsd); } template void BaseCompiler::emitBinop(ImmType immediate, void (*op)(MacroAssembler&, ImmType, RhsType, LhsDestType, TempType1 temp1, TempType2 temp2)) { RhsType rs = pop(); LhsDestType rsd = pop(); TempType1 temp1 = need(); TempType2 temp2 = need(); op(masm, immediate, rs, rsd, temp1, temp2); free(rs); free(temp1); free(temp2); push(rsd); } template void BaseCompiler::emitBinop(void (*op)(CompilerType1& compiler, RegType rs, RegType rsd), void (*opConst)(CompilerType2& compiler, ImmType c, RegType rsd), RegType (BaseCompiler::*rhsPopper)()) { ImmType c; if (popConst(&c)) { RegType rsd = pop(); opConst(selectCompiler(), c, rsd); push(rsd); } else { RegType rs = rhsPopper ? (this->*rhsPopper)() : pop(); RegType rsd = pop(); op(selectCompiler(), rs, rsd); free(rs); push(rsd); } } template bool BaseCompiler::emitInstanceCallOp(const SymbolicAddressSignature& fn, R reader) { if (!reader()) { return false; } if (deadCode_) { return true; } return emitInstanceCall(fn); } template bool BaseCompiler::emitInstanceCallOp(const SymbolicAddressSignature& fn, R reader) { A1 arg = 0; if (!reader(&arg)) { return false; } if (deadCode_) { return true; } push(arg); return emitInstanceCall(fn); } template bool BaseCompiler::emitInstanceCallOp(const SymbolicAddressSignature& fn, R reader) { A1 arg1 = 0; A2 arg2 = 0; if (!reader(&arg1, &arg2)) { return false; } if (deadCode_) { return true; } // Note order of arguments must be the same as for the reader. push(arg1); push(arg2); return emitInstanceCall(fn); } } // namespace wasm } // namespace js #endif // wasm_wasm_baseline_codegen_inl_h