/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * 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. */ #include #include #include #include #include #include #include "bytecode_emitter.h" using namespace panda; using Opcode = BytecodeInstruction::Opcode; typedef std::tuple Tuple16; typedef std::tuple Tuple32; typedef std::tuple Tuple64; namespace globals { constexpr static const int64_t IMM_2 = 2; constexpr static const int64_t IMM_3 = 3; constexpr static const int64_t IMM_4 = 4; constexpr static const int64_t IMM_5 = 5; constexpr static const int64_t IMM_6 = 6; constexpr static const int64_t IMM_7 = 7; constexpr static const int64_t IMM_8 = 8; constexpr static const int64_t IMM_9 = 9; constexpr static const int64_t IMM_11 = 11; constexpr static const int64_t IMM_12 = 12; constexpr static const int64_t IMM_15 = 15; constexpr static const int64_t IMM_16 = 16; constexpr static const int64_t IMM_24 = 24; constexpr static const int64_t IMM_32 = 32; constexpr static const int64_t IMM_40 = 40; constexpr static const int64_t IMM_48 = 48; constexpr static const int64_t IMM_56 = 56; } // namespace globals static std::vector &operator<<(std::vector &out, uint8_t val) { out.push_back(val); return out; } static std::vector &operator<<(std::vector &out, Opcode op); static std::vector &operator<<(std::vector &out, Tuple16 val) { return out << std::get<0>(val) << std::get<1>(val); } static std::vector &operator<<(std::vector &out, Tuple32 val) { return out << std::get<0>(val) << std::get<1>(val) << std::get(val) << std::get(val); } static std::vector &operator<<(std::vector &out, Tuple64 val) { return out << std::get<0>(val) << std::get<1>(val) << std::get(val) << std::get(val) << std::get(val) << std::get(val) << std::get(val) << std::get(val); } static Tuple16 Split16(uint16_t val) { return Tuple16 {val & 0xFF, val >> 8}; } static Tuple32 Split32(uint32_t val) { return Tuple32 {val & 0xFF, (val >> globals::IMM_8) & 0xFF, (val >> globals::IMM_16) & 0xFF, (val >> globals::IMM_24) & 0xFF}; } static Tuple64 Split64(uint64_t val) { return Tuple64 {val & 0xFF, (val >> globals::IMM_8) & 0xFF, (val >> globals::IMM_16) & 0xFF, (val >> globals::IMM_24) & 0xFF, (val >> globals::IMM_32) & 0xFF, (val >> globals::IMM_40) & 0xFF, (val >> globals::IMM_48) & 0xFF, (val >> globals::IMM_56) & 0xFF}; } HWTEST(BytecodeEmitter, JmpBwd_IMM8, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Bind(label); int num_ret = -std::numeric_limits::min(); for (int i = 0; i < num_ret; ++i) { emitter.Return(); } emitter.Jmp(label); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; for (int i = 0; i < num_ret; ++i) { expected << Opcode::RETURN; } expected << Opcode::JMP_IMM8 << -num_ret; ASSERT_EQ(expected, out); } HWTEST(BytecodeEmitter, JmpFwd_IMM8, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Jmp(label); // -5 because 2 bytes takes jmp itself and // emitter estimate length of jmp by 3 greater the it is actually int num_ret = std::numeric_limits::max() - globals::IMM_5; for (int i = 0; i < num_ret; ++i) { emitter.Return(); } emitter.Bind(label); emitter.Return(); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << Opcode::JMP_IMM8 << num_ret + globals::IMM_2; for (int i = 0; i < num_ret + 1; ++i) { expected << Opcode::RETURN; } ASSERT_EQ(expected, out); } HWTEST(BytecodeEmitter, JmpBwd_IMM16, testing::ext::TestSize.Level0) { { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Bind(label); int num_ret = -std::numeric_limits::min() + 1; for (int i = 0; i < num_ret; ++i) { emitter.Return(); } emitter.Jmp(label); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; for (int i = 0; i < num_ret; ++i) { expected << Opcode::RETURN; } expected << Opcode::JMP_IMM16 << Split16(-num_ret); ASSERT_EQ(expected, out); } { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Bind(label); int num_ret = -std::numeric_limits::min(); for (int i = 0; i < num_ret; ++i) { emitter.Return(); } emitter.Jmp(label); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; for (int i = 0; i < num_ret; ++i) { expected << Opcode::RETURN; } expected << Opcode::JMP_IMM16 << Split16(-num_ret); ASSERT_EQ(expected, out); } } HWTEST(BytecodeEmitter, JmpFwd_IMM16, testing::ext::TestSize.Level0) { { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Jmp(label); // -4 because 2 bytes takes jmp itself and // emitter estimate length of jmp by 3 greater the it is actually // and plus one byte to make 8bit overflow int num_ret = std::numeric_limits::max() - globals::IMM_4; for (int i = 0; i < num_ret; ++i) { emitter.Return(); } emitter.Bind(label); emitter.Return(); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << Opcode::JMP_IMM16 << Split16(num_ret + globals::IMM_3); for (int i = 0; i < num_ret + 1; ++i) { expected << Opcode::RETURN; } ASSERT_EQ(expected, out); } { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Jmp(label); // -5 because 2 bytes takes jmp itself and // emitter estimate length of jmp by 3 greater the it is actually int num_ret = std::numeric_limits::max() - globals::IMM_5; for (int i = 0; i < num_ret; ++i) { emitter.Return(); } emitter.Bind(label); emitter.Return(); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << Opcode::JMP_IMM16 << Split16(num_ret + globals::IMM_3); for (int i = 0; i < num_ret + 1; ++i) { expected << Opcode::RETURN; } ASSERT_EQ(expected, out); } } static void EmitJmp(Opcode op, int32_t imm, std::vector *out) { *out << op; switch (op) { case Opcode::JMP_IMM8: *out << static_cast(imm); break; case Opcode::JMP_IMM16: *out << Split16(imm); break; default: *out << Split32(imm); break; } } static Opcode GetOpcode(size_t inst_size) { switch (inst_size) { case globals::IMM_2: return Opcode::JMP_IMM8; case globals::IMM_3: return Opcode::JMP_IMM16; default: return Opcode::JMP_IMM32; } } /* * Emit bytecode for the following program: * * label1: * jmp label2 * ... <- n1 return.void instructions * jmp label1 * ... <- n2 return.void instructions * label2: * return.void */ static std::vector EmitJmpFwdBwd(size_t n1, size_t n2) { std::array, 3> jmps { std::tuple {globals::IMM_2, std::numeric_limits::min(), std::numeric_limits::max()}, std::tuple {globals::IMM_3, std::numeric_limits::min(), std::numeric_limits::max()}, std::tuple {globals::IMM_5, std::numeric_limits::min(), std::numeric_limits::max()}}; std::vector out; size_t jmp_size1; size_t jmp_size2; int32_t imm_max1; int32_t imm_min2; for (const auto &t1 : jmps) { std::tie(jmp_size1, std::ignore, imm_max1) = t1; for (const auto &t2 : jmps) { std::tie(jmp_size2, imm_min2, std::ignore) = t2; int32_t imm1 = jmp_size1 + n1 + jmp_size2 + n2; int32_t imm2 = jmp_size1 + n1; if (imm1 <= imm_max1 && -imm2 >= imm_min2) { EmitJmp(GetOpcode(jmp_size1), imm1, &out); for (size_t i = 0; i < n1; i++) { out << Opcode::RETURN; } EmitJmp(GetOpcode(jmp_size2), -imm2, &out); for (size_t i = 0; i < n2; i++) { out << Opcode::RETURN; } out << Opcode::RETURN; return out; } } } return out; } /* * Test following control flow: * * label1: * jmp label2 * ... <- n1 return.void instructions * jmp label1 * ... <- n2 return.void instructions * label2: * return.void */ void TestJmpFwdBwd(size_t n1, size_t n2) { BytecodeEmitter emitter; Label label1 = emitter.CreateLabel(); Label label2 = emitter.CreateLabel(); emitter.Bind(label1); emitter.Jmp(label2); for (size_t i = 0; i < n1; ++i) { emitter.Return(); } emitter.Jmp(label1); for (size_t i = 0; i < n2; ++i) { emitter.Return(); } emitter.Bind(label2); emitter.Return(); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)) << "n1 = " << n1 << " n2 = " << n2; ASSERT_EQ(EmitJmpFwdBwd(n1, n2), out) << "n1 = " << n1 << " n2 = " << n2; } HWTEST(BytecodeEmitter, JmpFwdBwd, testing::ext::TestSize.Level0) { // fwd jmp imm16 // bwd jmp imm8 TestJmpFwdBwd(0, std::numeric_limits::max()); // fwd jmp imm16 // bwd jmp imm16 TestJmpFwdBwd(std::numeric_limits::max(), 0); // fwd jmp imm32 // bwd jmp imm8 TestJmpFwdBwd(0, std::numeric_limits::max()); // fwd jmp imm32 // bwd jmp imm16 TestJmpFwdBwd(std::numeric_limits::max(), std::numeric_limits::max()); // fwd jmp imm32 // bwd jmp imm32 TestJmpFwdBwd(std::numeric_limits::max(), 0); } HWTEST(BytecodeEmitter, JmpBwd_IMM32, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Bind(label); int num_ret = -std::numeric_limits::min() + 1; for (int i = 0; i < num_ret; ++i) { emitter.Return(); } emitter.Jmp(label); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; for (int i = 0; i < num_ret; ++i) { expected << Opcode::RETURN; } expected << Opcode::JMP_IMM32 << Split32(-num_ret); ASSERT_EQ(expected, out); } HWTEST(BytecodeEmitter, JmpFwd_IMM32, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Jmp(label); // -4 because 2 bytes takes jmp itself and // emitter estimate length of jmp by 3 greater the it is actually // and plus one byte to make 16bit overflow int num_ret = std::numeric_limits::max() - globals::IMM_4; for (int i = 0; i < num_ret; ++i) { emitter.Return(); } emitter.Bind(label); emitter.Return(); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << Opcode::JMP_IMM32 << Split32(num_ret + globals::IMM_5); for (int i = 0; i < num_ret + 1; ++i) { expected << Opcode::RETURN; } ASSERT_EQ(expected, out); } HWTEST(BytecodeEmitter, Jne_V8_IMM8, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Jne(0, label); emitter.Bind(label); emitter.Return(); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << Opcode::JNE_V8_IMM8 << 0 << globals::IMM_3 << Opcode::RETURN; ASSERT_EQ(expected, out); } HWTEST(BytecodeEmitter, Jne_V8_IMM16, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); static constexpr uint8_t JNE_REG = 16; static constexpr size_t N_NOPS = 256; static constexpr size_t INSN_SZ = 4; emitter.Jne(JNE_REG, label); for (size_t i = 0; i < N_NOPS; ++i) { emitter.Nop(); } emitter.Bind(label); emitter.Return(); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << Opcode::JNE_V8_IMM16 << JNE_REG << Split16(INSN_SZ + N_NOPS); for (size_t i = 0; i < N_NOPS; ++i) { expected << Opcode::NOP; } expected << Opcode::RETURN; ASSERT_EQ(expected, out); } HWTEST(BytecodeEmitter, JmpFwdCrossRef, testing::ext::TestSize.Level0) { // Situation: // +---------+ // +----|----+ | // | | | | // | | v v // ---*----*----*----*---- // lbl1 lbl2 BytecodeEmitter emitter; Label lbl1 = emitter.CreateLabel(); Label lbl2 = emitter.CreateLabel(); emitter.Jeq(0, lbl1); emitter.Jeq(0, lbl2); emitter.Return(); emitter.Bind(lbl1); emitter.Return(); for (int i = 0; i < globals::IMM_6; ++i) { emitter.Return(); } emitter.Bind(lbl2); emitter.Return(); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << Opcode::JEQ_V8_IMM8 << 0u << globals::IMM_7 << Opcode::JEQ_V8_IMM8 << 0u << globals::IMM_11; for (int i = 0; i < globals::IMM_9; ++i) { expected << Opcode::RETURN; } ASSERT_EQ(expected, out); } HWTEST(BytecodeEmitter, JmpBwdCrossRef, testing::ext::TestSize.Level0) { // Situation: // +---------+ // | | // +---------+ | // | | | | // v | | v // ---*----*----*----*---- // lbl1 lbl2 BytecodeEmitter emitter; Label lbl1 = emitter.CreateLabel(); Label lbl2 = emitter.CreateLabel(); emitter.Bind(lbl1); emitter.Return(); emitter.Jeq(0, lbl2); for (int i = 0; i < globals::IMM_5; ++i) { emitter.Return(); } emitter.Jeq(0, lbl1); emitter.Bind(lbl2); emitter.Return(); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << Opcode::RETURN << Opcode::JEQ_V8_IMM8 << 0u << globals::IMM_11; for (int i = 0; i < globals::IMM_5; ++i) { expected << Opcode::RETURN; } expected << Opcode::JEQ_V8_IMM8 << 0u << static_cast(-globals::IMM_9) << Opcode::RETURN; ASSERT_EQ(expected, out); } HWTEST(BytecodeEmitter, Jmp3FwdCrossRefs, testing::ext::TestSize.Level0) { // Situation: // +--------+ // +|--------+ // ||+-------+---+ // ||| v v // ---***-------*---* // lbl1 lbl2 BytecodeEmitter emitter; Label lbl1 = emitter.CreateLabel(); Label lbl2 = emitter.CreateLabel(); emitter.Jmp(lbl1); emitter.Jmp(lbl1); emitter.Jmp(lbl2); constexpr int32_t INT8T_MAX = std::numeric_limits::max(); size_t n = INT8T_MAX - globals::IMM_4; for (size_t i = 0; i < n; i++) { emitter.Return(); } emitter.Bind(lbl1); emitter.Return(); emitter.Return(); emitter.Bind(lbl2); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << Opcode::JMP_IMM16 << Split16(INT8T_MAX + globals::IMM_5); expected << Opcode::JMP_IMM16 << Split16(INT8T_MAX + globals::IMM_2); expected << Opcode::JMP_IMM16 << Split16(INT8T_MAX + 1); for (size_t i = 0; i < n + globals::IMM_2; i++) { expected << Opcode::RETURN; } ASSERT_EQ(expected, out); } HWTEST(BytecodeEmitter, UnboundLabel, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Bind(label); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); } HWTEST(BytecodeEmitter, JumpToUnboundLabel, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Jmp(label); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::UNBOUND_LABELS, emitter.Build(&out)); } HWTEST(BytecodeEmitter, JumpToUnboundLabel2, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label1 = emitter.CreateLabel(); Label label2 = emitter.CreateLabel(); emitter.Jmp(label1); emitter.Bind(label2); emitter.Mov(0, 1); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::UNBOUND_LABELS, emitter.Build(&out)); } HWTEST(BytecodeEmitter, TwoJumpsToOneLabel, testing::ext::TestSize.Level0) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emitter.Bind(label); emitter.Mov(0, 1); emitter.Jmp(label); emitter.Jmp(label); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); } static void Jmpz_IMM8(Opcode opcode, std::function emit_jcmp) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emit_jcmp(&emitter, label); emitter.Bind(label); emitter.Return(); } static void Jmpz_IMM16(Opcode opcode, std::function emit_jcmp) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emit_jcmp(&emitter, label); for (int i = 0; i < std::numeric_limits::max() - globals::IMM_2; ++i) { emitter.Return(); } emitter.Bind(label); emitter.Return(); } static void Jmp_V8_IMM8(Opcode opcode, std::function emit_jcmp) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emit_jcmp(&emitter, globals::IMM_12, label); emitter.Bind(label); emitter.Return(); } static void Jmp_V8_IMM16(Opcode opcode, std::function emit_jcmp) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emit_jcmp(&emitter, globals::IMM_15, label); for (int i = 0; i < std::numeric_limits::max() - globals::IMM_2; ++i) { emitter.Return(); } emitter.Bind(label); emitter.Return(); } static void Jmpz_IMM32(Opcode opcode, std::function emit_jcmp) { BytecodeEmitter emitter; Label label = emitter.CreateLabel(); emit_jcmp(&emitter, label); for (int i = 0; i < std::numeric_limits::max() - globals::IMM_2; ++i) { emitter.Return(); } emitter.Bind(label); emitter.Return(); } static void TestNoneFormat(Opcode opcode, std::function emit) { BytecodeEmitter emitter; emit(&emitter); std::vector out; ASSERT_EQ(BytecodeEmitter::ErrorCode::SUCCESS, emitter.Build(&out)); std::vector expected; expected << opcode; ASSERT_EQ(expected, out); } #include